NIFI-655:

- Creating an endpoint for returning the identity of the current user.
- Updating the LoginAuthenticationFilter.
This commit is contained in:
Matt Gilman 2015-10-30 10:11:03 -04:00
parent a40e5a07ba
commit e7a5e18221
22 changed files with 373 additions and 192 deletions

View File

@ -0,0 +1,60 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.api.dto;
import com.wordnik.swagger.annotations.ApiModelProperty;
import javax.xml.bind.annotation.XmlType;
/**
* Details for the login configuration.
*/
@XmlType(name = "loginConfig")
public class LoginConfigurationDTO {
private Boolean supportsLogin;
private Boolean supportsRegistration;
/**
* @return Indicates whether or not this NiFi supports user login.
*/
@ApiModelProperty(
value = "Indicates whether or not this NiFi supports user login.",
readOnly = true
)
public Boolean getSupportsLogin() {
return supportsLogin;
}
public void setSupportsLogin(Boolean supportsLogin) {
this.supportsLogin = supportsLogin;
}
/**
* @return If this NiFi supports login, indicates whether or not registration is supported.
*/
@ApiModelProperty(
value = "If this NiFi supports login, indicates whether or not registration is supported.",
readOnly = true
)
public Boolean getSupportsRegistration() {
return supportsRegistration;
}
public void setSupportsRegistration(Boolean supportsRegistration) {
this.supportsRegistration = supportsRegistration;
}
}

View File

@ -14,24 +14,30 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.nifi.web.security.token; package org.apache.nifi.web.api.entity;
import java.util.List; import javax.xml.bind.annotation.XmlRootElement;
import org.apache.nifi.authentication.LoginCredentials; import org.apache.nifi.web.api.dto.LoginConfigurationDTO;
/** /**
* This is an Authentication Token for requesting an ID token for accessing the NIFI REST API. * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a LoginConfigurationDTO.
*/ */
public class LoginAuthenticationRequestToken extends NiFiAuthenticationRequestToken { @XmlRootElement(name = "loginConfigurationEntity")
public class LoginConfigurationEntity extends Entity {
final LoginCredentials credentials; private LoginConfigurationDTO config;
public LoginAuthenticationRequestToken(final List<String> proxyChain, final LoginCredentials credentials) { /**
super(proxyChain); * The LoginConfigurationDTO that is being serialized.
this.credentials = credentials; *
* @return The LoginConfigurationDTO object
*/
public LoginConfigurationDTO getConfig() {
return config;
} }
public LoginCredentials getLoginCredentials() { public void setConfig(LoginConfigurationDTO config) {
return credentials; this.config = config;
} }
} }

View File

