NIFI-1928:

- Fixing UI action for bulk delete.
- Removing registration form.
- Fixing default visibility of the anonymous user warning.
This commit is contained in:
Matt Gilman 2016-06-01 08:09:04 -04:00
parent 950e0cfa58
commit f0f74fe404
24 changed files with 270 additions and 452 deletions

View File

@ -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;
}
}

View File

@ -28,8 +28,6 @@ public class AccessStatusDTO {
public static enum Status {
UNKNOWN,
UNREGISTERED,
NOT_ACTIVE,
ACTIVE
}

View File

@ -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;
}
}

View File

@ -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<String> 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<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.");
}
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<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.");
}
// 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;
}
}

View File

@ -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
// ---------------

View File

@ -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.
*

View File

@ -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<AccessDenied
public Response toResponse(AccessDeniedException exception) {
// get the current user
NiFiUser user = NiFiUserUtils.getNiFiUser();
if (user != null) {
logger.info(String.format("%s does not have permission to access the requested resource. Returning %s response.", user.getIdentity(), Response.Status.FORBIDDEN));
// if the user was authenticated - forbidden, otherwise unauthorized
final Response.Status status;
if (user.isAnonymous()) {
status = Status.UNAUTHORIZED;
} else {
logger.info(String.format("User does not have permission to access the requested resource. Returning %s response.", Response.Status.FORBIDDEN));
status = Status.FORBIDDEN;
}
if (user != null) {
logger.info(String.format("%s does not have permission to access the requested resource. Returning %s response.", user.getIdentity(), status));
} else {
logger.info(String.format("User does not have permission to access the requested resource. Returning %s response.", status));
}
if (logger.isDebugEnabled()) {
logger.debug(StringUtils.EMPTY, exception);
}
return Response.status(Response.Status.FORBIDDEN).entity("Unable to perform the desired action because access is denied.").type("text/plain").build();
return Response.status(status).entity("Unable to perform the desired action. Contact the system administrator.").type("text/plain").build();
}
}

View File

@ -292,6 +292,7 @@
<property name="serviceFacade" ref="serviceFacade"/>
<property name="properties" ref="nifiProperties"/>
<property name="clusterManager" ref="clusterManager"/>
<property name="authorizer" ref="authorizer"/>
</bean>
<bean id="clusterResource" class="org.apache.nifi.web.api.ClusterResource" scope="singleton">
<property name="serviceFacade" ref="serviceFacade"/>
@ -305,9 +306,11 @@
</bean>
<bean id="accessResource" class="org.apache.nifi.web.api.AccessResource" scope="singleton">
<property name="properties" ref="nifiProperties"/>
<property name="certificateExtractor" ref="certificateExtractor"/>
<property name="certificateIdentityProvider" ref="certificateIdentityProvider"/>
<property name="loginIdentityProvider" ref="loginIdentityProvider"/>
<property name="x509AuthenticationProvider" ref="x509AuthenticationProvider"/>
<property name="certificateExtractor" ref="certificateExtractor"/>
<property name="principalExtractor" ref="principalExtractor"/>
<property name="jwtAuthenticationProvider" ref="jwtAuthenticationProvider"/>
<property name="jwtService" ref="jwtService"/>
<property name="otpService" ref="otpService"/>
<property name="kerberosService" ref="kerberosService"/>

View File

@ -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

View File

@ -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<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);
}
}

View File

@ -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));
}

View File

@ -40,7 +40,7 @@
<div id="login-user-links-container">
<ul id="login-user-links" class="links">
<li id="user-logout-container" style="display: none;">
<span id="user-logout" class="link">logout</span>
<span id="user-logout" class="link">log out</span>
</li>
<li>
<span id="user-home" class="link">home</span>
@ -51,7 +51,6 @@
<div id="login-contents-container">
<jsp:include page="/WEB-INF/partials/login/login-message.jsp"/>
<jsp:include page="/WEB-INF/partials/login/login-form.jsp"/>
<jsp:include page="/WEB-INF/partials/login/nifi-registration-form.jsp"/>
<jsp:include page="/WEB-INF/partials/login/login-submission.jsp"/>
<jsp:include page="/WEB-INF/partials/login/login-progress.jsp"/>
</div>

View File

@ -69,11 +69,11 @@
</div>
<div id="login-link-container">
<span id="login-link" class="link"
ng-click="appCtrl.serviceProvider.headerCtrl.loginCtrl.shell.launch();">login</span>
ng-click="appCtrl.serviceProvider.headerCtrl.loginCtrl.shell.launch();">log in</span>
</div>
<div id="logout-link-container" style="display: none;">
<span id="logout-link" class="link"
ng-click="appCtrl.serviceProvider.headerCtrl.logoutCtrl.logout();">logout</span>
ng-click="appCtrl.serviceProvider.headerCtrl.logoutCtrl.logout();">log out</span>
</div>
</div>
<md-menu md-position-mode="target-right target" md-offset="-1 44">

