From f0f74fe4049f38a8c022c4bf47494b12f9ce7174 Mon Sep 17 00:00:00 2001 From: Matt Gilman Date: Wed, 1 Jun 2016 08:09:04 -0400 Subject: [PATCH] NIFI-1928: - Fixing UI action for bulk delete. - Removing registration form. - Fixing default visibility of the anonymous user warning. --- .../web/api/dto/AccessConfigurationDTO.java | 16 -- .../nifi/web/api/dto/AccessStatusDTO.java | 2 - .../nifi/web/api/entity/IdentityEntity.java | 11 ++ .../apache/nifi/web/api/AccessResource.java | 170 ++++++++--------- .../nifi/web/api/ControllerResource.java | 50 ----- .../org/apache/nifi/web/api/FlowResource.java | 171 ++++++++++++------ .../config/AccessDeniedExceptionMapper.java | 28 ++- .../main/resources/nifi-web-api-context.xml | 7 +- .../AccessTokenEndpointTest.java | 29 +-- .../nifi/integration/util/NiFiTestUser.java | 32 +--- .../anonymous/NiFiAnonymousUserFilter.java | 8 - .../src/main/webapp/WEB-INF/pages/login.jsp | 3 +- .../WEB-INF/partials/canvas/canvas-header.jsp | 4 +- .../partials/login/nifi-registration-form.jsp | 38 ---- .../webapp/WEB-INF/partials/message-pane.jsp | 2 +- .../src/main/webapp/css/header.css | 1 + .../nifi-web-ui/src/main/webapp/css/login.css | 16 -- .../src/main/webapp/js/nf/canvas/nf-canvas.js | 10 +- .../main/webapp/js/nf/cluster/nf-cluster.js | 2 +- .../main/webapp/js/nf/counters/nf-counters.js | 2 +- .../main/webapp/js/nf/history/nf-history.js | 2 +- .../src/main/webapp/js/nf/login/nf-login.js | 114 ++---------- .../webapp/js/nf/provenance/nf-provenance.js | 2 +- .../webapp/js/nf/templates/nf-templates.js | 2 +- 24 files changed, 270 insertions(+), 452 deletions(-) delete mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/nifi-registration-form.jsp diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java index d9719b3f80..3c15912220 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessConfigurationDTO.java @@ -26,7 +26,6 @@ import javax.xml.bind.annotation.XmlType; public class AccessConfigurationDTO { private Boolean supportsLogin; - private Boolean supportsAnonymous; /** * @return Indicates whether or not this NiFi supports user login. @@ -43,19 +42,4 @@ public class AccessConfigurationDTO { this.supportsLogin = supportsLogin; } - /** - * @return Indicates whether or not this NiFi supports anonymous access. - */ - @ApiModelProperty( - value = "Indicates whether or not this NiFi supports anonymous.", - readOnly = true - ) - public Boolean getSupportsAnonymous() { - return supportsAnonymous; - } - - public void setSupportsAnonymous(Boolean supportsAnonymous) { - this.supportsAnonymous = supportsAnonymous; - } - } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java index 712da0ecb1..5962cc2a93 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/AccessStatusDTO.java @@ -28,8 +28,6 @@ public class AccessStatusDTO { public static enum Status { UNKNOWN, - UNREGISTERED, - NOT_ACTIVE, ACTIVE } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java index 02991c76e1..ee38efef8c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/IdentityEntity.java @@ -26,6 +26,7 @@ public class IdentityEntity extends Entity { private String userId; private String identity; + private boolean anonymous; /** * @return current user id @@ -49,4 +50,14 @@ public class IdentityEntity extends Entity { this.identity = identity; } + /** + * @return if the user is anonymous + */ + public boolean isAnonymous() { + return anonymous; + } + + public void setAnonymous(boolean anonymous) { + this.anonymous = anonymous; + } } 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 d03e07da04..06489c11f2 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 @@ -29,9 +29,15 @@ import org.apache.nifi.authentication.LoginIdentityProvider; import org.apache.nifi.authentication.exception.IdentityAccessException; import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException; import org.apache.nifi.authorization.AccessDeniedException; +import org.apache.nifi.authorization.AuthorizationRequest; +import org.apache.nifi.authorization.AuthorizationResult; +import org.apache.nifi.authorization.AuthorizationResult.Result; +import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.resource.ResourceFactory; import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.user.NiFiUserDetails; import org.apache.nifi.authorization.user.NiFiUserUtils; -import org.apache.nifi.security.util.CertificateUtils; import org.apache.nifi.util.FormatUtils; import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.web.api.dto.AccessConfigurationDTO; @@ -42,20 +48,23 @@ import org.apache.nifi.web.security.InvalidAuthenticationException; import org.apache.nifi.web.security.ProxiedEntitiesUtils; import org.apache.nifi.web.security.UntrustedProxyException; import org.apache.nifi.web.security.jwt.JwtAuthenticationFilter; +import org.apache.nifi.web.security.jwt.JwtAuthenticationProvider; +import org.apache.nifi.web.security.jwt.JwtAuthenticationRequestToken; import org.apache.nifi.web.security.jwt.JwtService; import org.apache.nifi.web.security.kerberos.KerberosService; import org.apache.nifi.web.security.otp.OtpService; import org.apache.nifi.web.security.token.LoginAuthenticationToken; +import org.apache.nifi.web.security.token.NiFiAuthenticationToken; import org.apache.nifi.web.security.token.OtpAuthenticationToken; +import org.apache.nifi.web.security.x509.X509AuthenticationProvider; +import org.apache.nifi.web.security.x509.X509AuthenticationRequestToken; import org.apache.nifi.web.security.x509.X509CertificateExtractor; -import org.apache.nifi.web.security.x509.X509IdentityProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.AccountStatusException; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; @@ -69,8 +78,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -87,14 +94,35 @@ public class AccessResource extends ApplicationResource { private NiFiProperties properties; - private LoginIdentityProvider loginIdentityProvider; private X509CertificateExtractor certificateExtractor; - private X509IdentityProvider certificateIdentityProvider; + private X509AuthenticationProvider x509AuthenticationProvider; + private X509PrincipalExtractor principalExtractor; + + private LoginIdentityProvider loginIdentityProvider; + private JwtAuthenticationProvider jwtAuthenticationProvider; private JwtService jwtService; private OtpService otpService; private KerberosService kerberosService; + private Authorizer authorizer; + + /** + * Authorizes access to the flow. + */ + private boolean hasFlowAccess(final NiFiUser user) { + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(ResourceFactory.getFlowResource()) + .identity(user.getIdentity()) + .anonymous(user.isAnonymous()) + .accessAttempt(true) + .action(RequestAction.READ) + .build(); + + final AuthorizationResult result = authorizer.authorize(request); + return Result.Approved.equals(result.getResult()); + } + /** * Retrieves the access configuration for this NiFi. * @@ -103,8 +131,8 @@ public class AccessResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) - @Path("/config") + @Produces(MediaType.APPLICATION_JSON) + @Path("config") @ApiOperation( value = "Retrieves the access configuration for this NiFi", response = AccessConfigurationEntity.class @@ -115,7 +143,6 @@ public class AccessResource extends ApplicationResource { // specify whether login should be supported and only support for secure requests accessConfiguration.setSupportsLogin(loginIdentityProvider != null && httpServletRequest.isSecure()); - accessConfiguration.setSupportsAnonymous(false); // create the response entity final AccessConfigurationEntity entity = new AccessConfigurationEntity(); @@ -133,7 +160,7 @@ public class AccessResource extends ApplicationResource { */ @GET @Consumes(MediaType.WILDCARD) - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + @Produces(MediaType.APPLICATION_JSON) @Path("") @ApiOperation( value = "Gets the status the client's access", @@ -173,56 +200,41 @@ public class AccessResource extends ApplicationResource { try { // Extract the Base64 encoded token from the Authorization header final String token = StringUtils.substringAfterLast(authorization, " "); - final String principal = jwtService.getAuthenticationFromToken(token); + + final JwtAuthenticationRequestToken jwtRequest = new JwtAuthenticationRequestToken(token); + final NiFiAuthenticationToken authenticationResponse = (NiFiAuthenticationToken) jwtAuthenticationProvider.authenticate(jwtRequest); + final NiFiUser nifiUser = ((NiFiUserDetails) authenticationResponse.getDetails()).getNiFiUser(); // set the user identity - accessStatus.setIdentity(principal); - accessStatus.setUsername(CertificateUtils.extractUsername(principal)); + accessStatus.setIdentity(nifiUser.getIdentity()); + accessStatus.setUsername(nifiUser.getUserName()); - // without a certificate, this is not a proxied request - final List chain = Arrays.asList(principal); - - // TODO - ensure the proxy chain is authorized -// final UserDetails userDetails = checkAuthorization(chain); - - // no issues with authorization... verify authorities + // attempt authorize to /flow accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); - accessStatus.setMessage("Your account is active and you are already logged in."); + accessStatus.setMessage("You are already logged in."); } catch (JwtException e) { throw new InvalidAuthenticationException(e.getMessage(), e); } } } else { try { - final AuthenticationResponse authenticationResponse = certificateIdentityProvider.authenticate(certificates); + final X509AuthenticationRequestToken x509Request = new X509AuthenticationRequestToken( + httpServletRequest.getHeader(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN), principalExtractor, certificates); - // get the proxy chain and ensure its populated - final List 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."); - } + final NiFiAuthenticationToken authenticationResponse = (NiFiAuthenticationToken) x509AuthenticationProvider.authenticate(x509Request); + final NiFiUser nifiUser = ((NiFiUserDetails) authenticationResponse.getDetails()).getNiFiUser(); // set the user identity - accessStatus.setIdentity(proxyChain.get(0)); - accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0))); + accessStatus.setIdentity(nifiUser.getIdentity()); + accessStatus.setUsername(nifiUser.getUserName()); - // TODO - ensure the proxy chain is authorized -// final UserDetails userDetails = checkAuthorization(proxyChain); - - // no issues with authorization... verify authorities + // attempt authorize to /flow accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name()); - accessStatus.setMessage("Your account is active and you are already logged in."); + accessStatus.setMessage("You are already logged in."); } catch (final IllegalArgumentException iae) { throw new InvalidAuthenticationException(iae.getMessage(), iae); } } - } catch (final UsernameNotFoundException unfe) { - accessStatus.setStatus(AccessStatusDTO.Status.NOT_ACTIVE.name()); - accessStatus.setMessage("This NiFi does not support new account requests."); - } catch (final AccountStatusException ase) { - accessStatus.setStatus(AccessStatusDTO.Status.NOT_ACTIVE.name()); - accessStatus.setMessage(ase.getMessage()); } catch (final UntrustedProxyException upe) { throw new AccessDeniedException(upe.getMessage(), upe); } catch (final AuthenticationServiceException ase) { @@ -354,8 +366,7 @@ public class AccessResource extends ApplicationResource { @ApiResponse(code = 500, message = "Unable to create access token because an unexpected error occurred.") } ) - public Response createAccessTokenFromTicket( - @Context HttpServletRequest httpServletRequest) { + public Response createAccessTokenFromTicket(@Context HttpServletRequest httpServletRequest) { // only support access tokens when communicating over HTTPS if (!httpServletRequest.isSecure()) { @@ -389,7 +400,6 @@ public class AccessResource extends ApplicationResource { // create the authentication token final LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(identity, expiration, "KerberosService"); - // generate JWT for response final String token = jwtService.generateSignedToken(loginAuthenticationToken); @@ -446,43 +456,22 @@ public class AccessResource extends ApplicationResource { final LoginAuthenticationToken loginAuthenticationToken; - final X509Certificate[] certificates = certificateExtractor.extractClientCertificate(httpServletRequest); + // ensure we have login credentials + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + throw new IllegalArgumentException("The username and password must be specified."); + } - // 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."); - } - - try { - // attempt to authenticate - final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password)); - long expiration = validateTokenExpiration(authenticationResponse.getExpiration(), authenticationResponse.getIdentity()); - - // create the authentication token - loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, authenticationResponse.getIssuer()); - } 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 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."); - } - - // TODO - authorize the proxy if necessary -// authorizeProxyIfNecessary(proxyChain); + try { + // attempt to authenticate + final AuthenticationResponse authenticationResponse = loginIdentityProvider.authenticate(new LoginCredentials(username, password)); + long expiration = validateTokenExpiration(authenticationResponse.getExpiration(), authenticationResponse.getIdentity()); // create the authentication token - loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration(), authenticationResponse.getIssuer()); + loginAuthenticationToken = new LoginAuthenticationToken(authenticationResponse.getIdentity(), expiration, authenticationResponse.getIssuer()); + } 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); } // generate JWT for response @@ -515,6 +504,10 @@ public class AccessResource extends ApplicationResource { this.properties = properties; } + public void setAuthorizer(Authorizer authorizer) { + this.authorizer = authorizer; + } + public void setLoginIdentityProvider(LoginIdentityProvider loginIdentityProvider) { this.loginIdentityProvider = loginIdentityProvider; } @@ -523,19 +516,28 @@ public class AccessResource extends ApplicationResource { this.jwtService = jwtService; } + public void setJwtAuthenticationProvider(JwtAuthenticationProvider jwtAuthenticationProvider) { + this.jwtAuthenticationProvider = jwtAuthenticationProvider; + } + public void setKerberosService(KerberosService kerberosService) { this.kerberosService = kerberosService; } - public void setOtpService(OtpService otpService) { - this.otpService = otpService; + public void setX509AuthenticationProvider(X509AuthenticationProvider x509AuthenticationProvider) { + this.x509AuthenticationProvider = x509AuthenticationProvider; + } + + public void setPrincipalExtractor(X509PrincipalExtractor principalExtractor) { + this.principalExtractor = principalExtractor; } public void setCertificateExtractor(X509CertificateExtractor certificateExtractor) { this.certificateExtractor = certificateExtractor; } - public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) { - this.certificateIdentityProvider = certificateIdentityProvider; + public void setOtpService(OtpService otpService) { + this.otpService = otpService; } + } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java index 59484a0f7e..3488dcbb2d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java @@ -45,7 +45,6 @@ import org.apache.nifi.web.NiFiServiceFacade; import org.apache.nifi.web.Revision; import org.apache.nifi.web.api.dto.CounterDTO; import org.apache.nifi.web.api.dto.CountersDTO; -import org.apache.nifi.web.api.entity.AuthorityEntity; import org.apache.nifi.web.api.entity.ControllerConfigurationEntity; import org.apache.nifi.web.api.entity.ControllerServiceEntity; import org.apache.nifi.web.api.entity.CounterEntity; @@ -65,12 +64,10 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; -import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -410,53 +407,6 @@ public class ControllerResource extends ApplicationResource { ); } - /**x - * Retrieves the user details, including the authorities, about the user making the request. - * - * @return A authoritiesEntity. - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("authorities") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN', 'ROLE_PROXY', 'ROLE_NIFI', 'ROLE_PROVENANCE')") - @ApiOperation( - value = "Retrieves the user details, including the authorities, about the user making the request", - response = AuthorityEntity.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN"), - @Authorization(value = "Proxy", type = "ROLE_PROXY"), - @Authorization(value = "NiFi", type = "ROLE_NIFI"), - @Authorization(value = "Provenance", type = "ROLE_PROVENANCE") - } - ) - @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 = 401, message = "Client could not be authenticated."), - @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.") - } - ) - public Response getAuthorities() { - - // note that the cluster manager will handle this request directly - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - if (user == null) { - throw new WebApplicationException(new Throwable("Unable to access details for current user.")); - } - - // create the response entity - AuthorityEntity entity = new AuthorityEntity(); - entity.setUserId(user.getIdentity()); - entity.setAuthorities(new HashSet<>(Arrays.asList("ROLE_MONITOR", "ROLE_DFM", "ROLE_ADMIN", "ROLE_PROXY", "ROLE_NIFI", "ROLE_PROVENANCE"))); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - // --------------- // reporting tasks // --------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java index a181a848b0..35e57ccf82 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java @@ -63,6 +63,7 @@ import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO; import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO; import org.apache.nifi.web.api.dto.status.StatusHistoryDTO; import org.apache.nifi.web.api.entity.AboutEntity; +import org.apache.nifi.web.api.entity.AuthorityEntity; import org.apache.nifi.web.api.entity.BannerEntity; import org.apache.nifi.web.api.entity.BulletinBoardEntity; import org.apache.nifi.web.api.entity.ConnectionStatusEntity; @@ -105,6 +106,7 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -213,6 +215,117 @@ public class FlowResource extends ApplicationResource { // flow // ---- + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.TEXT_PLAIN) + @Path("client-id") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") + @ApiOperation( + value = "Generates a client id.", + response = String.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN") + } + ) + @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 = 401, message = "Client could not be authenticated."), + @ApiResponse(code = 403, message = "Client is not authorized to make this request."), + @ApiResponse(code = 404, message = "The specified resource could not be found."), + @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.") + } + ) + public Response generateClientId() { + authorizeFlow(); + return clusterContext(generateOkResponse(generateUuid())).build(); + } + + /** + * Retrieves the identity of the user making the request. + * + * @return An identityEntity + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("identity") + @ApiOperation( + value = "Retrieves the user identity of the user making the request", + response = IdentityEntity.class + ) + public Response getIdentity() { + + authorizeFlow(); + + // note that the cluster manager will handle this request directly + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + if (user == null) { + throw new WebApplicationException(new Throwable("Unable to access details for current user.")); + } + + // create the response entity + IdentityEntity entity = new IdentityEntity(); + entity.setUserId(user.getIdentity()); + entity.setIdentity(user.getUserName()); + entity.setAnonymous(user.isAnonymous()); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + + /**x + * Retrieves the user details, including the authorities, about the user making the request. + * + * @return A authoritiesEntity. + */ + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @Path("authorities") + // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN', 'ROLE_PROXY', 'ROLE_NIFI', 'ROLE_PROVENANCE')") + @ApiOperation( + value = "Retrieves the user details, including the authorities, about the user making the request", + response = AuthorityEntity.class, + authorizations = { + @Authorization(value = "Read Only", type = "ROLE_MONITOR"), + @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), + @Authorization(value = "Administrator", type = "ROLE_ADMIN"), + @Authorization(value = "Proxy", type = "ROLE_PROXY"), + @Authorization(value = "NiFi", type = "ROLE_NIFI"), + @Authorization(value = "Provenance", type = "ROLE_PROVENANCE") + } + ) + @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 = 401, message = "Client could not be authenticated."), + @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.") + } + ) + public Response getAuthorities() { + + // TODO - Remove + authorizeFlow(); + + // note that the cluster manager will handle this request directly + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + if (user == null) { + throw new WebApplicationException(new Throwable("Unable to access details for current user.")); + } + + // create the response entity + AuthorityEntity entity = new AuthorityEntity(); + entity.setUserId(user.getIdentity()); + entity.setAuthorities(new HashSet<>(Arrays.asList("ROLE_MONITOR", "ROLE_DFM", "ROLE_ADMIN", "ROLE_PROXY", "ROLE_NIFI", "ROLE_PROVENANCE"))); + + // generate the response + return clusterContext(generateOkResponse(entity)).build(); + } + /** * Retrieves the contents of the specified group. * @@ -570,34 +683,6 @@ public class FlowResource extends ApplicationResource { ); } - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.TEXT_PLAIN) - @Path("client-id") - // TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')") - @ApiOperation( - value = "Generates a client id.", - response = String.class, - authorizations = { - @Authorization(value = "Read Only", type = "ROLE_MONITOR"), - @Authorization(value = "Data Flow Manager", type = "ROLE_DFM"), - @Authorization(value = "Administrator", type = "ROLE_ADMIN") - } - ) - @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 = 401, message = "Client could not be authenticated."), - @ApiResponse(code = 403, message = "Client is not authorized to make this request."), - @ApiResponse(code = 404, message = "The specified resource could not be found."), - @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.") - } - ) - public Response generateClientId() { - authorizeFlow(); - return clusterContext(generateOkResponse(generateUuid())).build(); - } - // ------ // search // ------ @@ -694,36 +779,6 @@ public class FlowResource extends ApplicationResource { return clusterContext(generateOkResponse(entity)).build(); } - /** - * Retrieves the identity of the user making the request. - * - * @return An identityEntity - */ - @GET - @Consumes(MediaType.WILDCARD) - @Produces(MediaType.APPLICATION_JSON) - @Path("identity") - @ApiOperation( - value = "Retrieves the user identity of the user making the request", - response = IdentityEntity.class - ) - public Response getIdentity() { - - // note that the cluster manager will handle this request directly - final NiFiUser user = NiFiUserUtils.getNiFiUser(); - if (user == null) { - throw new WebApplicationException(new Throwable("Unable to access details for current user.")); - } - - // create the response entity - IdentityEntity entity = new IdentityEntity(); - entity.setUserId(user.getIdentity()); - entity.setIdentity(user.getUserName()); - - // generate the response - return clusterContext(generateOkResponse(entity)).build(); - } - /** * Retrieves the banners for this NiFi. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccessDeniedExceptionMapper.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccessDeniedExceptionMapper.java index 3af22c9d67..93d1bab991 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccessDeniedExceptionMapper.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccessDeniedExceptionMapper.java @@ -16,17 +16,18 @@ */ package org.apache.nifi.web.api.config; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - +import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.AccessDeniedException; import org.apache.nifi.authorization.user.NiFiUser; -import org.apache.commons.lang3.StringUtils; import org.apache.nifi.authorization.user.NiFiUserUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + /** * Maps access denied exceptions into a client response. */ @@ -39,17 +40,26 @@ public class AccessDeniedExceptionMapper implements ExceptionMapper + @@ -305,9 +306,11 @@ - - + + + + 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 index e43747d7c9..0302153470 100644 --- 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 @@ -18,10 +18,6 @@ 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; @@ -44,6 +40,11 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; +import javax.net.ssl.SSLContext; +import java.io.File; +import java.util.HashMap; +import java.util.Map; + /** * Access token endpoint test. */ @@ -122,7 +123,6 @@ public class AccessTokenEndpointTest { // verify config Assert.assertTrue(accessConfig.getSupportsLogin()); - Assert.assertFalse(accessConfig.getSupportsAnonymous()); } /** @@ -259,24 +259,7 @@ public class AccessTokenEndpointTest { 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()); + Assert.assertEquals("ACTIVE", accessStatus.getStatus()); } @AfterClass 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 621dc092a1..80b3384a6f 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 @@ -20,10 +20,11 @@ import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.core.util.MultivaluedMapImpl; -import java.util.Map; -import javax.ws.rs.core.MediaType; import org.apache.nifi.web.security.ProxiedEntitiesUtils; +import javax.ws.rs.core.MediaType; +import java.util.Map; + /** * */ @@ -419,31 +420,4 @@ public class NiFiTestUser { 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 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); - } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java index 6c14ca5336..b1a9a6658f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/anonymous/NiFiAnonymousUserFilter.java @@ -19,20 +19,13 @@ package org.apache.nifi.web.security.anonymous; import org.apache.nifi.authorization.user.NiFiUser; import org.apache.nifi.authorization.user.NiFiUserDetails; import org.apache.nifi.web.security.token.NiFiAuthenticationToken; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import javax.servlet.http.HttpServletRequest; -/** - * Custom AnonymousAuthenticationFilter used to grant additional authorities depending on the current operating mode. - */ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter { - private static final Logger anonymousUserFilterLogger = LoggerFactory.getLogger(NiFiAnonymousUserFilter.class); - private static final String ANONYMOUS_KEY = "anonymousNifiKey"; public NiFiAnonymousUserFilter() { @@ -41,7 +34,6 @@ public class NiFiAnonymousUserFilter extends AnonymousAuthenticationFilter { @Override protected Authentication createAuthentication(HttpServletRequest request) { - // TODO - conditional anonymous authentication return new NiFiAuthenticationToken(new NiFiUserDetails(NiFiUser.ANONYMOUS)); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp index 9f876e1277..06a7e91eb3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp @@ -40,7 +40,7 @@ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/nifi-registration-form.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/nifi-registration-form.jsp deleted file mode 100644 index 3806bd5b0f..0000000000 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/login/nifi-registration-form.jsp +++ /dev/null @@ -1,38 +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. ---%> -<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> - \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp index 4fdc9c6192..0b12baa228 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/message-pane.jsp @@ -19,7 +19,7 @@