@ -43,6 +43,7 @@ import org.apache.nifi.web.api.dto.ProcessGroupDTO;
import org.apache.nifi.web.api.dto.ProcessorDTO; import org.apache.nifi.web.api.dto.ProcessorDTO;
import org.apache.nifi.web.api.dto.ComponentHistoryDTO; import org.apache.nifi.web.api.dto.ComponentHistoryDTO;
import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO; import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
import org.apache.nifi.web.api.dto.LoginConfigurationDTO;
import org.apache.nifi.web.api.dto.PropertyDescriptorDTO; import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO; import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
@ -177,6 +178,13 @@ public interface NiFiServiceFacade {
*/ */
ControllerConfigurationDTO getControllerConfiguration(); ControllerConfigurationDTO getControllerConfiguration();
/**
* Gets the login configuration for this controller.
*
* @return The login configuration
*/
LoginConfigurationDTO getLoginConfiguration();
/** /**
* Updates the configuration for this controller. * Updates the configuration for this controller.
* *

View File

@ -24,7 +24,6 @@ import org.apache.nifi.web.security.NiFiAuthenticationProvider;
import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter; import org.apache.nifi.web.security.anonymous.NiFiAnonymousUserFilter;
import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint; import org.apache.nifi.web.security.NiFiAuthenticationEntryPoint;
import org.apache.nifi.web.security.form.LoginAuthenticationFilter; import org.apache.nifi.web.security.form.LoginAuthenticationFilter;
import org.apache.nifi.web.security.form.LoginAuthenticationProvider;
import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter; import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter;
import org.apache.nifi.web.security.jwt.JwtAuthenticationProvider; import org.apache.nifi.web.security.jwt.JwtAuthenticationProvider;
import org.apache.nifi.web.security.jwt.JwtService; import org.apache.nifi.web.security.jwt.JwtService;
@ -37,6 +36,7 @@ import org.apache.nifi.web.security.x509.ocsp.OcspCertificateValidator;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@ -75,6 +75,7 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
.authenticationEntryPoint(new NiFiAuthenticationEntryPoint()) .authenticationEntryPoint(new NiFiAuthenticationEntryPoint())
.and() .and()
.authorizeRequests() .authorizeRequests()
.antMatchers(HttpMethod.GET, "/controller/login/config").permitAll()
.anyRequest().fullyAuthenticated() .anyRequest().fullyAuthenticated()
.and() .and()
.sessionManagement() .sessionManagement()
@ -113,15 +114,10 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
@Override @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { protected void configure(AuthenticationManagerBuilder auth) throws Exception {
final LoginAuthenticationProvider baseLoginAuthenticationProvider = new LoginAuthenticationProvider();
baseLoginAuthenticationProvider.setLoginIdentityProvider(loginIdentityProvider);
final AuthenticationProvider loginAuthenticationProvider = new NiFiAuthenticationProvider(baseLoginAuthenticationProvider, userDetailsService);
final AuthenticationProvider x509AuthenticationProvider = new NiFiAuthenticationProvider(new X509AuthenticationProvider(), userDetailsService); final AuthenticationProvider x509AuthenticationProvider = new NiFiAuthenticationProvider(new X509AuthenticationProvider(), userDetailsService);
final AuthenticationProvider jwtAuthenticationProvider = new NiFiAuthenticationProvider(new JwtAuthenticationProvider(), userDetailsService); final AuthenticationProvider jwtAuthenticationProvider = new NiFiAuthenticationProvider(new JwtAuthenticationProvider(), userDetailsService);
auth auth
.authenticationProvider(loginAuthenticationProvider)
.authenticationProvider(x509AuthenticationProvider) .authenticationProvider(x509AuthenticationProvider)
.authenticationProvider(jwtAuthenticationProvider); .authenticationProvider(jwtAuthenticationProvider);
} }
@ -129,7 +125,7 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte
private LoginAuthenticationFilter buildLoginFilter(final String url) { private LoginAuthenticationFilter buildLoginFilter(final String url) {
final LoginAuthenticationFilter loginFilter = new LoginAuthenticationFilter(url); final LoginAuthenticationFilter loginFilter = new LoginAuthenticationFilter(url);
loginFilter.setJwtService(jwtService); loginFilter.setJwtService(jwtService);
loginFilter.setLoginIdentityProvider(loginIdentityProvider); loginFilter.setUserDetailsService(userDetailsService);
loginFilter.setPrincipalExtractor(new SubjectDnX509PrincipalExtractor()); loginFilter.setPrincipalExtractor(new SubjectDnX509PrincipalExtractor());
loginFilter.setCertificateExtractor(new X509CertificateExtractor()); loginFilter.setCertificateExtractor(new X509CertificateExtractor());
return loginFilter; return loginFilter;

View File

@ -153,6 +153,7 @@ import org.apache.nifi.web.util.SnippetUtils;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authentication.LoginIdentityProvider;
import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.Validator; import org.apache.nifi.components.Validator;
import org.apache.nifi.controller.ReportingTaskNode; import org.apache.nifi.controller.ReportingTaskNode;
@ -163,6 +164,7 @@ import org.apache.nifi.controller.service.ControllerServiceState;
import org.apache.nifi.reporting.ComponentType; import org.apache.nifi.reporting.ComponentType;
import org.apache.nifi.web.api.dto.ControllerServiceDTO; import org.apache.nifi.web.api.dto.ControllerServiceDTO;
import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO; import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
import org.apache.nifi.web.api.dto.LoginConfigurationDTO;
import org.apache.nifi.web.api.dto.PropertyDescriptorDTO; import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
import org.apache.nifi.web.api.dto.ReportingTaskDTO; import org.apache.nifi.web.api.dto.ReportingTaskDTO;
import org.apache.nifi.web.api.dto.status.ClusterProcessGroupStatusDTO; import org.apache.nifi.web.api.dto.status.ClusterProcessGroupStatusDTO;
@ -205,6 +207,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
// administrative services // administrative services
private AuditService auditService; private AuditService auditService;
private UserService userService; private UserService userService;
private LoginIdentityProvider loginIdentityProvider;
// cluster manager // cluster manager
private WebClusterManager clusterManager; private WebClusterManager clusterManager;
@ -2347,6 +2350,22 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
return controllerFacade.getInstanceId(); return controllerFacade.getInstanceId();
} }
@Override
public LoginConfigurationDTO getLoginConfiguration() {
final LoginConfigurationDTO loginConfiguration = new LoginConfigurationDTO();
// specify whether login/registration should be supported
if (loginIdentityProvider == null) {
loginConfiguration.setSupportsLogin(false);
loginConfiguration.setSupportsRegistration(false);
} else {
loginConfiguration.setSupportsLogin(true);
loginConfiguration.setSupportsRegistration(loginIdentityProvider.supportsRegistration());
}
return loginConfiguration;
}
@Override @Override
public ControllerConfigurationDTO getControllerConfiguration() { public ControllerConfigurationDTO getControllerConfiguration() {
ControllerConfigurationDTO controllerConfig = new ControllerConfigurationDTO(); ControllerConfigurationDTO controllerConfig = new ControllerConfigurationDTO();
@ -3407,6 +3426,10 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
this.snippetUtils = snippetUtils; this.snippetUtils = snippetUtils;
} }
public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
this.loginIdentityProvider = loginIdentityProvider;
}
private boolean isPrimaryNode(String nodeId) { private boolean isPrimaryNode(String nodeId) {
final Node primaryNode = clusterManager.getPrimaryNode(); final Node primaryNode = clusterManager.getPrimaryNode();
return (primaryNode != null && primaryNode.getNodeId().getId().equals(nodeId)); return (primaryNode != null && primaryNode.getNodeId().getId().equals(nodeId));

View File

@ -78,8 +78,10 @@ import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.nifi.web.api.request.IntegerParameter; import org.apache.nifi.web.api.request.IntegerParameter;
import org.apache.nifi.web.api.request.LongParameter; import org.apache.nifi.web.api.request.LongParameter;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.web.api.dto.LoginConfigurationDTO;
import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity; import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity;
import org.apache.nifi.web.api.entity.IdentityEntity; import org.apache.nifi.web.api.entity.IdentityEntity;
import org.apache.nifi.web.api.entity.LoginConfigurationEntity;
import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity; import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@ -654,6 +656,53 @@ public class ControllerResource extends ApplicationResource {
return clusterContext(generateOkResponse(entity)).build(); return clusterContext(generateOkResponse(entity)).build();
} }
/**
* Retrieves the login configuration for this NiFi.
*
* @param httpServletRequest the servlet request
* @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
* @return A loginConfigurationEntity
*/
@GET
@Consumes(MediaType.WILDCARD)
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Path("/login/config")
@ApiOperation(
value = "Retrieves the login configuration for this NiFi",
response = LoginConfigurationEntity.class
)
public Response getLoginConfig(
@Context HttpServletRequest httpServletRequest,
@ApiParam(
value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
required = false
)
@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
// replicate if cluster manager
if (properties.isClusterManager()) {
return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
}
final LoginConfigurationDTO loginConfig = serviceFacade.getLoginConfiguration();
// only support login/registration when running securely
loginConfig.setSupportsLogin(loginConfig.getSupportsLogin() && httpServletRequest.isSecure());
loginConfig.setSupportsRegistration(loginConfig.getSupportsRegistration() && httpServletRequest.isSecure());
// create the revision
final RevisionDTO revision = new RevisionDTO();
revision.setClientId(clientId.getClientId());
// create the response entity
final LoginConfigurationEntity entity = new LoginConfigurationEntity();
entity.setRevision(revision);
entity.setConfig(loginConfig);
// generate the response
return clusterContext(generateOkResponse(entity)).build();
}
/** /**
* Retrieves the configuration for this NiFi. * Retrieves the configuration for this NiFi.
* *

View File

@ -122,6 +122,7 @@
<property name="optimisticLockingManager" ref="webOptimisticLockingManager"/> <property name="optimisticLockingManager" ref="webOptimisticLockingManager"/>
<property name="dtoFactory" ref="dtoFactory"/> <property name="dtoFactory" ref="dtoFactory"/>
<property name="clusterManager" ref="clusterManager"/> <property name="clusterManager" ref="clusterManager"/>
<property name="loginIdentityProvider" ref="loginIdentityProvider"/>
</bean> </bean>
<!-- depecrated --> <!-- depecrated -->

View File

@ -26,16 +26,21 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.nifi.authentication.LoginCredentials; import org.apache.nifi.authentication.LoginCredentials;
import org.apache.nifi.authentication.LoginIdentityProvider; import org.apache.nifi.authentication.LoginIdentityProvider;
import org.apache.nifi.util.StringUtils;
import org.apache.nifi.web.security.ProxiedEntitiesUtils; import org.apache.nifi.web.security.ProxiedEntitiesUtils;
import org.apache.nifi.web.security.jwt.JwtService; import org.apache.nifi.web.security.jwt.JwtService;
import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken; import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken;
import org.apache.nifi.web.security.token.LoginAuthenticationRequestToken;
import org.apache.nifi.web.security.x509.X509CertificateExtractor; import org.apache.nifi.web.security.x509.X509CertificateExtractor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
/** /**
@ -45,11 +50,13 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF
private static final Logger logger = LoggerFactory.getLogger(LoginAuthenticationFilter.class); private static final Logger logger = LoggerFactory.getLogger(LoginAuthenticationFilter.class);
private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService;
private X509CertificateExtractor certificateExtractor; private X509CertificateExtractor certificateExtractor;
private X509PrincipalExtractor principalExtractor; private X509PrincipalExtractor principalExtractor;
private JwtService jwtService;
private LoginIdentityProvider loginIdentityProvider; private LoginIdentityProvider loginIdentityProvider;
private JwtService jwtService;
public LoginAuthenticationFilter(final String defaultFilterProcessesUrl) { public LoginAuthenticationFilter(final String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl); super(defaultFilterProcessesUrl);
@ -65,33 +72,69 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF
return null; return null;
} }
// look for a certificate // look for the credentials in the request
final X509Certificate certificate = certificateExtractor.extractClientCertificate(request); final LoginCredentials credentials = getLoginCredentials(request);
// ensure the cert was found // if the credentials were not part of the request, attempt to log in with the certificate in the request
if (certificate != null) { if (credentials == null) {
// extract the principal // look for a certificate
Object certificatePrincipal = principalExtractor.extractPrincipal(certificate); final X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
final String principal = ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
final List<String> proxyChain = ProxiedEntitiesUtils.buildProxyChain(request, principal); if (certificate == null) {
return new NiFiAuthenticationRequestToken(proxyChain); throw new PreAuthenticatedCredentialsNotFoundException("Unable to extract client certificate after processing request with no login credentials specified.");
} else {
// if there were no certificate or no principal, defer to the login provider
final LoginCredentials credentials = getLoginCredentials(request);
// if unable to authenticate return null
if (credentials == null) {
return null;
} }
final List<String> proxyChain = ProxiedEntitiesUtils.buildProxyChain(request, credentials.getUsername()); // authorize the proxy if necessary
return new LoginAuthenticationRequestToken(proxyChain, credentials); final String principal = extractPrincipal(certificate);
authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(request, principal));
final LoginCredentials preAuthenticatedCredentials = new LoginCredentials(principal, null);
return new LoginAuthenticationToken(preAuthenticatedCredentials);
} else {
// look for a certificate
final X509Certificate certificate = certificateExtractor.extractClientCertificate(request);
// if there was a certificate with this request see if it was proxying an end user request
if (certificate != null) {
// authorize the proxy if necessary
final String principal = extractPrincipal(certificate);
authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(request, principal));
}
if (loginIdentityProvider.authenticate(credentials)) {
return new LoginAuthenticationToken(credentials);
} else {
throw new BadCredentialsException("User could not be authenticated with the configured identity provider.");
}
} }
} }
private void authorizeProxyIfNecessary(final List<String> proxyChain) throws AuthenticationException {
if (proxyChain.size() > 1) {
try {
userDetailsService.loadUserDetails(new NiFiAuthenticationRequestToken(proxyChain));
} catch (final UsernameNotFoundException unfe) {
// if a username not found exception was thrown, the proxies were authorized and now
// we can issue a new ID token to the end user
}
}
}
private String extractPrincipal(final X509Certificate certificate) {
// extract the principal
final Object certificatePrincipal = principalExtractor.extractPrincipal(certificate);
return ProxiedEntitiesUtils.formatProxyDn(certificatePrincipal.toString());
}
private LoginCredentials getLoginCredentials(HttpServletRequest request) { private LoginCredentials getLoginCredentials(HttpServletRequest request) {
return new LoginCredentials(request.getParameter("username"), request.getParameter("password")); final String username = request.getParameter("username");
final String password = request.getParameter("password");
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
return null;
} else {
return new LoginCredentials(username, password);
}
} }
@Override @Override
@ -112,6 +155,34 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF
out.println("Invalid username/password"); out.println("Invalid username/password");
} }
/**
* This is an Authentication Token for logging in. Once a user is authenticated, they can be issues an ID token.
*/
public static class LoginAuthenticationToken extends AbstractAuthenticationToken {
final LoginCredentials credentials;
public LoginAuthenticationToken(final LoginCredentials credentials) {
super(null);
setAuthenticated(true);
this.credentials = credentials;
}
public LoginCredentials getLoginCredentials() {
return credentials;
}
@Override
public Object getCredentials() {
return credentials.getPassword();
}
@Override
public Object getPrincipal() {
return credentials.getUsername();
}
}
public void setJwtService(JwtService jwtService) { public void setJwtService(JwtService jwtService) {
this.jwtService = jwtService; this.jwtService = jwtService;
} }
@ -128,4 +199,8 @@ public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingF
this.principalExtractor = principalExtractor; this.principalExtractor = principalExtractor;
} }
public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) {
this.userDetailsService = userDetailsService;
}
} }

