NIFI-655: - Refactoring certificate extraction and validation. - Refactoring how expiration is specified in the login identity providers. - Adding unit tests for the access endpoints. - Code clean up.
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/a1962077 Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/a1962077 Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/a1962077 Branch: refs/heads/master Commit: a196207725a37aecaccc8c7858cec3a4d1c4ff55 Parents: 7529694 Author: Matt Gilman <[email protected]> Authored: Tue Nov 17 17:02:41 2015 -0500 Committer: Matt Gilman <[email protected]> Committed: Tue Nov 17 17:02:41 2015 -0500 ---------------------------------------------------------------------- .../authentication/AuthenticationResponse.java | 14 +- .../authentication/LoginIdentityProvider.java | 7 - .../web/NiFiWebApiSecurityConfiguration.java | 25 +- .../org/apache/nifi/web/api/AccessResource.java | 188 ++++++----- .../nifi/web/api/ApplicationResource.java | 8 +- .../nifi/web/controller/ControllerFacade.java | 2 +- .../src/main/resources/nifi-web-api-context.xml | 3 +- .../accesscontrol/AccessTokenEndpointTest.java | 292 +++++++++++++++++ .../util/NiFiTestAuthorizationProvider.java | 3 +- .../util/NiFiTestLoginIdentityProvider.java | 75 +++++ .../nifi/integration/util/NiFiTestServer.java | 9 +- .../nifi/integration/util/NiFiTestUser.java | 229 ++++++++++++-- ...he.nifi.authentication.LoginIdentityProvider | 15 + .../access-control/controller-services.xml | 18 -- .../access-control/login-identity-providers.xml | 24 ++ .../resources/access-control/nifi.properties | 9 +- .../access-control/reporting-tasks.xml | 17 - .../web/security/NiFiAuthenticationFilter.java | 14 +- .../security/NiFiAuthenticationProvider.java | 8 +- .../nifi/web/security/ProxiedEntitiesUtils.java | 117 +++---- .../authorization/NiFiAuthorizationService.java | 15 +- .../security/jwt/JwtAuthenticationFilter.java | 3 +- .../nifi/web/security/jwt/JwtService.java | 2 +- .../security/node/NodeAuthorizedUserFilter.java | 68 ++-- .../LoginIdentityProviderFactoryBean.java | 7 - .../security/x509/X509AuthenticationFilter.java | 63 +--- .../x509/X509AuthenticationFilterOld.java | 317 ------------------- .../security/x509/X509CertificateExtractor.java | 4 +- .../security/x509/X509CertificateValidator.java | 10 +- .../web/security/x509/X509IdentityProvider.java | 92 ++++++ .../x509/ocsp/OcspCertificateValidator.java | 20 +- .../resources/nifi-web-security-context.xml | 6 + .../NiFiAuthorizationServiceTest.java | 2 +- .../src/main/webapp/js/nf/login/nf-login.js | 2 - .../apache/nifi/ldap/AbstractLdapProvider.java | 9 +- 35 files changed, 985 insertions(+), 712 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java b/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java index 8f57810..a64947b 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java +++ b/nifi-api/src/main/java/org/apache/nifi/authentication/AuthenticationResponse.java @@ -23,16 +23,19 @@ public class AuthenticationResponse { private final String identity; private final String username; + private final long expiration; /** * Creates an authentication response. The username and how long the authentication is valid in milliseconds * * @param identity The user identity * @param username The username + * @param expiration The expiration in milliseconds */ - public AuthenticationResponse(final String identity, final String username) { + public AuthenticationResponse(final String identity, final String username, final long expiration) { this.identity = identity; this.username = username; + this.expiration = expiration; } public String getIdentity() { @@ -43,4 +46,13 @@ public class AuthenticationResponse { return username; } + /** + * Returns the expiration of a given authentication in milliseconds. + * + * @return The expiration in milliseconds + */ + public long getExpiration() { + return expiration; + } + } http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java index 5f4db40..54becb3 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java +++ b/nifi-api/src/main/java/org/apache/nifi/authentication/LoginIdentityProvider.java @@ -37,13 +37,6 @@ public interface LoginIdentityProvider { AuthenticationResponse authenticate(LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException; /** - * Returns the expiration of a given authentication in milliseconds. - * - * @return The expiration in milliseconds - */ - long getExpiration(); - - /** * Called immediately after instance creation for implementers to perform additional setup * * @param initializationContext in which to initialize http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java index 216e311..e8ed267 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiSecurityConfiguration.java @@ -28,7 +28,7 @@ import org.apache.nifi.web.security.node.NodeAuthorizedUserFilter; import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken; import org.apache.nifi.web.security.x509.X509AuthenticationFilter; import org.apache.nifi.web.security.x509.X509CertificateExtractor; -import org.apache.nifi.web.security.x509.X509CertificateValidator; +import org.apache.nifi.web.security.x509.X509IdentityProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -42,7 +42,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; -import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; /** * NiFi Web Api Spring security @@ -56,9 +55,8 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte private UserService userService; private AuthenticationUserDetailsService userDetailsService; private JwtService jwtService; - private X509CertificateValidator certificateValidator; private X509CertificateExtractor certificateExtractor; - private X509PrincipalExtractor principalExtractor; + private X509IdentityProvider certificateIdentityProvider; private LoginIdentityProvider loginIdentityProvider; public NiFiWebApiSecurityConfiguration() { @@ -113,7 +111,11 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte } private NodeAuthorizedUserFilter buildNodeAuthorizedUserFilter() { - return new NodeAuthorizedUserFilter(properties); + final NodeAuthorizedUserFilter nodeFilter = new NodeAuthorizedUserFilter(); + nodeFilter.setProperties(properties); + nodeFilter.setCertificateExtractor(certificateExtractor); + nodeFilter.setCertificateIdentityProvider(certificateIdentityProvider); + return nodeFilter; } private JwtAuthenticationFilter buildJwtFilter() throws Exception { @@ -127,9 +129,8 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte private X509AuthenticationFilter buildX509Filter() throws Exception { final X509AuthenticationFilter x509Filter = new X509AuthenticationFilter(); x509Filter.setProperties(properties); - x509Filter.setPrincipalExtractor(principalExtractor); x509Filter.setCertificateExtractor(certificateExtractor); - x509Filter.setCertificateValidator(certificateValidator); + x509Filter.setCertificateIdentityProvider(certificateIdentityProvider); x509Filter.setAuthenticationManager(authenticationManager()); return x509Filter; } @@ -166,17 +167,13 @@ public class NiFiWebApiSecurityConfiguration extends WebSecurityConfigurerAdapte } @Autowired - public void setCertificateValidator(X509CertificateValidator certificateValidator) { - this.certificateValidator = certificateValidator; - } - - @Autowired public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) { this.certificateExtractor = certificateExtractor; } @Autowired - public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) { - this.principalExtractor = principalExtractor; + public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) { + this.certificateIdentityProvider = certificateIdentityProvider; } + } http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java index 9cb4141..67eb8b4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/AccessResource.java @@ -30,8 +30,6 @@ import com.wordnik.swagger.annotations.ApiParam; import com.wordnik.swagger.annotations.ApiResponse; import com.wordnik.swagger.annotations.ApiResponses; import java.net.URI; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.List; @@ -41,6 +39,7 @@ import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; +import org.apache.commons.lang3.StringUtils; import org.apache.nifi.admin.service.AdministrationException; import org.apache.nifi.authentication.AuthenticationResponse; import org.apache.nifi.authentication.LoginCredentials; @@ -48,8 +47,6 @@ import org.apache.nifi.authentication.LoginIdentityProvider; import org.apache.nifi.authentication.exception.IdentityAccessException; import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException; import org.apache.nifi.security.util.CertificateUtils; -import org.apache.nifi.util.StringUtils; -import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID; import org.apache.nifi.web.api.dto.AccessStatusDTO; import org.apache.nifi.web.api.dto.AccessConfigurationDTO; import org.apache.nifi.web.api.dto.RevisionDTO; @@ -62,15 +59,15 @@ import org.apache.nifi.web.security.jwt.JwtService; import org.apache.nifi.web.security.token.LoginAuthenticationToken; import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken; import org.apache.nifi.web.security.x509.X509CertificateExtractor; -import org.apache.nifi.web.security.x509.X509CertificateValidator; +import org.apache.nifi.web.security.x509.X509IdentityProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.AccountStatusException; -import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.authentication.AuthenticationServiceException; 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.preauth.x509.X509PrincipalExtractor; /** * RESTful endpoint for managing a cluster. @@ -82,13 +79,15 @@ import org.springframework.security.web.authentication.preauth.x509.X509Principa ) public class AccessResource extends ApplicationResource { - private NiFiProperties properties; + private static final Logger logger = LoggerFactory.getLogger(AccessResource.class); - private X509CertificateValidator certificateValidator; - private X509CertificateExtractor certificateExtractor; - private X509PrincipalExtractor principalExtractor; + private static final String AUTHORIZATION = "Authorization"; + + private NiFiProperties properties; private LoginIdentityProvider loginIdentityProvider; + private X509CertificateExtractor certificateExtractor; + private X509IdentityProvider certificateIdentityProvider; private JwtService jwtService; private AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService; @@ -172,57 +171,64 @@ public class AccessResource extends ApplicationResource { final AccessStatusDTO accessStatus = new AccessStatusDTO(); try { - // look for a certificate - final X509Certificate certificate = certificateExtractor.extractClientCertificate(httpServletRequest); + final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(httpServletRequest); - // if no certificate, just check the credentials - if (certificate == null) { - final String principal = jwtService.getAuthentication(httpServletRequest); + // if there is not certificate, consider a token + if (certificates == null) { + // look for an authorization token + final String authorization = httpServletRequest.getHeader(AUTHORIZATION); - // ensure we have something we can work with (certificate or crendentials) - if (principal == null) { + // if there is no authorization header, we don't know the user + if (authorization == null) { accessStatus.setStatus(AccessStatusDTO.Status.UNKNOWN.name()); accessStatus.setMessage("No credentials supplied, unknown user."); } else { - // set the user identity - accessStatus.setIdentity(principal); - accessStatus.setUsername(CertificateUtils.extractUsername(principal)); - - // without a certificate, this is not a proxied request - final List<String> chain = Arrays.asList(principal); - - // check authorization for this user - checkAuthorization(chain); - - // no issues with authorization - accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); - accessStatus.setStatus("Account is active and authorized"); + // TODO - use this token with the JWT service + final String token = StringUtils.substringAfterLast(authorization, " "); + + // TODO - do not call this method of the jwt service + final String principal = jwtService.getAuthentication(httpServletRequest); + + // TODO - catch jwt exception? + // ensure we have something we can work with (certificate or crendentials) + if (principal == null) { + throw new IllegalArgumentException("The specific token is not valid."); + } else { + // set the user identity + accessStatus.setIdentity(principal); + accessStatus.setUsername(CertificateUtils.extractUsername(principal)); + + // without a certificate, this is not a proxied request + final List<String> chain = Arrays.asList(principal); + + // check authorization for this user + checkAuthorization(chain); + + // no issues with authorization + accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); + accessStatus.setMessage("Account is active and authorized"); + } } } else { - // we have a certificate so let's consider a proxy chain - final String principal = principalExtractor.extractPrincipal(certificate).toString(); - - try { - // validate the certificate - certificateValidator.validateClientCertificate(httpServletRequest, certificate); - } catch (CertificateExpiredException cee) { - throw new IllegalArgumentException(String.format("Client certificate for (%s) is expired.", principal), cee); - } catch (CertificateNotYetValidException cnyve) { - throw new IllegalArgumentException(String.format("Client certificate for (%s) is not yet valid.", principal), cnyve); - } catch (final Exception e) { - throw new IllegalArgumentException(e.getMessage(), e); - } + final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates); - // set the user identity - accessStatus.setIdentity(principal); - accessStatus.setUsername(CertificateUtils.extractUsername(principal)); + // get the proxy chain and ensure its populated + final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity()); + if (proxyChain.isEmpty()) { + logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity())); + throw new IllegalArgumentException("Unable to determine the user from the incoming request."); + } // ensure the proxy chain is authorized - checkAuthorization(ProxiedEntitiesUtils.buildProxyChain(httpServletRequest, principal)); + checkAuthorization(proxyChain); + + // set the user identity + accessStatus.setIdentity(proxyChain.get(0)); + accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0))); // no issues with authorization accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); - accessStatus.setStatus("Account is active and authorized"); + accessStatus.setMessage("Account is active and authorized"); } } catch (final UsernameNotFoundException unfe) { accessStatus.setStatus(AccessStatusDTO.Status.UNREGISTERED.name()); @@ -277,6 +283,7 @@ public class AccessResource extends ApplicationResource { @ApiResponses( value = { @ApiResponse(code = 400, message = "NiFi was unable to complete the request because it was invalid. The request should not be retried without modification."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.") } ) @@ -297,58 +304,42 @@ public class AccessResource extends ApplicationResource { final LoginAuthenticationToken loginAuthenticationToken; - // if we don't have username/password, consider JWT or x509 - if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { - // look for a certificate - final X509Certificate certificate = certificateExtractor.extractClientCertificate(httpServletRequest); - - // if there is no certificate, look for an existing token - if (certificate == null) { - // if not configured for login, don't consider existing tokens - if (loginIdentityProvider == null) { - throw new IllegalStateException("Login not supported."); - } + final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(httpServletRequest); - // look for the principal - final String principal = jwtService.getAuthentication(httpServletRequest); - if (principal == null) { - throw new AuthenticationCredentialsNotFoundException("Unable to issue token as issue token as no credentials were found in the request."); - } - - // create the authentication token - loginAuthenticationToken = new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration()); - } else { - // extract the principal - final String principal = principalExtractor.extractPrincipal(certificate).toString(); - - try { - certificateValidator.validateClientCertificate(httpServletRequest, certificate); - } catch (CertificateExpiredException cee) { - throw new IllegalArgumentException(String.format("Client certificate for (%s) is expired.", principal), cee); - } catch (CertificateNotYetValidException cnyve) { - throw new IllegalArgumentException(String.format("Client certificate for (%s) is not yet valid.", principal), cnyve); - } catch (final Exception e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - - // authorize the proxy if necessary - authorizeProxyIfNecessary(ProxiedEntitiesUtils.buildProxyChain(httpServletRequest, principal)); - - // create the authentication token - loginAuthenticationToken = new LoginAuthenticationToken(principal, loginIdentityProvider.getExpiration()); + // if there is not certificate, consider login credentials + if (certificates == null) { + // ensure we have login credentials + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + throw new IllegalArgumentException("The username and password must be specified."); } - } else { + try { // attempt to authenticate final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password)); // create the authentication token - loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getUsername(), loginIdentityProvider.getExpiration()); + loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getUsername(), authenticationResponse.getExpiration()); } catch (final InvalidLoginCredentialsException ilce) { throw new IllegalArgumentException("The supplied username and password are not valid.", ilce); } catch (final IdentityAccessException iae) { throw new AdministrationException(iae.getMessage(), iae); } + } else { + // consider a certificate + final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates); + + // get the proxy chain and ensure its populated + final List<String> proxyChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(httpServletRequest, authenticationResponse.getIdentity()); + if (proxyChain.isEmpty()) { + logger.error(String.format("Unable to parse the proxy chain %s from the incoming request.", authenticationResponse.getIdentity())); + throw new IllegalArgumentException("Unable to determine the user from the incoming request."); + } + + // authorize the proxy if necessary + authorizeProxyIfNecessary(proxyChain); + + // create the authentication token + loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration()); } // generate JWT for response @@ -371,11 +362,14 @@ public class AccessResource extends ApplicationResource { 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 + // we can issue a new token to the end user which they will use to identify themselves + // when they enter a new account request + } catch (final AuthenticationServiceException ase) { + // throw an administration exception which will return a 500 + throw new AdministrationException(ase.getMessage(), ase); } catch (final Exception e) { - // any other issue we're going to treat as an authentication exception which will return 401 - throw new AdministrationException(e.getMessage(), e) { - }; + // any other issue we're going to treat as access denied exception which will return 403 + throw new AccessDeniedException(e.getMessage(), e); } } } @@ -393,16 +387,12 @@ public class AccessResource extends ApplicationResource { this.jwtService = jwtService; } - public void setCertificateValidator(X509CertificateValidator certificateValidator) { - this.certificateValidator = certificateValidator; - } - public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) { this.certificateExtractor = certificateExtractor; } - public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) { - this.principalExtractor = principalExtractor; + public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) { + this.certificateIdentityProvider = certificateIdentityProvider; } public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthenticationRequestToken> userDetailsService) { http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java index 2e15d30..d0c36d4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java @@ -53,6 +53,8 @@ import org.apache.nifi.web.util.WebUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.nifi.user.NiFiUser; +import org.apache.nifi.web.security.user.NiFiUserUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; @@ -363,9 +365,9 @@ public abstract class ApplicationResource { if (httpServletRequest.isSecure()) { // add the certificate DN to the proxy chain - final String xProxiedEntitiesChain = ProxiedEntitiesUtils.getXProxiedEntitiesChain(httpServletRequest); - if (StringUtils.isNotBlank(xProxiedEntitiesChain)) { - result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, xProxiedEntitiesChain); + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + if (user != null) { + result.put(PROXIED_ENTITIES_CHAIN_HTTP_HEADER, ProxiedEntitiesUtils.buildProxiedEntitiesChainString(user)); } // add the user's authorities (if any) to the headers http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java index 0e1b20b..a02d1fd 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java @@ -823,7 +823,7 @@ public class ControllerFacade { final Map<String, String> attributes = event.getAttributes(); // calculate the dn chain - final List<String> dnChain = ProxiedEntitiesUtils.getXProxiedEntitiesChain(user); + final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user); // ensure the users in this chain are allowed to download this content final DownloadAuthorization downloadAuthorization = userService.authorizeDownload(dnChain, attributes); http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml index e992dc9..73929d8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml @@ -243,9 +243,8 @@ </bean> <bean id="accessResource" class="org.apache.nifi.web.api.AccessResource" scope="singleton"> <property name="properties" ref="nifiProperties"/> - <property name="certificateValidator" ref="certificateValidator"/> <property name="certificateExtractor" ref="certificateExtractor"/> - <property name="principalExtractor" ref="principalExtractor"/> + <property name="certificateIdentityProvider" ref="certificateIdentityProvider"/> <property name="loginIdentityProvider" ref="loginIdentityProvider"/> <property name="jwtService" ref="jwtService"/> <property name="userDetailsService" ref="userDetailsService"/> http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java new file mode 100644 index 0000000..82fe73a --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AccessTokenEndpointTest.java @@ -0,0 +1,292 @@ +/* + * 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.integration.accesscontrol; + +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import javax.net.ssl.SSLContext; +import org.apache.commons.io.FileUtils; +import org.apache.nifi.integration.util.NiFiTestServer; +import org.apache.nifi.integration.util.NiFiTestUser; +import org.apache.nifi.integration.util.SourceTestProcessor; +import org.apache.nifi.nar.ExtensionManager; +import org.apache.nifi.nar.NarClassLoaders; +import org.apache.nifi.security.util.SslContextFactory; +import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.api.dto.AccessConfigurationDTO; +import org.apache.nifi.web.api.dto.AccessStatusDTO; +import org.apache.nifi.web.api.dto.ProcessorDTO; +import org.apache.nifi.web.api.dto.RevisionDTO; +import org.apache.nifi.web.api.entity.AccessConfigurationEntity; +import org.apache.nifi.web.api.entity.AccessStatusEntity; +import org.apache.nifi.web.api.entity.ProcessorEntity; +import org.apache.nifi.web.util.WebUtils; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Access token endpoint test. + */ +public class AccessTokenEndpointTest { + + private static final String CLIENT_ID = "token-endpoint-id"; + private static final String CONTEXT_PATH = "/nifi-api"; + private static final String FLOW_XML_PATH = "target/test-classes/access-control/flow-admin.xml"; + + private static NiFiTestServer SERVER; + private static NiFiTestUser TOKEN_USER; + private static String BASE_URL; + + @BeforeClass + public static void setup() throws Exception { + // configure the location of the nifi properties + File nifiPropertiesFile = new File("src/test/resources/access-control/nifi.properties"); + System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, nifiPropertiesFile.getAbsolutePath()); + + // update the flow.xml property + NiFiProperties props = NiFiProperties.getInstance(); + props.setProperty("nifi.flow.configuration.file", FLOW_XML_PATH); + + // delete the database directory to avoid issues with re-registration in testRequestAccessUsingToken + FileUtils.deleteDirectory(props.getDatabaseRepositoryPath().toFile()); + + // load extensions + NarClassLoaders.load(props); + ExtensionManager.discoverExtensions(); + + // start the server + SERVER = new NiFiTestServer("src/main/webapp", CONTEXT_PATH); + SERVER.startServer(); + SERVER.loadFlow(); + + // get the base url + BASE_URL = SERVER.getBaseUrl() + CONTEXT_PATH; + + // create the user + final Client client = WebUtils.createClient(null, createTrustContext(props)); + TOKEN_USER = new NiFiTestUser(client, null); + } + + private static SSLContext createTrustContext(final NiFiProperties props) throws Exception { + return SslContextFactory.createTrustSslContext(props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE), + props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray(), + props.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE), "TLS"); + } + + // ----------- + // LOGIN CONIG + // ----------- + /** + * Test getting access configuration. + * + * @throws Exception ex + */ + @Test + public void testGetAccessConfig() throws Exception { + String url = BASE_URL + "/access/config"; + + ClientResponse response = TOKEN_USER.testGet(url); + + // ensure the request is successful + Assert.assertEquals(200, response.getStatus()); + + // extract the process group + AccessConfigurationEntity accessConfigEntity = response.getEntity(AccessConfigurationEntity.class); + + // ensure there is content + Assert.assertNotNull(accessConfigEntity); + + // extract the process group dto + AccessConfigurationDTO accessConfig = accessConfigEntity.getConfig(); + + // verify config + Assert.assertTrue(accessConfig.getSupportsLogin()); + Assert.assertFalse(accessConfig.getSupportsAnonymous()); + } + + /** + * Obtains a token and creates a processor using it. + * + * @throws Exception ex + */ + @Test + public void testCreateProcessorUsingToken() throws Exception { + String url = BASE_URL + "/access/token"; + + ClientResponse response = TOKEN_USER.testCreateToken(url, "user@nifi", "whateve"); + + // ensure the request is successful + Assert.assertEquals(201, response.getStatus()); + + // get the token + String token = response.getEntity(String.class); + + // attempt to create a processor with it + createProcessor(token); + } + + private ProcessorDTO createProcessor(final String token) throws Exception { + String url = BASE_URL + "/controller/process-groups/root/processors"; + + // authorization header + Map<String, String> headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + token); + + // create the processor + ProcessorDTO processor = new ProcessorDTO(); + processor.setName("Copy"); + processor.setType(SourceTestProcessor.class.getName()); + + // create the revision + final RevisionDTO revision = new RevisionDTO(); + revision.setClientId(CLIENT_ID); + revision.setVersion(NiFiTestUser.REVISION); + + // create the entity body + ProcessorEntity entity = new ProcessorEntity(); + entity.setRevision(revision); + entity.setProcessor(processor); + + // perform the request + ClientResponse response = TOKEN_USER.testPostWithHeaders(url, entity, headers); + + // ensure the request is successful + Assert.assertEquals(201, response.getStatus()); + + // get the entity body + entity = response.getEntity(ProcessorEntity.class); + + // verify creation + processor = entity.getProcessor(); + Assert.assertEquals("Copy", processor.getName()); + Assert.assertEquals("org.apache.nifi.integration.util.SourceTestProcessor", processor.getType()); + + return processor; + } + + /** + * Verifies the response when bad credentials are specified. + * + * @throws Exception ex + */ + @Test + public void testInvalidCredentials() throws Exception { + String url = BASE_URL + "/access/token"; + + ClientResponse response = TOKEN_USER.testCreateToken(url, "user@nifi", "not a real password"); + + // ensure the request is successful + Assert.assertEquals(400, response.getStatus()); + } + + /** + * Verifies the response when the user is known. + * + * @throws Exception ex + */ + @Test + public void testUnkownUser() throws Exception { + String url = BASE_URL + "/access/token"; + + ClientResponse response = TOKEN_USER.testCreateToken(url, "not a real user", "not a real password"); + + // ensure the request is successful + Assert.assertEquals(400, response.getStatus()); + } + + /** + * Request access using access token. + * + * @throws Exception ex + */ + @Test + public void testRequestAccessUsingToken() throws Exception { + String accessStatusUrl = BASE_URL + "/access"; + String accessTokenUrl = BASE_URL + "/access/token"; + String registrationUrl = BASE_URL + "/controller/users"; + + ClientResponse response = TOKEN_USER.testGet(accessStatusUrl); + + // ensure the request is successful + Assert.assertEquals(200, response.getStatus()); + + AccessStatusEntity accessStatusEntity = response.getEntity(AccessStatusEntity.class); + AccessStatusDTO accessStatus = accessStatusEntity.getAccessStatus(); + + // verify unknown + Assert.assertEquals("UNKNOWN", accessStatus.getStatus()); + + response = TOKEN_USER.testCreateToken(accessTokenUrl, "unregistered-user@nifi", "password"); + + // ensure the request is successful + Assert.assertEquals(201, response.getStatus()); + + // get the token + String token = response.getEntity(String.class); + + // authorization header + Map<String, String> headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + token); + + // check the status with the token + response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers); + + // ensure the request is successful + Assert.assertEquals(200, response.getStatus()); + + accessStatusEntity = response.getEntity(AccessStatusEntity.class); + accessStatus = accessStatusEntity.getAccessStatus(); + + // verify unregistered + Assert.assertEquals("UNREGISTERED", accessStatus.getStatus()); + + response = TOKEN_USER.testRegisterUser(registrationUrl, "Gimme access", headers); + + // ensure the request is successful + Assert.assertEquals(201, response.getStatus()); + + // check the status with the token + response = TOKEN_USER.testGetWithHeaders(accessStatusUrl, null, headers); + + // ensure the request is successful + Assert.assertEquals(200, response.getStatus()); + + accessStatusEntity = response.getEntity(AccessStatusEntity.class); + accessStatus = accessStatusEntity.getAccessStatus(); + + // verify unregistered + Assert.assertEquals("NOT_ACTIVE", accessStatus.getStatus()); + } + + @AfterClass + public static void cleanup() throws Exception { + // shutdown the server + SERVER.shutdownServer(); + SERVER = null; + + // look for the flow.xml + File flow = new File(FLOW_XML_PATH); + if (flow.exists()) { + flow.delete(); + } + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java index d51b7df..d29be92 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestAuthorizationProvider.java @@ -37,7 +37,7 @@ import org.apache.nifi.authorization.DownloadAuthorization; */ public class NiFiTestAuthorizationProvider implements AuthorityProvider { - private Map<String, Set<Authority>> users; + private final Map<String, Set<Authority>> users; /** * Creates a new FileAuthorizationProvider. @@ -48,6 +48,7 @@ public class NiFiTestAuthorizationProvider implements AuthorityProvider { users.put("CN=Lastname Firstname Middlename monitor, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown", EnumSet.of(Authority.ROLE_MONITOR)); users.put("CN=Lastname Firstname Middlename dfm, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown", EnumSet.of(Authority.ROLE_DFM)); users.put("CN=Lastname Firstname Middlename admin, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown", EnumSet.of(Authority.ROLE_ADMIN)); + users.put("user@nifi", EnumSet.of(Authority.ROLE_DFM)); } @Override http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java new file mode 100644 index 0000000..8ee51d9 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestLoginIdentityProvider.java @@ -0,0 +1,75 @@ +/* + * 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.integration.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.apache.nifi.authorization.exception.ProviderCreationException; +import org.apache.nifi.authentication.AuthenticationResponse; +import org.apache.nifi.authentication.LoginCredentials; +import org.apache.nifi.authentication.LoginIdentityProvider; +import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext; +import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext; +import org.apache.nifi.authentication.exception.IdentityAccessException; +import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException; + +/** + * + */ +public class NiFiTestLoginIdentityProvider implements LoginIdentityProvider { + + private final Map<String, String> users; + + /** + * Creates a new FileAuthorizationProvider. + */ + public NiFiTestLoginIdentityProvider() { + users = new HashMap<>(); + users.put("user@nifi", "whateve"); + users.put("unregistered-user@nifi", "password"); + } + + private void checkUser(final String user, final String password) { + if (!users.containsKey(user)) { + throw new InvalidLoginCredentialsException("Unknown user"); + } + + if (!users.get(user).equals(password)) { + throw new InvalidLoginCredentialsException("Invalid password"); + } + } + + @Override + public AuthenticationResponse authenticate(LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException { + checkUser(credentials.getUsername(), credentials.getPassword()); + return new AuthenticationResponse(credentials.getUsername(), credentials.getUsername(), TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS)); + } + + @Override + public void initialize(LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException { + } + + @Override + public void onConfigured(LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException { + } + + @Override + public void preDestruction() { + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java index 42b0aab..38c2d41 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java @@ -78,8 +78,12 @@ public class NiFiTestServer { private void createSecureConnector() { org.eclipse.jetty.util.ssl.SslContextFactory contextFactory = new org.eclipse.jetty.util.ssl.SslContextFactory(); - // need client auth - contextFactory.setNeedClientAuth(properties.getNeedClientAuth()); + // require client auth when not supporting login or anonymous access + if (StringUtils.isBlank(properties.getProperty(NiFiProperties.SECURITY_USER_LOGIN_IDENTITY_PROVIDER)) && properties.getAnonymousAuthorities().isEmpty()) { + contextFactory.setNeedClientAuth(true); + } else { + contextFactory.setWantClientAuth(true); + } /* below code sets JSSE system properties when values are provided */ // keystore properties @@ -163,7 +167,6 @@ public class NiFiTestServer { } public Client getClient() { - // create the client return WebUtils.createClient(null, SslContextFactory.createSslContext(properties)); } http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java index c0e9246..621dc09 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestUser.java @@ -34,9 +34,27 @@ public class NiFiTestUser { private final Client client; private final String proxyDn; - public NiFiTestUser(Client client, String dn) { + public NiFiTestUser(Client client, String proxyDn) { this.client = client; - this.proxyDn = ProxiedEntitiesUtils.formatProxyDn(dn); + if (proxyDn != null) { + this.proxyDn = ProxiedEntitiesUtils.formatProxyDn(proxyDn); + } else { + this.proxyDn = null; + } + } + + /** + * Conditionally adds the proxied entities chain. + * + * @param builder the resource builder + * @return the resource builder + */ + private WebResource.Builder addProxiedEntities(final WebResource.Builder builder) { + if (proxyDn == null) { + return builder; + } else { + return builder.header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn); + } } /** @@ -58,6 +76,18 @@ public class NiFiTestUser { * @return response */ public ClientResponse testGet(String url, Map<String, String> queryParams) { + return testGetWithHeaders(url, queryParams, null); + } + + /** + * Performs a GET using the specified url and query parameters. + * + * @param url url + * @param queryParams params + * @param headers http headers + * @return response + */ + public ClientResponse testGetWithHeaders(String url, Map<String, String> queryParams, Map<String, String> headers) { // get the resource WebResource resource = client.resource(url); @@ -68,8 +98,18 @@ public class NiFiTestUser { } } + // get the builder + WebResource.Builder builder = addProxiedEntities(resource.accept(MediaType.APPLICATION_JSON)); + + // append any headers + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + builder = builder.header(key, headers.get(key)); + } + } + // perform the query - return resource.accept(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn).get(ClientResponse.class); + return builder.get(ClientResponse.class); } /** @@ -92,14 +132,34 @@ public class NiFiTestUser { * @throws Exception ex */ public ClientResponse testPost(String url, Object entity) throws Exception { + return testPostWithHeaders(url, entity, null); + } + + /** + * Performs a POST using the specified url and entity body. + * + * @param url url + * @param entity entity + * @param headers http headers + * @return response + * @throws Exception ex + */ + public ClientResponse testPostWithHeaders(String url, Object entity, Map<String, String> headers) throws Exception { // get the resource - WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn); + WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON)); // include the request entity if (entity != null) { resourceBuilder = resourceBuilder.entity(entity); } + // append any headers + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + resourceBuilder = resourceBuilder.header(key, headers.get(key)); + } + } + // perform the request return resourceBuilder.post(ClientResponse.class); } @@ -109,18 +169,38 @@ public class NiFiTestUser { * * @param url url * @param entity entity - * @return repsonse + * @return response * @throws Exception ex */ public ClientResponse testPostMultiPart(String url, Object entity) throws Exception { + return testPostMultiPartWithHeaders(url, entity, null); + } + + /** + * Performs a POST using the specified url and entity body. + * + * @param url url + * @param entity entity + * @param headers http headers + * @return response + * @throws Exception ex + */ + public ClientResponse testPostMultiPartWithHeaders(String url, Object entity, Map<String, String> headers) throws Exception { // get the resource - WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn); + WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_XML).type(MediaType.MULTIPART_FORM_DATA)); // include the request entity if (entity != null) { resourceBuilder = resourceBuilder.entity(entity); } + // append any headers + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + resourceBuilder = resourceBuilder.header(key, headers.get(key)); + } + } + // perform the request return resourceBuilder.post(ClientResponse.class); } @@ -134,6 +214,19 @@ public class NiFiTestUser { * @throws java.lang.Exception ex */ public ClientResponse testPost(String url, Map<String, String> formData) throws Exception { + return testPostWithHeaders(url, formData, null); + } + + /** + * Performs a POST using the specified url and form data. + * + * @param url url + * @param formData form data + * @param headers http headers + * @return response + * @throws java.lang.Exception ex + */ + public ClientResponse testPostWithHeaders(String url, Map<String, String> formData, Map<String, String> headers) throws Exception { // convert the form data MultivaluedMapImpl entity = new MultivaluedMapImpl(); for (String key : formData.keySet()) { @@ -141,14 +234,20 @@ public class NiFiTestUser { } // get the resource - WebResource.Builder resourceBuilder - = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn); + WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED)); // add the form data if necessary if (!entity.isEmpty()) { resourceBuilder = resourceBuilder.entity(entity); } + // append any headers + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + resourceBuilder = resourceBuilder.header(key, headers.get(key)); + } + } + // perform the request return resourceBuilder.post(ClientResponse.class); } @@ -162,14 +261,34 @@ public class NiFiTestUser { * @throws java.lang.Exception ex */ public ClientResponse testPut(String url, Object entity) throws Exception { + return testPutWithHeaders(url, entity, null); + } + + /** + * Performs a PUT using the specified url and entity body. + * + * @param url url + * @param entity entity + * @param headers http headers + * @return response + * @throws java.lang.Exception ex + */ + public ClientResponse testPutWithHeaders(String url, Object entity, Map<String, String> headers) throws Exception { // get the resource - WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn); + WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON)); // include the request entity if (entity != null) { resourceBuilder = resourceBuilder.entity(entity); } + // append any headers + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + resourceBuilder = resourceBuilder.header(key, headers.get(key)); + } + } + // perform the request return resourceBuilder.put(ClientResponse.class); } @@ -183,6 +302,19 @@ public class NiFiTestUser { * @throws java.lang.Exception ex */ public ClientResponse testPut(String url, Map<String, String> formData) throws Exception { + return testPutWithHeaders(url, formData, null); + } + + /** + * Performs a PUT using the specified url and form data. + * + * @param url url + * @param formData form data + * @param headers http headers + * @return response + * @throws java.lang.Exception ex + */ + public ClientResponse testPutWithHeaders(String url, Map<String, String> formData, Map<String, String> headers) throws Exception { // convert the form data MultivaluedMapImpl entity = new MultivaluedMapImpl(); for (String key : formData.keySet()) { @@ -190,14 +322,20 @@ public class NiFiTestUser { } // get the resource - WebResource.Builder resourceBuilder - = client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn); + WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED)); // add the form data if necessary if (!entity.isEmpty()) { resourceBuilder = resourceBuilder.entity(entity); } + // append any headers + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + resourceBuilder = resourceBuilder.header(key, headers.get(key)); + } + } + // perform the request return resourceBuilder.put(ClientResponse.class); } @@ -210,24 +348,26 @@ public class NiFiTestUser { * @throws java.lang.Exception ex */ public ClientResponse testDelete(String url) throws Exception { - return testDelete(url, (Object) null); + return testDelete(url, null); } /** * Performs a DELETE using the specified url and entity. * * @param url url - * @param entity entity - * @return repsonse + * @param headers http headers + * @return response * @throws java.lang.Exception ex */ - public ClientResponse testDelete(String url, Object entity) throws Exception { + public ClientResponse testDeleteWithHeaders(String url, Map<String, String> headers) throws Exception { // get the resource - WebResource.Builder resourceBuilder = client.resource(url).accept(MediaType.APPLICATION_JSON).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn); + WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.APPLICATION_JSON)); - // append any query parameters - if (entity != null) { - resourceBuilder = resourceBuilder.entity(entity); + // append any headers + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + resourceBuilder = resourceBuilder.header(key, headers.get(key)); + } } // perform the query @@ -254,7 +394,56 @@ public class NiFiTestUser { } // perform the request - return resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED).header(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxyDn).delete(ClientResponse.class); + return addProxiedEntities(resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_FORM_URLENCODED)).delete(ClientResponse.class); } + /** + * Attempts to create a token with the specified username and password. + * + * @param url the url + * @param username the username + * @param password the password + * @return response + * @throws Exception ex + */ + public ClientResponse testCreateToken(String url, String username, String password) throws Exception { + // convert the form data + MultivaluedMapImpl entity = new MultivaluedMapImpl(); + entity.add("username", username); + entity.add("password", password); + + // get the resource + WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.TEXT_PLAIN).type(MediaType.APPLICATION_FORM_URLENCODED)).entity(entity); + + // perform the request + return resourceBuilder.post(ClientResponse.class); + } + + /** + * Attempts to create a token with the specified username and password. + * + * @param url the url + * @param justification justification + * @param headers http headers + * @return response + * @throws Exception ex + */ + public ClientResponse testRegisterUser(String url, String justification, Map<String, String> headers) throws Exception { + // convert the form data + MultivaluedMapImpl entity = new MultivaluedMapImpl(); + entity.add("justification", justification); + + // get the resource + WebResource.Builder resourceBuilder = addProxiedEntities(client.resource(url).accept(MediaType.TEXT_PLAIN).type(MediaType.APPLICATION_FORM_URLENCODED)).entity(entity); + + // append any headers + if (headers != null && !headers.isEmpty()) { + for (String key : headers.keySet()) { + resourceBuilder = resourceBuilder.header(key, headers.get(key)); + } + } + + // perform the request + return resourceBuilder.post(ClientResponse.class); + } } http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider new file mode 100644 index 0000000..4b42e4f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/META-INF/services/org.apache.nifi.authentication.LoginIdentityProvider @@ -0,0 +1,15 @@ +# 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. +org.apache.nifi.integration.util.NiFiTestLoginIdentityProvider \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml deleted file mode 100644 index f5bd96a..0000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/controller-services.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<!-- - 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. ---> -<services> - -</services> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml new file mode 100644 index 0000000..04120c9 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/login-identity-providers.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + 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. +--> +<!-- + This file lists all authority providers to use when running securely. +--> +<loginIdentityProviders> + <provider> + <identifier>test-provider</identifier> + <class>org.apache.nifi.integration.util.NiFiTestLoginIdentityProvider</class> + </provider> +</loginIdentityProviders> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties index fc20d78..10db651 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/nifi.properties @@ -14,16 +14,15 @@ # limitations under the License. # Core Properties # -nifi.version=nifi 0.2.1-SNAPSHOT +nifi.version=nifi version nifi.flow.configuration.file= nifi.flow.configuration.archive.dir=target/archive nifi.flowcontroller.autoResumeState=true nifi.flowcontroller.graceful.shutdown.period=10 sec nifi.flowservice.writedelay.interval=2 sec -nifi.reporting.task.configuration.file=target/test-classes/access-control/reporting-tasks.xml -nifi.controller.service.configuration.file=target/test-classes/access-control/controller-services.xml nifi.authority.provider.configuration.file=target/test-classes/access-control/authority-providers.xml +nifi.login.identity.provider.configuration.file=target/test-classes/access-control/login-identity-providers.xml nifi.templates.directory=target/test-classes/access-control/templates nifi.ui.banner.text=TEST BANNER nifi.ui.autorefresh.interval=30 sec @@ -93,11 +92,11 @@ nifi.security.truststoreType=JKS nifi.security.truststorePasswd=localtest nifi.security.needClientAuth=true nifi.security.user.authority.provider=test-provider -nifi.security.user.login.identity.provider= +nifi.security.user.login.identity.provider=test-provider nifi.security.authorizedUsers.file=target/test-classes/access-control/users.xml nifi.security.user.credential.cache.duration=1 hr nifi.security.support.new.account.requests= -nifi.security.default.user.roles= +nifi.security.anonymous.authorities= # cluster common properties (cluster manager and nodes must have same values) # nifi.cluster.protocol.heartbeat.interval=5 sec http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml deleted file mode 100644 index 251735e..0000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/resources/access-control/reporting-tasks.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.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. ---> -<tasks> -</tasks> http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java index e5e5c04..ec34ace 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java @@ -29,12 +29,14 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.user.NiFiUser; import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.web.security.token.NiFiAuthenticationRequestToken; import org.apache.nifi.web.security.user.NiFiUserUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.AccountStatusException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; @@ -85,8 +87,13 @@ public abstract class NiFiAuthenticationFilter implements Filter { private void authenticate(final HttpServletRequest request, final HttpServletResponse response) throws IOException { try { - final Authentication authenticated = attemptAuthentication(request, response); + final NiFiAuthenticationRequestToken authenticated = attemptAuthentication(request, response); if (authenticated != null) { + // log the request attempt - response details will be logged later + logger.info(String.format("Attempting request for (%s) %s %s (source ip: %s)", + ProxiedEntitiesUtils.formatProxyDn(StringUtils.join(authenticated.getChain(), "><")), request.getMethod(), + request.getRequestURL().toString(), request.getRemoteAddr())); + final Authentication authorized = authenticationManager.authenticate(authenticated); successfulAuthorization(request, response, authorized); } @@ -97,7 +104,7 @@ public abstract class NiFiAuthenticationFilter implements Filter { } } - public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response); + public abstract NiFiAuthenticationRequestToken attemptAuthentication(HttpServletRequest request, HttpServletResponse response); protected void successfulAuthorization(HttpServletRequest request, HttpServletResponse response, Authentication authResult) { if (logger.isDebugEnabled()) { @@ -127,6 +134,9 @@ public abstract class NiFiAuthenticationFilter implements Filter { response.setStatus(HttpServletResponse.SC_FORBIDDEN); out.println("Access is denied."); } + } else if (ae instanceof BadCredentialsException) { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + out.println(ae.getMessage()); } else if (ae instanceof AccountStatusException) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); out.println(ae.getMessage()); http://git-wip-us.apache.org/repos/asf/nifi/blob/a1962077/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java index 79e8eb2..eb0684b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationProvider.java @@ -47,10 +47,12 @@ public class NiFiAuthenticationProvider implements AuthenticationProvider { final UserDetails userDetails = userDetailsService.loadUserDetails(request); // build an authentication for accesing nifi - return new NiFiAuthorizationToken(userDetails); + final NiFiAuthorizationToken result = new NiFiAuthorizationToken(userDetails); + result.setDetails(request.getDetails()); + return result; } catch (final UsernameNotFoundException unfe) { - // if the result was an authenticated new account request and it could not be authorized because the user was not found, - // return the token so the new account could be created. this must go here to ensure that any proxies have been authorized + // if the authentication request is for a new account and it could not be authorized because the user was not found, + // return the token so the new account could be created. this must go here toe nsure that any proxies have been authorized if (isNewAccountAuthenticationToken(request)) { return new NewAccountAuthenticationToken(((NewAccountAuthenticationRequestToken) authentication).getNewAccountRequest()); } else {