View File

@ -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" %>
<div id="nifi-registration-container" class="login-container hidden">
<div id="nifi-registration-title" class="login-title nifi-submit-justification">Submit Justification</div>
<div id="nifi-user-submit-justification-container" class="nifi-submit-justification">
<div class="setting">
<div class="setting-name">User</div>
<div class="setting-field">
<div id="nifi-user-submit-justification"></div>
</div>
</div>
</div>
<div class="setting">
<div class="setting-name">Justification</div>
<div class="setting-field">
<textarea cols="30" rows="4" id="nifi-registration-justification" maxlength="500" class="setting-input"></textarea>
</div>
<div style="text-align: right; color: #666; margin-top: 2px; float: right;">
<span id="remaining-characters"></span>&nbsp;characters remaining
</div>
<div class="clear"></div>
</div>
</div>

View File

@ -19,7 +19,7 @@
<div id="user-links-container">
<ul class="links">
<li id="user-logout-container" style="display: none;">
<span id="user-logout" class="link">logout</span>
<span id="user-logout" class="link">log out</span>
</li>
<li>
<span id="user-home" class="link">home</span>

View File

@ -152,6 +152,7 @@ md-toolbar.md-small .md-toolbar-tools {
/* styles for the status link */
#anonymous-user-alert {
display: none;
font-size: 12px;
}

View File

@ -75,22 +75,6 @@ div.login-container {
width: 412px;
}
/*
NiFi Registration
*/
#nifi-user-submit-justification-container {
margin-bottom: 10px;
}
#nifi-user-submit-justification {
font-weight: bold;
}
#nifi-registration-justification {
height: 200px;
}
/*
Login Progress
*/

View File