View File

@ -1,53 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.security.form;
import org.apache.nifi.authentication.LoginIdentityProvider;
import org.apache.nifi.web.security.token.LoginAuthenticationRequestToken;
import org.apache.nifi.web.security.token.LoginAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
/**
*
*/
public class LoginAuthenticationProvider implements AuthenticationProvider {
private LoginIdentityProvider loginIdentityProvider;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final LoginAuthenticationRequestToken loginAuthenticationToken = (LoginAuthenticationRequestToken) authentication;
if (loginIdentityProvider.authenticate(loginAuthenticationToken.getLoginCredentials())) {
return new LoginAuthenticationToken(loginAuthenticationToken.getLoginCredentials());
} else {
return null;
}
}
@Override
public boolean supports(Class<?> authentication) {
return LoginAuthenticationRequestToken.class.isAssignableFrom(authentication);
}
public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) {
this.loginIdentityProvider = loginIdentityProvider;
}
}

View File

@ -1,48 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.security.token;
import org.apache.nifi.authentication.LoginCredentials;
import org.springframework.security.authentication.AbstractAuthenticationToken;
/**
* This is an Authentication Token for logging in. Once a user is authenticated, they can be issues an ID token.
*/
public class LoginAuthenticationToken extends AbstractAuthenticationToken {
final LoginCredentials credentials;
public LoginAuthenticationToken(final LoginCredentials credentials) {
super(null);
setAuthenticated(true);
this.credentials = credentials;
}
public LoginCredentials getLoginCredentials() {
return credentials;
}
@Override
public Object getCredentials() {
return credentials.getPassword();
}
@Override
public Object getPrincipal() {
return credentials.getUsername();
}
}

View File

@ -16,16 +16,12 @@
*/ */
package org.apache.nifi.web; package org.apache.nifi.web;
import org.apache.nifi.web.security.form.LoginAuthenticationFilter;
import org.apache.nifi.web.security.jwt.JwtService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/** /**
* NiFi Web Ui Security Config * NiFi Web Ui Security Config
@ -38,32 +34,11 @@ public class NiFiWebUiSecurityConfiguration extends WebSecurityConfigurerAdapter
super(true); // disable defaults super(true); // disable defaults
} }
private JwtService jwtService;
@Override @Override
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
http
.addFilterBefore(buildFormLoginFilter(), UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
private LoginAuthenticationFilter buildFormLoginFilter() throws Exception {
final LoginAuthenticationFilter loginFilter = new LoginAuthenticationFilter("/token");
loginFilter.setJwtService(jwtService);
return loginFilter;
} }
@Autowired @Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception { public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("gilman").password("password").roles("USER");
} }
@Autowired
public void setJwtService(JwtService jwtService) {
this.jwtService = jwtService;
}
} }

View File

@ -34,16 +34,7 @@
${nf.login.script.tags} ${nf.login.script.tags}
</head> </head>
<body> <body>
<form name="loginForm" action="token" method="post"> <jsp:include page="/WEB-INF/partials/login/login-form.jsp"/>
<legend>Please Login</legend> <jsp:include page="/WEB-INF/partials/login/registration-form.jsp"/>
<label for="username">Username</label>
<input type="text" id="username" name="username" value="${username}"/>
<br>
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
<div class="form-actions">
<button type="submit" class="btn">Log in</button>
</div>
</form>
</body> </body>
</html> </html>

View File

@ -50,7 +50,7 @@
<li> <li>
<span id="about-link" class="link">about</span> <span id="about-link" class="link">about</span>
</li> </li>
<li> <li id="login-link-container">
<span id="login-link" class="link">login</span> <span id="login-link" class="link">login</span>
</li> </li>
</ul> </ul>

View File

@ -0,0 +1,28 @@
<%--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="login-form">
<legend>Please Login</legend>
<label for="username">Username</label>
<input type="text" id="username" name="username" value="${username}"/>
<br>
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
<div class="form-actions">
<button id="login-button" type="submit" class="btn">Log in</button>
</div>
</div>

View File

@ -0,0 +1,19 @@
<%--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="registration-form">
</div>

View File

@ -42,4 +42,5 @@
@import url(settings.css); @import url(settings.css);
@import url(about.css); @import url(about.css);
@import url(message-pane.css); @import url(message-pane.css);
@import url(login.css);
@import url(status-history.css); @import url(status-history.css);

View File

@ -32,7 +32,7 @@ nf.CanvasHeader = (function () {
/** /**
* Initialize the canvas header. * Initialize the canvas header.
*/ */
init: function () { init: function (supportsLogin) {
// mouse over for the reporting link // mouse over for the reporting link
nf.Common.addHoverEffect('#reporting-link', 'reporting-link', 'reporting-link-hover').click(function () { nf.Common.addHoverEffect('#reporting-link', 'reporting-link', 'reporting-link-hover').click(function () {
nf.Shell.showPage('summary'); nf.Shell.showPage('summary');
@ -139,10 +139,14 @@ nf.CanvasHeader = (function () {
nf.Shell.showPage(config.urls.helpDocument); nf.Shell.showPage(config.urls.helpDocument);
}); });
// login link if (supportsLogin === true) {
$('#login-link').click(function () { // login link
nf.Shell.showPage('login', false); $('#login-link').click(function () {
}); nf.Shell.showPage('login', false);
});
} else {
$('#login-link-container').css('display', 'none');
}
// initialize the new template dialog // initialize the new template dialog
$('#new-template-dialog').modal({ $('#new-template-dialog').modal({

View File

@ -64,6 +64,7 @@ nf.Canvas = (function () {
banners: '../nifi-api/controller/banners', banners: '../nifi-api/controller/banners',
controller: '../nifi-api/controller', controller: '../nifi-api/controller',
controllerConfig: '../nifi-api/controller/config', controllerConfig: '../nifi-api/controller/config',
loginConfig: '../nifi-api/controller/login/config',
cluster: '../nifi-api/cluster', cluster: '../nifi-api/cluster',
d3Script: 'js/d3/d3.min.js' d3Script: 'js/d3/d3.min.js'
} }
@ -1037,6 +1038,13 @@ nf.Canvas = (function () {
dataType: 'json' dataType: 'json'
}); });
// get the login config
var loginXhr = $.ajax({
type: 'GET',
url: config.urls.loginConfig,
dataType: 'json'
});
// create the deferred cluster request // create the deferred cluster request
var isClusteredRequest = $.Deferred(function (deferred) { var isClusteredRequest = $.Deferred(function (deferred) {
$.ajax({ $.ajax({
@ -1063,9 +1071,10 @@ nf.Canvas = (function () {
}); });
// ensure the authorities and config request is processed first // ensure the authorities and config request is processed first
$.when(authoritiesXhr, configXhr).done(function (authoritiesResult, configResult) { $.when(authoritiesXhr, configXhr, loginXhr).done(function (authoritiesResult, configResult, loginResult) {
var authoritiesResponse = authoritiesResult[0]; var authoritiesResponse = authoritiesResult[0];
var configResponse = configResult[0]; var configResponse = configResult[0];
var loginResponse = loginResult[0];
// set the user's authorities // set the user's authorities
nf.Common.setAuthorities(authoritiesResponse.authorities); nf.Common.setAuthorities(authoritiesResponse.authorities);
@ -1076,6 +1085,7 @@ nf.Canvas = (function () {
// get the config details // get the config details
var configDetails = configResponse.config; var configDetails = configResponse.config;
var loginDetails = loginResponse.config;
// when both request complete, load the application // when both request complete, load the application
isClusteredRequest.done(function () { isClusteredRequest.done(function () {
@ -1095,7 +1105,7 @@ nf.Canvas = (function () {
nf.ContextMenu.init(); nf.ContextMenu.init();
nf.CanvasToolbar.init(); nf.CanvasToolbar.init();
nf.CanvasToolbox.init(); nf.CanvasToolbox.init();
nf.CanvasHeader.init(); nf.CanvasHeader.init(loginDetails.supportsLogin);
nf.GraphControl.init(); nf.GraphControl.init();
nf.Search.init(); nf.Search.init();
nf.Settings.init(); nf.Settings.init();

View File

@ -22,10 +22,40 @@ $(document).ready(function () {
}); });
nf.Login = (function () { nf.Login = (function () {
var loadControllerConfiguration = function () {
return $.ajax({
type: 'GET',
url: '../nifi-api/controller/login/config',
dataType: 'json'
});
};
var initializePage = function () { var initializePage = function () {
return $.Deferred(function (deferred) { return loadControllerConfiguration().done(function (response) {
console.log('hello there'); var config = response.config;
deferred.resolve();
if (config.supportsLogin === true) {
}
if (config.supportsRegistration === true) {
}
});
};
var login = function () {
var username = $('#username').val();
var password = $('#password').val();
return $.ajax({
type: 'POST',
url: '../nifi-api/token',
data: {
'username': username,
'password': password
},
dataType: 'json'
}); });
}; };
@ -34,7 +64,13 @@ nf.Login = (function () {
* Initializes the login page. * Initializes the login page.
*/ */
init: function () { init: function () {
initializePage().done(function () { initializePage();
// handle login click
$('#login-button').on('click', function () {
login().done(function (response) {
console.log(response);
});
}); });
} }
}; };