@ -124,7 +124,7 @@ nf.Canvas = (function () {
urls: {
api: '../nifi-api',
identity: '../nifi-api/flow/identity',
authorities: '../nifi-api/controller/authorities',
authorities: '../nifi-api/flow/authorities',
kerberos: '../nifi-api/access/kerberos',
revision: '../nifi-api/flow/revision',
banners: '../nifi-api/flow/banners',
@ -566,9 +566,9 @@ nf.Canvas = (function () {
}
}
} else {
if (evt.keyCode == 8 || evt.keyCode === 46) {
if (evt.keyCode === 8 || evt.keyCode === 46) {
// backspace or delete
if (nf.Common.isDFM() && nf.CanvasUtils.isDeletable(selection)) {
if (nf.Common.isDFM() && nf.CanvasUtils.areDeletable(selection)) {
nf.Actions['delete'](selection);
}
@ -761,7 +761,7 @@ nf.Canvas = (function () {
// at this point the user may be themselves or anonymous
// if the user is logged, we want to determine if they were logged in using a certificate
if (identityResponse.identity !== 'anonymous') {
if (identityResponse.anonymous === false) {
// rendner the users name
$('#current-user').text(identityResponse.identity).show();
@ -776,7 +776,7 @@ nf.Canvas = (function () {
deferred.resolve();
}).fail(function (xhr, status, error) {
// there is no anonymous access and we don't know this user - open the login page which handles login/registration/etc
if (xhr.status === 401 || xhr.status === 403) {
if (xhr.status === 401) {
window.location = '/nifi/login';
} else {
deferred.reject(xhr, status, error);

View File

@ -31,7 +31,7 @@ nf.Cluster = (function () {
urls: {
banners: '../nifi-api/flow/banners',
about: '../nifi-api/flow/about',
authorities: '../nifi-api/controller/authorities'
authorities: '../nifi-api/flow/authorities'
}
};

View File

@ -31,7 +31,7 @@ nf.Counters = (function () {
urls: {
banners: '../nifi-api/flow/banners',
about: '../nifi-api/flow/about',
authorities: '../nifi-api/controller/authorities'
authorities: '../nifi-api/flow/authorities'
}
};

View File

@ -31,7 +31,7 @@ nf.History = (function () {
urls: {
banners: '../nifi-api/flow/banners',
about: '../nifi-api/flow/about',
authorities: '../nifi-api/controller/authorities'
authorities: '../nifi-api/flow/authorities'
}
};

View File

@ -23,8 +23,6 @@ $(document).ready(function () {
nf.Login = (function () {
var supportsAnonymous = false;
var config = {
urls: {
identity: '../nifi-api/flow/identity',
@ -53,33 +51,10 @@ nf.Login = (function () {
$('#username').focus();
};
var initializeNiFiRegistration = function () {
$('#nifi-registration-justification').count({
charCountField: '#remaining-characters'
});
// toggle between signup and login
$('#login-to-account-link').on('click', function () {
showLogin();
});
};
var showNiFiRegistration = function () {
// reset the forms
$('#login-submission-button').text('Submit');
$('#nifi-registration-justification').val('');
// update the form visibility
$('#login-container').hide();
$('#nifi-registration-container').show();
};
var initializeSubmission = function () {
$('#login-submission-button').on('click', function () {
if ($('#login-container').is(':visible')) {
login();
} else if ($('#nifi-registration-container').is(':visible')) {
submitJustification();
}
});
@ -121,7 +96,14 @@ nf.Login = (function () {
showLogoutLink();
// update according to the access status
if (accessStatus.status === 'UNKNOWN' || accessStatus.status === 'NOT_ACTIVE') {
if (accessStatus.status === 'ACTIVE') {
// reload as appropriate - no need to schedule token refresh as the page is reloading
if (top !== window) {
parent.window.location = '/nifi';
} else {
window.location = '/nifi';
}
} else {
$('#login-message-title').text('Unable to log in');
$('#login-message').text(accessStatus.message);
@ -130,27 +112,6 @@ nf.Login = (function () {
$('#login-submission-container').hide();
$('#login-progress-container').hide();
$('#login-message-container').show();
} else if (accessStatus.status === 'UNREGISTERED') {
// schedule automatic token refresh
nf.Common.scheduleTokenRefresh();
// show the user
$('#nifi-user-submit-justification').text(token['preferred_username']);
// show the registration form
initializeNiFiRegistration();
showNiFiRegistration();
// update the form visibility
$('#login-submission-container').show();
$('#login-progress-container').hide();
} else if (accessStatus.status === 'ACTIVE') {
// reload as appropriate - no need to schedule token refresh as the page is reloading
if (top !== window) {
parent.window.location = '/nifi';
} else {
window.location = '/nifi';
}
}
}).fail(function (xhr, status, error) {
$('#login-message-title').text('Unable to log in');
@ -174,39 +135,6 @@ nf.Login = (function () {
});
};
var submitJustification = function () {
// show the logging message...
$('#login-progress-label').text('Submitting...');
$('#login-progress-container').show();
$('#login-submission-container').hide();
// attempt to create the nifi account registration
$.ajax({
type: 'POST',
url: config.urls.users,
data: {
'justification': $('#nifi-registration-justification').val()
}
}).done(function (response) {
var markup = 'An administrator will process your request shortly.';
if (supportsAnonymous === true) {
markup += '<br/><br/>In the meantime you can continue accessing anonymously.';
}
$('#login-message-title').text('Thanks!');
$('#login-message').html(markup);
}).fail(function (xhr, status, error) {
$('#login-message-title').text('Unable to submit justification');
$('#login-message').text(xhr.responseText);
}).always(function () {
// update form visibility
$('#nifi-registration-container').hide();
$('#login-submission-container').hide();
$('#login-progress-container').hide();
$('#login-message-container').show();
});
};
var showLogoutLink = function () {
nf.Common.showLogoutLink();
};
@ -247,36 +175,24 @@ nf.Login = (function () {
url: config.urls.accessConfig,
dataType: 'json'
});
$.when(accessStatus, accessConfigXhr).done(function (accessStatusResult, accessConfigResult) {
var accessStatusResponse = accessStatusResult[0];
var accessStatus = accessStatusResponse.accessStatus;
var accessConfigResponse = accessConfigResult[0];
var accessConfig = accessConfigResponse.config;
// record whether this NiFi supports anonymous access
supportsAnonymous = accessConfig.supportsAnonymous;
// possible login states
var needsLogin = false;
var needsNiFiRegistration = false;
var needsLogin = true;
var showMessage = false;
// handle the status appropriately
if (accessStatus.status === 'UNKNOWN') {
needsLogin = true;
} else if (accessStatus.status === 'UNREGISTERED') {
needsNiFiRegistration = true;
$('#nifi-user-submit-justification').text(accessStatus.username);
} else if (accessStatus.status === 'NOT_ACTIVE') {
showMessage = true;
$('#login-message-title').text('Unable to log in');
$('#login-message').text(accessStatus.message);
} else if (accessStatus.status === 'ACTIVE') {
showMessage = true;
needsLogin = false;
$('#login-message-title').text('Success');
$('#login-message').text(accessStatus.message);
@ -295,12 +211,6 @@ nf.Login = (function () {
initializeMessage();
} else if (needsLogin === true) {
showLogin();
} else if (needsNiFiRegistration === true) {
initializeNiFiRegistration();
showNiFiRegistration();
}
if (needsLogin === true || needsNiFiRegistration === true) {
initializeSubmission();
}
});

View File

@ -32,7 +32,7 @@ nf.Provenance = (function () {
cluster: '../nifi-api/cluster',
banners: '../nifi-api/flow/banners',
about: '../nifi-api/flow/about',
authorities: '../nifi-api/controller/authorities'
authorities: '../nifi-api/flow/authorities'
}
};

View File

@ -31,7 +31,7 @@ nf.Templates = (function () {
urls: {
banners: '../nifi-api/flow/banners',
about: '../nifi-api/flow/about',
authorities: '../nifi-api/controller/authorities'
authorities: '../nifi-api/flow/authorities'
}
};