You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2016/04/07 22:23:31 UTC
[3/9] nifi git commit: Revert "NIFI-1551:"
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/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 9742011..5ec8d01 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
@@ -47,6 +47,7 @@ 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.NiFiAuthorizationRequestToken;
import org.apache.nifi.web.security.token.OtpAuthenticationToken;
import org.apache.nifi.web.security.user.NiFiUserUtils;
import org.apache.nifi.web.security.x509.X509CertificateExtractor;
@@ -58,6 +59,8 @@ 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.AuthenticationUserDetailsService;
+import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.servlet.http.HttpServletRequest;
@@ -100,6 +103,8 @@ public class AccessResource extends ApplicationResource {
private KerberosService kerberosService;
+ private AuthenticationUserDetailsService<NiFiAuthorizationRequestToken> userDetailsService;
+
/**
* Retrieves the access configuration for this NiFi.
*
@@ -206,12 +211,16 @@ public class AccessResource extends ApplicationResource {
// 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);
+ // ensure the proxy chain is authorized
+ final UserDetails userDetails = checkAuthorization(chain);
// no issues with authorization... verify authorities
accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
- accessStatus.setMessage("Your account is active and you are already logged in.");
+ if (userDetails.getAuthorities().isEmpty()) {
+ accessStatus.setMessage("Your account is active but currently does not have any level of access.");
+ } else {
+ accessStatus.setMessage("Your account is active and you are already logged in.");
+ }
} catch (JwtException e) {
throw new InvalidAuthenticationException(e.getMessage(), e);
}
@@ -231,12 +240,16 @@ public class AccessResource extends ApplicationResource {
accessStatus.setIdentity(proxyChain.get(0));
accessStatus.setUsername(CertificateUtils.extractUsername(proxyChain.get(0)));
- // TODO - ensure the proxy chain is authorized
-// final UserDetails userDetails = checkAuthorization(proxyChain);
+ // ensure the proxy chain is authorized
+ final UserDetails userDetails = checkAuthorization(proxyChain);
// no issues with authorization... verify authorities
accessStatus.setStatus(AccessStatusDTO.Status.ACTIVE.name());
- accessStatus.setMessage("Your account is active and you are already logged in.");
+ if (userDetails.getAuthorities().isEmpty()) {
+ accessStatus.setMessage("Your account is active but currently does not have any level of access.");
+ } else {
+ accessStatus.setMessage("Your account is active and you are already logged in.");
+ }
} catch (final IllegalArgumentException iae) {
throw new InvalidAuthenticationException(iae.getMessage(), iae);
}
@@ -271,6 +284,16 @@ public class AccessResource extends ApplicationResource {
}
/**
+ * Checks the status of the proxy.
+ *
+ * @param proxyChain the proxy chain
+ * @throws AuthenticationException if the proxy chain is not authorized
+ */
+ private UserDetails checkAuthorization(final List<String> proxyChain) throws AuthenticationException {
+ return userDetailsService.loadUserDetails(new NiFiAuthorizationRequestToken(proxyChain));
+ }
+
+ /**
* Creates a single use access token for downloading FlowFile content.
*
* @param httpServletRequest the servlet request
@@ -512,8 +535,8 @@ public class AccessResource extends ApplicationResource {
throw new IllegalArgumentException("Unable to determine the user from the incoming request.");
}
- // TODO - authorize the proxy if necessary
-// authorizeProxyIfNecessary(proxyChain);
+ // authorize the proxy if necessary
+ authorizeProxyIfNecessary(proxyChain);
// create the authentication token
loginAuthenticationToken = new LoginAuthenticationToken(proxyChain.get(0), authenticationResponse.getExpiration(), authenticationResponse.getIssuer());
@@ -527,6 +550,30 @@ public class AccessResource extends ApplicationResource {
return generateCreatedResponse(uri, token).build();
}
+ /**
+ * Ensures the proxyChain is authorized before allowing the user to be authenticated.
+ *
+ * @param proxyChain the proxy chain
+ * @throws AuthenticationException if the proxy chain is not authorized
+ */
+ private void authorizeProxyIfNecessary(final List<String> proxyChain) throws AuthenticationException {
+ if (proxyChain.size() > 1) {
+ try {
+ userDetailsService.loadUserDetails(new NiFiAuthorizationRequestToken(proxyChain));
+ } catch (final UsernameNotFoundException unfe) {
+ // if a username not found exception was thrown, the proxies were authorized and now
+ // 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 access denied exception which will return 403
+ throw new AccessDeniedException(e.getMessage(), e);
+ }
+ }
+ }
+
private long validateTokenExpiration(long proposedTokenExpiration, String identity) {
final long maxExpiration = TimeUnit.MILLISECONDS.convert(12, TimeUnit.HOURS);
final long minExpiration = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
@@ -572,4 +619,9 @@ public class AccessResource extends ApplicationResource {
public void setCertificateIdentityProvider(X509IdentityProvider certificateIdentityProvider) {
this.certificateIdentityProvider = certificateIdentityProvider;
}
+
+ public void setUserDetailsService(AuthenticationUserDetailsService<NiFiAuthorizationRequestToken> userDetailsService) {
+ this.userDetailsService = userDetailsService;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.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/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 4fa0b3c..a3d0dc1 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
@@ -84,7 +84,6 @@ 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.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -122,6 +121,34 @@ public class ControllerResource extends ApplicationResource {
}
/**
+ * Locates the User sub-resource.
+ *
+ * @return the User sub-resource
+ */
+ @Path("/users")
+ @ApiOperation(
+ value = "Gets the user resource",
+ response = UserResource.class
+ )
+ public UserResource getUserResource() {
+ return resourceContext.getResource(UserResource.class);
+ }
+
+ /**
+ * Locates the User sub-resource.
+ *
+ * @return the User sub-resource
+ */
+ @Path("/user-groups")
+ @ApiOperation(
+ value = "Gets the user group resource",
+ response = UserGroupResource.class
+ )
+ public UserGroupResource getUserGroupResource() {
+ return resourceContext.getResource(UserGroupResource.class);
+ }
+
+ /**
* Locates the History sub-resource.
*
* @return the History sub-resource
@@ -905,7 +932,7 @@ public class ControllerResource extends ApplicationResource {
// create the response entity
IdentityEntity entity = new IdentityEntity();
entity.setRevision(revision);
- entity.setUserId(user.getIdentity());
+ entity.setUserId(user.getId());
entity.setIdentity(user.getUserName());
// generate the response
@@ -963,8 +990,8 @@ public class ControllerResource extends ApplicationResource {
// create the response entity
AuthorityEntity entity = new AuthorityEntity();
entity.setRevision(revision);
- entity.setUserId(user.getIdentity());
- entity.setAuthorities(new HashSet<>(Arrays.asList("ROLE_MONITOR", "ROLE_DFM", "ROLE_ADMIN", "ROLE_PROXY", "ROLE_NIFI", "ROLE_PROVENANCE")));
+ entity.setUserId(user.getId());
+ entity.setAuthorities(NiFiUserUtils.getAuthorities());
// generate the response
return clusterContext(generateOkResponse(entity)).build();
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserGroupResource.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/UserGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserGroupResource.java
new file mode 100644
index 0000000..3a0b596
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserGroupResource.java
@@ -0,0 +1,465 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.api;
+
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+import com.wordnik.swagger.annotations.Authorization;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.api.entity.UserGroupEntity;
+import org.apache.nifi.web.api.request.ClientIdParameter;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.web.NiFiServiceFacade;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.UserGroupDTO;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+/**
+ * RESTful endpoint for managing this Controller's user groups.
+ */
+@Api(hidden = true)
+public class UserGroupResource extends ApplicationResource {
+
+ /*
+ * Developer Note: Clustering assumes a centralized security provider. The
+ * cluster manager will manage user accounts when in clustered mode and
+ * interface with the authorization provider. However, when nodes perform
+ * Site-to-Site, the authorization details of the remote NiFi will be cached
+ * locally. These details need to be invalidated when certain actions are
+ * performed (revoking/deleting accounts, changing user authorities, user
+ * group, etc).
+ */
+ private WebClusterManager clusterManager;
+ private NiFiProperties properties;
+ private NiFiServiceFacade serviceFacade;
+
+ /**
+ * Updates a new user group.
+ *
+ * @param httpServletRequest request
+ * @param clientId Optional client id. If the client id is not specified, a
+ * new one will be generated. This value (whether specified or generated) is
+ * included in the response.
+ * @param userIds A collection of user ids to include in this group. If a
+ * user already belongs to another group, they will be placed in this group
+ * instead. Existing users in this group will remain in this group.
+ * @param group The name of the group.
+ * @param rawAuthorities Array of authorities to assign to the specified
+ * user.
+ * @param status The status of the specified users account.
+ * @param formParams form params
+ * @return A userGroupEntity.
+ */
+ @PUT
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{group}")
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
+ public Response updateUserGroup(
+ @Context HttpServletRequest httpServletRequest,
+ @PathParam("group") String group,
+ @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @FormParam("userIds[]") Set<String> userIds,
+ @FormParam("authorities[]") Set<String> rawAuthorities,
+ @FormParam("status") String status,
+ MultivaluedMap<String, String> formParams) {
+
+ // get the collection of specified authorities
+ final Set<String> authorities = new HashSet<>();
+ for (String authority : rawAuthorities) {
+ if (StringUtils.isNotBlank(authority)) {
+ authorities.add(authority);
+ }
+ }
+
+ // create the user group dto
+ final UserGroupDTO userGroup = new UserGroupDTO();
+ userGroup.setGroup(group);
+ userGroup.setUserIds(userIds);
+ userGroup.setStatus(status);
+
+ // set the authorities
+ if (!authorities.isEmpty() || formParams.containsKey("authorities")) {
+ userGroup.setAuthorities(authorities);
+ }
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the user group entity
+ final UserGroupEntity entity = new UserGroupEntity();
+ entity.setRevision(revision);
+ entity.setUserGroup(userGroup);
+
+ // create the user group
+ return updateUserGroup(httpServletRequest, group, entity);
+ }
+
+ /**
+ * Creates a new user group with the specified users.
+ *
+ * @param httpServletRequest request
+ * @param group The user group.
+ * @param userGroupEntity A userGroupEntity.
+ * @return A userGroupEntity.
+ */
+ @PUT
+ @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{group}")
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
+ @ApiOperation(
+ value = "Updates a user group",
+ response = UserGroupEntity.class,
+ authorizations = {
+ @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 updateUserGroup(
+ @Context HttpServletRequest httpServletRequest,
+ @ApiParam(
+ value = "The name of the user group.",
+ required = true
+ )
+ @PathParam("group") String group,
+ @ApiParam(
+ value = "The user group configuration details.",
+ required = true
+ )
+ UserGroupEntity userGroupEntity) {
+
+ if (userGroupEntity == null || userGroupEntity.getUserGroup() == null) {
+ throw new IllegalArgumentException("User group details must be specified.");
+ }
+
+ // get the user group
+ UserGroupDTO userGroup = userGroupEntity.getUserGroup();
+
+ // ensure the same id is being used
+ if (!group.equals(userGroup.getGroup())) {
+ throw new IllegalArgumentException(String.format("The user group (%s) in the request body does "
+ + "not equal the user group of the requested resource (%s).", userGroup.getGroup(), group));
+ }
+
+ // the user group must be specified and cannot be blank
+ if (StringUtils.isBlank(userGroup.getGroup())) {
+ throw new IllegalArgumentException("User group must be specified and cannot be blank.");
+ }
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ if (userGroupEntity.getRevision() == null) {
+ revision.setClientId(new ClientIdParameter().getClientId());
+ } else {
+ revision.setClientId(userGroupEntity.getRevision().getClientId());
+ }
+
+ // this user is being modified, replicate to the nodes to invalidate this account
+ // so that it will be re-authorized during the next attempted access - if this wasn't
+ // done the account would remain stale for up to the configured cache duration. this
+ // is acceptable sometimes but when updating a users authorities or groups via the UI
+ // they shouldn't have to wait for the changes to take effect`
+ if (properties.isClusterManager()) {
+ // change content type to JSON for serializing entity
+ final Map<String, String> headersToOverride = new HashMap<>();
+ headersToOverride.put("content-type", MediaType.APPLICATION_JSON);
+
+ // identify yourself as the NCM attempting to invalidate the user
+ final Map<String, String> headers = getHeaders(headersToOverride);
+ headers.put(WebClusterManager.CLUSTER_INVALIDATE_USER_GROUP_HEADER, Boolean.TRUE.toString());
+
+ final RevisionDTO invalidateUserRevision = new RevisionDTO();
+ revision.setClientId(revision.getClientId());
+
+ final UserGroupDTO invalidateUserGroup = new UserGroupDTO();
+ invalidateUserGroup.setGroup(group);
+ invalidateUserGroup.setUserIds(userGroup.getUserIds());
+
+ final UserGroupEntity invalidateUserGroupEntity = new UserGroupEntity();
+ invalidateUserGroupEntity.setRevision(invalidateUserRevision);
+ invalidateUserGroupEntity.setUserGroup(invalidateUserGroup);
+
+ // replicate the invalidate request to each node - if this request is not successful return that fact,
+ // otherwise continue with the desired user modification
+ final NodeResponse response = clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), invalidateUserGroupEntity, headers);
+ if (!response.is2xx()) {
+ return response.getResponse();
+ }
+ }
+
+ // handle expects request (usually from the cluster manager)
+ final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+ if (expects != null) {
+ return generateContinueResponse().build();
+ }
+
+ // handle an invalidate request from the NCM
+ final String invalidateRequest = httpServletRequest.getHeader(WebClusterManager.CLUSTER_INVALIDATE_USER_GROUP_HEADER);
+ if (invalidateRequest != null) {
+ serviceFacade.invalidateUserGroup(userGroup.getGroup(), userGroup.getUserIds());
+ return generateOkResponse().build();
+ }
+
+ // create the user group
+ userGroup = serviceFacade.updateUserGroup(userGroup);
+
+ // create the response entity
+ final UserGroupEntity entity = new UserGroupEntity();
+ entity.setRevision(revision);
+ entity.setUserGroup(userGroup);
+
+ // generate the URI for this group and return
+ return generateOkResponse(entity).build();
+ }
+
+ /**
+ * Deletes the user from the specified group. The user will not be removed,
+ * just the fact that they were in this group.
+ *
+ * @param httpServletRequest request
+ * @param group The user group.
+ * @param userId The user id to remove.
+ * @param clientId Optional client id. If the client id is not specified, a
+ * new one will be generated. This value (whether specified or generated) is
+ * included in the response.
+ * @return A userGroupEntity.
+ */
+ @DELETE
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{group}/users/{userId}")
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
+ @ApiOperation(
+ value = "Removes a user from a user group",
+ notes = "Removes a user from a user group. The will not be deleted, jsut the fact that they were in this group.",
+ response = UserGroupEntity.class,
+ authorizations = {
+ @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 removeUserFromGroup(
+ @Context HttpServletRequest httpServletRequest,
+ @ApiParam(
+ value = "The name of the user group.",
+ required = true
+ )
+ @PathParam("group") String group,
+ @ApiParam(
+ value = "The id of the user to remove from the user group.",
+ required = true
+ )
+ @PathParam("userId") String userId,
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+ // this user is being modified, replicate to the nodes to invalidate this account
+ // so that it will be re-authorized during the next attempted access - if this wasn't
+ // done the account would remain stale for up to the configured cache duration. this
+ // is acceptable sometimes but when removing a user via the UI they shouldn't have to
+ // wait for the changes to take effect
+ if (properties.isClusterManager()) {
+ // identify yourself as the NCM attempting to invalidate the user
+ final Map<String, String> headers = getHeaders();
+ headers.put(WebClusterManager.CLUSTER_INVALIDATE_USER_HEADER, Boolean.TRUE.toString());
+
+ // replicate the invalidate request to each node - if this request is not successful return that fact,
+ // otherwise continue with the desired user modification
+ final NodeResponse response = clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), headers);
+ if (!response.is2xx()) {
+ return response.getResponse();
+ }
+ }
+
+ // handle expects request (usually from the cluster manager)
+ final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+ if (expects != null) {
+ return generateContinueResponse().build();
+ }
+
+ // handle an invalidate request from the NCM
+ final String invalidateRequest = httpServletRequest.getHeader(WebClusterManager.CLUSTER_INVALIDATE_USER_HEADER);
+ if (invalidateRequest != null) {
+ serviceFacade.invalidateUser(userId);
+ return generateOkResponse().build();
+ }
+
+ // ungroup the specified user
+ serviceFacade.removeUserFromGroup(userId);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the response entity
+ final UserGroupEntity entity = new UserGroupEntity();
+ entity.setRevision(revision);
+
+ // generate ok response
+ return generateOkResponse(entity).build();
+ }
+
+ /**
+ * Deletes the user group. The users will not be removed, just the fact that
+ * they were grouped.
+ *
+ * @param httpServletRequest request
+ * @param group The user group.
+ * @param clientId Optional client id. If the client id is not specified, a
+ * new one will be generated. This value (whether specified or generated) is
+ * included in the response.
+ * @return A userGroupEntity.
+ */
+ @DELETE
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{group}")
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
+ @ApiOperation(
+ value = "Deletes a user group",
+ notes = "Deletes a user group. The users will not be removed, just the fact that they were grouped.",
+ response = UserGroupEntity.class,
+ authorizations = {
+ @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 ungroup(
+ @Context HttpServletRequest httpServletRequest,
+ @ApiParam(
+ value = "The name of the user group.",
+ required = true
+ )
+ @PathParam("group") String group,
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+ // this user is being modified, replicate to the nodes to invalidate this account
+ // so that it will be re-authorized during the next attempted access - if this wasn't
+ // done the account would remain stale for up to the configured cache duration. this
+ // is acceptable sometimes but when removing a user via the UI they shouldn't have to
+ // wait for the changes to take effect
+ if (properties.isClusterManager()) {
+ // identify yourself as the NCM attempting to invalidate the user
+ final Map<String, String> headers = getHeaders();
+ headers.put(WebClusterManager.CLUSTER_INVALIDATE_USER_GROUP_HEADER, Boolean.TRUE.toString());
+
+ // replicate the invalidate request to each node - if this request is not successful return that fact,
+ // otherwise continue with the desired user modification
+ final NodeResponse response = clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), headers);
+ if (!response.is2xx()) {
+ return response.getResponse();
+ }
+ }
+
+ // handle expects request (usually from the cluster manager)
+ final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+ if (expects != null) {
+ return generateContinueResponse().build();
+ }
+
+ // handle an invalidate request from the NCM
+ final String invalidateRequest = httpServletRequest.getHeader(WebClusterManager.CLUSTER_INVALIDATE_USER_GROUP_HEADER);
+ if (invalidateRequest != null) {
+ serviceFacade.invalidateUserGroup(group, null);
+ return generateOkResponse().build();
+ }
+
+ // delete the user group
+ serviceFacade.removeUserGroup(group);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the response entity
+ final UserGroupEntity entity = new UserGroupEntity();
+ entity.setRevision(revision);
+
+ // generate ok response
+ return generateOkResponse(entity).build();
+ }
+
+ /* setters */
+ public void setServiceFacade(NiFiServiceFacade serviceFacade) {
+ this.serviceFacade = serviceFacade;
+ }
+
+ public void setProperties(NiFiProperties properties) {
+ this.properties = properties;
+ }
+
+ public void setClusterManager(WebClusterManager clusterManager) {
+ this.clusterManager = clusterManager;
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.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/UserResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
new file mode 100644
index 0000000..1426999
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/UserResource.java
@@ -0,0 +1,617 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.api;
+
+import com.sun.jersey.api.Responses;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+import com.wordnik.swagger.annotations.Authorization;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.api.dto.UserDTO;
+import org.apache.nifi.web.api.dto.search.UserGroupSearchResultDTO;
+import org.apache.nifi.web.api.dto.search.UserSearchResultDTO;
+import org.apache.nifi.web.api.entity.UserEntity;
+import org.apache.nifi.web.api.entity.UserSearchResultsEntity;
+import org.apache.nifi.web.api.entity.UsersEntity;
+import org.apache.nifi.web.api.request.ClientIdParameter;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.web.NiFiServiceFacade;
+import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+/**
+ * RESTful endpoint for managing this Controller's users.
+ */
+@Api(hidden = true)
+public class UserResource extends ApplicationResource {
+
+ /*
+ * Developer Note: Clustering assumes a centralized security provider. The
+ * cluster manager will manage user accounts when in clustered mode and
+ * interface with the authorization provider. However, when nodes perform
+ * Site-to-Site, the authorization details of the remote NiFi will be cached
+ * locally. These details need to be invalidated when certain actions are
+ * performed (revoking/deleting accounts, changing user authorities, user
+ * group, etc).
+ */
+ private WebClusterManager clusterManager;
+ private NiFiProperties properties;
+ private NiFiServiceFacade serviceFacade;
+
+ /**
+ * Creates a new user account request.
+ *
+ * @return A string
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @Produces(MediaType.TEXT_PLAIN)
+ @Path("") // necessary due to a bug in swagger
+ @ApiOperation(
+ value = "Creates a user",
+ response = String.class
+ )
+ public Response createUser() {
+ if (!properties.getSupportNewAccountRequests()) {
+ return Responses.notFound().entity("This NiFi does not support new account requests.").build();
+ }
+
+ final NiFiUser nifiUser = NiFiUserUtils.getNiFiUser();
+ if (nifiUser != null) {
+ throw new IllegalArgumentException("User account already created " + nifiUser.getIdentity());
+ }
+
+ // create an account request for the current user
+ final UserDTO user = serviceFacade.createUser();
+
+ final String uri = generateResourceUri("controller", "users", user.getId());
+ return generateCreatedResponse(URI.create(uri), "Not authorized. User account created. Authorization pending.").build();
+ }
+
+ /**
+ * Gets all users that are registered within this Controller.
+ *
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @param grouped Whether to return the users in their groups.
+ * @return A usersEntity.
+ */
+ @GET
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("") // necessary due to a bug in swagger
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
+ @ApiOperation(
+ value = "Gets all users",
+ response = UsersEntity.class,
+ authorizations = {
+ @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 = 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 getUsers(
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @ApiParam(
+ value = "Whether to return the users in their respective groups.",
+ required = false
+ )
+ @QueryParam("grouped") @DefaultValue("false") Boolean grouped) {
+
+ // get the users
+ final Collection<UserDTO> users = serviceFacade.getUsers(grouped);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the response entity
+ final UsersEntity usersEntity = new UsersEntity();
+ usersEntity.setRevision(revision);
+ usersEntity.setUsers(users);
+ usersEntity.setGenerated(new Date());
+
+ // build the response
+ return generateOkResponse(usersEntity).build();
+ }
+
+ /**
+ * Gets the details for the specified user.
+ *
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @param id The user id.
+ * @return A userEntity.
+ */
+ @GET
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
+ @Path("/{id}")
+ @ApiOperation(
+ value = "Gets a user",
+ response = UserEntity.class,
+ authorizations = {
+ @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 getUser(
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @ApiParam(
+ value = "The user id.",
+ required = true
+ )
+ @PathParam("id") String id) {
+
+ // get the specified user
+ final UserDTO userDTO = serviceFacade.getUser(id);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the response entity
+ final UserEntity userEntity = new UserEntity();
+ userEntity.setRevision(revision);
+ userEntity.setUser(userDTO);
+
+ // build the response
+ return generateOkResponse(userEntity).build();
+ }
+
+ /**
+ * Searches for users with match the specified query.
+ *
+ * @param value Search value that will be matched against users
+ * @return A userSearchResultsEntity
+ */
+ @GET
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/search-results")
+ @PreAuthorize("hasAnyRole('ROLE_DFM', 'ROLE_ADMIN')")
+ @ApiOperation(
+ value = "Searches for users",
+ response = UserSearchResultsEntity.class,
+ authorizations = {
+ @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 = 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 searchUsers(
+ @ApiParam(
+ value = "The search terms.",
+ required = true
+ )
+ @QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) {
+
+ final List<UserSearchResultDTO> userMatches = new ArrayList<>();
+ final List<UserGroupSearchResultDTO> userGroupMatches = new ArrayList<>();
+
+ // get the users
+ final Collection<UserDTO> users = serviceFacade.getUsers(Boolean.FALSE);
+ final Collection<String> matchedGroups = new HashSet<>();
+
+ // check each to see if it matches the search term
+ for (UserDTO user : users) {
+ // count the user if there is no search or it matches the address
+ if (StringUtils.isBlank(value)) {
+ // record the group match if there is one and it hasn't already been encountered
+ if (user.getUserGroup() != null && !matchedGroups.contains(user.getUserGroup())) {
+ // add the matched group
+ matchedGroups.add(user.getUserGroup());
+
+ // record the group match
+ final UserGroupSearchResultDTO userGroupMatch = new UserGroupSearchResultDTO();
+ userGroupMatch.setGroup(user.getUserGroup());
+ userGroupMatches.add(userGroupMatch);
+ }
+
+ // record the user match
+ final UserSearchResultDTO userMatch = new UserSearchResultDTO();
+ userMatch.setUserDn(user.getDn());
+ userMatch.setUserName(user.getUserName());
+ userMatches.add(userMatch);
+ } else {
+ // look for a user match
+ if (StringUtils.containsIgnoreCase(user.getDn(), value) || StringUtils.containsIgnoreCase(user.getUserName(), value)) {
+ // record the user match
+ final UserSearchResultDTO userMatch = new UserSearchResultDTO();
+ userMatch.setUserDn(user.getDn());
+ userMatch.setUserName(user.getUserName());
+ userMatches.add(userMatch);
+ }
+
+ // look for a dn match
+ if (StringUtils.containsIgnoreCase(user.getUserGroup(), value)) {
+ // record the group match if it hasn't already been encountered
+ if (!matchedGroups.contains(user.getUserGroup())) {
+ // add the matched group
+ matchedGroups.add(user.getUserGroup());
+
+ // record the group match
+ final UserGroupSearchResultDTO userGroupMatch = new UserGroupSearchResultDTO();
+ userGroupMatch.setGroup(user.getUserGroup());
+ userGroupMatches.add(userGroupMatch);
+ }
+ }
+ }
+ }
+
+ // sort the user matches
+ Collections.sort(userMatches, new Comparator<UserSearchResultDTO>() {
+ @Override
+ public int compare(UserSearchResultDTO user1, UserSearchResultDTO user2) {
+ return user1.getUserName().compareTo(user2.getUserName());
+ }
+ });
+
+ // sort the user group matches
+ Collections.sort(userGroupMatches, new Comparator<UserGroupSearchResultDTO>() {
+ @Override
+ public int compare(UserGroupSearchResultDTO userGroup1, UserGroupSearchResultDTO userGroup2) {
+ return userGroup1.getGroup().compareTo(userGroup2.getGroup());
+ }
+ });
+
+ // build the response
+ final UserSearchResultsEntity results = new UserSearchResultsEntity();
+ results.setUserResults(userMatches);
+ results.setUserGroupResults(userGroupMatches);
+
+ // generate an 200 - OK response
+ return noCache(Response.ok(results)).build();
+ }
+
+ /**
+ * Updates the specified user.
+ *
+ * @param httpServletRequest request
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @param id The id of the user to update.
+ * @param rawAuthorities Array of authorities to assign to the specified user.
+ * @param status The status of the specified users account.
+ * @param formParams form params
+ * @return A userEntity
+ */
+ @PUT
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
+ @Path("/{id}")
+ public Response updateUser(
+ @Context HttpServletRequest httpServletRequest,
+ @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+ @PathParam("id") String id,
+ @FormParam("authorities[]") Set<String> rawAuthorities,
+ @FormParam("status") String status,
+ MultivaluedMap<String, String> formParams) {
+
+ // create the user
+ final UserDTO userDTO = new UserDTO();
+ userDTO.setId(id);
+ userDTO.setStatus(status);
+
+ // get the collection of specified authorities
+ final Set<String> authorities = new HashSet<>();
+ for (String authority : rawAuthorities) {
+ if (StringUtils.isNotBlank(authority)) {
+ authorities.add(authority);
+ }
+ }
+
+ // set the authorities
+ if (!authorities.isEmpty() || formParams.containsKey("authorities")) {
+ userDTO.setAuthorities(authorities);
+ }
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the user entity
+ UserEntity userEntity = new UserEntity();
+ userEntity.setRevision(revision);
+ userEntity.setUser(userDTO);
+
+ // update the user
+ return updateUser(httpServletRequest, id, userEntity);
+ }
+
+ /**
+ * Updates the specified user.
+ *
+ * @param httpServletRequest request
+ * @param id The id of the user to update.
+ * @param userEntity A userEntity
+ * @return A userEntity
+ */
+ @PUT
+ @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
+ @Path("/{id}")
+ @ApiOperation(
+ value = "Updates a user",
+ response = UserEntity.class,
+ authorizations = {
+ @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 updateUser(
+ @Context HttpServletRequest httpServletRequest,
+ @ApiParam(
+ value = "The user id.",
+ required = true
+ )
+ @PathParam("id") String id,
+ @ApiParam(
+ value = "The user configuration details.",
+ required = true
+ ) UserEntity userEntity) {
+
+ if (userEntity == null || userEntity.getUser() == null) {
+ throw new IllegalArgumentException("User details must be specified.");
+ }
+
+ // ensure the same user id is being used
+ final UserDTO userDTO = userEntity.getUser();
+ if (!id.equals(userDTO.getId())) {
+ throw new IllegalArgumentException(String.format("The user id (%s) in the request body does "
+ + "not equal the user id of the requested resource (%s).", userDTO.getId(), id));
+ }
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ if (userEntity.getRevision() == null) {
+ revision.setClientId(new ClientIdParameter().getClientId());
+ } else {
+ revision.setClientId(userEntity.getRevision().getClientId());
+ }
+
+ // this user is being modified, replicate to the nodes to invalidate this account
+ // so that it will be re-authorized during the next attempted access - if this wasn't
+ // done the account would remain stale for up to the configured cache duration. this
+ // is acceptable sometimes but when updating a users authorities or groups via the UI
+ // they shouldn't have to wait for the changes to take effect`
+ if (properties.isClusterManager()) {
+ // change content type to JSON for serializing entity
+ final Map<String, String> headersToOverride = new HashMap<>();
+ headersToOverride.put("content-type", MediaType.APPLICATION_JSON);
+
+ // identify yourself as the NCM attempting to invalidate the user
+ final Map<String, String> headers = getHeaders(headersToOverride);
+ headers.put(WebClusterManager.CLUSTER_INVALIDATE_USER_HEADER, Boolean.TRUE.toString());
+
+ final RevisionDTO invalidateUserRevision = new RevisionDTO();
+ revision.setClientId(revision.getClientId());
+
+ final UserDTO invalidateUser = new UserDTO();
+ invalidateUser.setId(userDTO.getId());
+
+ final UserEntity invalidateUserEntity = new UserEntity();
+ invalidateUserEntity.setRevision(invalidateUserRevision);
+ invalidateUserEntity.setUser(userDTO);
+
+ // replicate the invalidate request to each node - if this request is not successful return that fact,
+ // otherwise continue with the desired user modification
+ final NodeResponse response = clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), invalidateUserEntity, headers);
+ if (!response.is2xx()) {
+ return response.getResponse();
+ }
+ }
+
+ // handle expects request (usually from the cluster manager)
+ final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+ if (expects != null) {
+ return generateContinueResponse().build();
+ }
+
+ // handle an invalidate request from the NCM
+ final String invalidateRequest = httpServletRequest.getHeader(WebClusterManager.CLUSTER_INVALIDATE_USER_HEADER);
+ if (invalidateRequest != null) {
+ serviceFacade.invalidateUser(id);
+ return generateOkResponse().build();
+ }
+
+ // update the user
+ final UserDTO reponseUserDTO = serviceFacade.updateUser(userDTO);
+
+ // create the response entity
+ UserEntity responseUserEntity = new UserEntity();
+ responseUserEntity.setRevision(revision);
+ responseUserEntity.setUser(reponseUserDTO);
+
+ // build the response
+ return generateOkResponse(responseUserEntity).build();
+ }
+
+ /**
+ * Deletes the specified user.
+ *
+ * @param httpServletRequest request
+ * @param id The user id
+ * @param clientId Optional client id. If the client id is not specified, a new one will be generated. This value (whether specified or generated) is included in the response.
+ * @return A userEntity.
+ */
+ @DELETE
+ @Consumes(MediaType.WILDCARD)
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ @Path("/{id}")
+ @PreAuthorize("hasRole('ROLE_ADMIN')")
+ @ApiOperation(
+ value = "Deletes a user",
+ response = UserEntity.class,
+ authorizations = {
+ @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 deleteUser(
+ @Context HttpServletRequest httpServletRequest,
+ @ApiParam(
+ value = "The user id.",
+ required = true
+ )
+ @PathParam("id") String id,
+ @ApiParam(
+ value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.",
+ required = false
+ )
+ @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+ // this user is being modified, replicate to the nodes to invalidate this account
+ // so that it will be re-authorized during the next attempted access - if this wasn't
+ // done the account would remain stale for up to the configured cache duration. this
+ // is acceptable sometimes but when removing a user via the UI they shouldn't have to
+ // wait for the changes to take effect
+ if (properties.isClusterManager()) {
+ // identify yourself as the NCM attempting to invalidate the user
+ final Map<String, String> headers = getHeaders();
+ headers.put(WebClusterManager.CLUSTER_INVALIDATE_USER_HEADER, Boolean.TRUE.toString());
+
+ // replicate the invalidate request to each node - if this request is not successful return that fact,
+ // otherwise continue with the desired user modification
+ final NodeResponse response = clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), headers);
+ if (!response.is2xx()) {
+ return response.getResponse();
+ }
+ }
+
+ // handle expects request (usually from the cluster manager)
+ final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+ if (expects != null) {
+ return generateContinueResponse().build();
+ }
+
+ // handle an invalidate request from the NCM
+ final String invalidateRequest = httpServletRequest.getHeader(WebClusterManager.CLUSTER_INVALIDATE_USER_HEADER);
+ if (invalidateRequest != null) {
+ serviceFacade.invalidateUser(id);
+ return generateOkResponse().build();
+ }
+
+ // ungroup the specified user
+ serviceFacade.deleteUser(id);
+
+ // create the revision
+ final RevisionDTO revision = new RevisionDTO();
+ revision.setClientId(clientId.getClientId());
+
+ // create the response entity
+ final UserEntity entity = new UserEntity();
+ entity.setRevision(revision);
+
+ // generate ok response
+ return generateOkResponse(entity).build();
+ }
+
+ /* setters */
+ public void setServiceFacade(NiFiServiceFacade serviceFacade) {
+ this.serviceFacade = serviceFacade;
+ }
+
+ public void setProperties(NiFiProperties properties) {
+ this.properties = properties;
+ }
+
+ public void setClusterManager(WebClusterManager clusterManager) {
+ this.clusterManager = clusterManager;
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccountNotFoundExceptionMapper.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/config/AccountNotFoundExceptionMapper.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccountNotFoundExceptionMapper.java
new file mode 100644
index 0000000..8fed1a2
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/AccountNotFoundExceptionMapper.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web.api.config;
+
+import com.sun.jersey.api.Responses;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.apache.nifi.admin.service.AccountNotFoundException;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Maps resource not found exceptions into client responses.
+ */
+@Provider
+public class AccountNotFoundExceptionMapper implements ExceptionMapper<AccountNotFoundException> {
+
+ private static final Logger logger = LoggerFactory.getLogger(AccountNotFoundExceptionMapper.class);
+
+ @Override
+ public Response toResponse(AccountNotFoundException exception) {
+ logger.info(String.format("%s. Returning %s response.", exception, Response.Status.NOT_FOUND));
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(StringUtils.EMPTY, exception);
+ }
+
+ return Responses.notFound().entity(exception.getMessage()).type("text/plain").build();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.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/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
index 0ae7649..5e7a902 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
@@ -16,6 +16,29 @@
*/
package org.apache.nifi.web.api.dto;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.WebApplicationException;
+
import org.apache.nifi.action.Action;
import org.apache.nifi.action.component.details.ComponentDetails;
import org.apache.nifi.action.component.details.ExtensionDetails;
@@ -34,6 +57,7 @@ import org.apache.nifi.action.details.PurgeDetails;
import org.apache.nifi.annotation.behavior.Stateful;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.authorization.Authority;
import org.apache.nifi.cluster.HeartbeatPayload;
import org.apache.nifi.cluster.event.Event;
import org.apache.nifi.cluster.manager.StatusMerger;
@@ -98,6 +122,8 @@ import org.apache.nifi.reporting.Bulletin;
import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.reporting.ReportingTask;
import org.apache.nifi.scheduling.SchedulingStrategy;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.user.NiFiUserGroup;
import org.apache.nifi.util.FormatUtils;
import org.apache.nifi.web.FlowModification;
import org.apache.nifi.web.Revision;
@@ -129,28 +155,6 @@ import org.apache.nifi.web.api.dto.status.ProcessorStatusSnapshotDTO;
import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO;
import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusSnapshotDTO;
-import javax.ws.rs.WebApplicationException;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.concurrent.TimeUnit;
-
public final class DtoFactory {
@SuppressWarnings("rawtypes")
@@ -2530,6 +2534,57 @@ public final class DtoFactory {
return revisionDTO;
}
+ /**
+ * Factory method for creating a new user transfer object.
+ *
+ * @param user user
+ * @return dto
+ */
+ public UserDTO createUserDTO(NiFiUser user) {
+ // convert the users authorities
+ Set<String> authorities = Authority.convertAuthorities(user.getAuthorities());
+
+ // create the user
+ UserDTO userDTO = new UserDTO();
+ userDTO.setId(String.valueOf(user.getId()));
+ userDTO.setDn(user.getIdentity());
+ userDTO.setUserName(user.getUserName());
+ userDTO.setUserGroup(user.getUserGroup());
+ userDTO.setJustification(user.getJustification());
+ userDTO.setAuthorities(authorities);
+
+ // ensure the date fields are not null
+ if (user.getCreation() != null) {
+ userDTO.setCreation(user.getCreation());
+ }
+ if (user.getLastAccessed() != null) {
+ userDTO.setLastAccessed(user.getLastAccessed());
+ }
+ if (user.getLastVerified() != null) {
+ userDTO.setLastVerified(user.getLastVerified());
+ }
+ if (user.getStatus() != null) {
+ userDTO.setStatus(user.getStatus().toString());
+ }
+
+ return userDTO;
+ }
+
+ public UserGroupDTO createUserGroupDTO(NiFiUserGroup userGroup) {
+ UserGroupDTO userGroupDto = new UserGroupDTO();
+ userGroupDto.setGroup(userGroup.getGroup());
+ userGroupDto.setUserIds(new HashSet<String>());
+
+ // set the users if they have been specified
+ if (userGroup.getUsers() != null) {
+ for (NiFiUser user : userGroup.getUsers()) {
+ userGroupDto.getUserIds().add(String.valueOf(user.getId()));
+ }
+ }
+
+ return userGroupDto;
+ }
+
public NodeDTO createNodeDTO(Node node, List<Event> events, boolean primary) {
final NodeDTO nodeDto = new NodeDTO();
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/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 1f2beaf..68d0dbe 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
@@ -20,6 +20,7 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.admin.service.UserService;
+import org.apache.nifi.authorization.DownloadAuthorization;
import org.apache.nifi.cluster.protocol.NodeIdentifier;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.connectable.Connectable;
@@ -103,6 +104,7 @@ import org.apache.nifi.web.security.ProxiedEntitiesUtils;
import org.apache.nifi.web.security.user.NiFiUserUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.security.access.AccessDeniedException;
import javax.ws.rs.WebApplicationException;
import java.io.IOException;
@@ -947,11 +949,11 @@ public class ControllerFacade {
// calculate the dn chain
final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user);
- // TODO - ensure the users in this chain are allowed to download this content
-// final DownloadAuthorization downloadAuthorization = userService.authorizeDownload(dnChain, attributes);
-// if (!downloadAuthorization.isApproved()) {
-// throw new AccessDeniedException(downloadAuthorization.getExplanation());
-// }
+ // ensure the users in this chain are allowed to download this content
+ final DownloadAuthorization downloadAuthorization = userService.authorizeDownload(dnChain, attributes);
+ if (!downloadAuthorization.isApproved()) {
+ throw new AccessDeniedException(downloadAuthorization.getExplanation());
+ }
// get the filename and fall back to the identifier (should never happen)
String filename = attributes.get(CoreAttributes.FILENAME.key());
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
index 5f0a70c..e1faa14 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java
@@ -17,6 +17,7 @@
package org.apache.nifi.web.dao.impl;
import org.apache.nifi.admin.service.UserService;
+import org.apache.nifi.authorization.DownloadAuthorization;
import org.apache.nifi.connectable.Connectable;
import org.apache.nifi.connectable.ConnectableType;
import org.apache.nifi.connectable.Connection;
@@ -47,6 +48,7 @@ import org.apache.nifi.web.security.ProxiedEntitiesUtils;
import org.apache.nifi.web.security.user.NiFiUserUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.security.access.AccessDeniedException;
import javax.ws.rs.WebApplicationException;
import java.io.IOException;
@@ -608,12 +610,12 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO
// calculate the dn chain
final List<String> dnChain = ProxiedEntitiesUtils.buildProxiedEntitiesChain(user);
- // TODO - ensure the users in this chain are allowed to download this content
+ // ensure the users in this chain are allowed to download this content
final Map<String, String> attributes = flowFile.getAttributes();
-// final DownloadAuthorization downloadAuthorization = userService.authorizeDownload(dnChain, attributes);
-// if (!downloadAuthorization.isApproved()) {
-// throw new AccessDeniedException(downloadAuthorization.getExplanation());
-// }
+ final DownloadAuthorization downloadAuthorization = userService.authorizeDownload(dnChain, attributes);
+ if (!downloadAuthorization.isApproved()) {
+ throw new AccessDeniedException(downloadAuthorization.getExplanation());
+ }
// get the filename and fall back to the identifier (should never happen)
String filename = attributes.get(CoreAttributes.FILENAME.key());
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/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 555107f..6c2165f 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
@@ -234,6 +234,16 @@
<property name="properties" ref="nifiProperties"/>
<property name="clusterManager" ref="clusterManager"/>
</bean>
+ <bean id="userResource" class="org.apache.nifi.web.api.UserResource" scope="singleton">
+ <property name="serviceFacade" ref="serviceFacade"/>
+ <property name="properties" ref="nifiProperties"/>
+ <property name="clusterManager" ref="clusterManager"/>
+ </bean>
+ <bean id="userGroupResource" class="org.apache.nifi.web.api.UserGroupResource" scope="singleton">
+ <property name="serviceFacade" ref="serviceFacade"/>
+ <property name="properties" ref="nifiProperties"/>
+ <property name="clusterManager" ref="clusterManager"/>
+ </bean>
<bean id="clusterResource" class="org.apache.nifi.web.api.ClusterResource" scope="singleton">
<property name="serviceFacade" ref="serviceFacade"/>
<property name="properties" ref="nifiProperties"/>
@@ -255,6 +265,7 @@
<property name="jwtService" ref="jwtService"/>
<property name="otpService" ref="otpService"/>
<property name="kerberosService" ref="kerberosService"/>
+ <property name="userDetailsService" ref="userDetailsService"/>
</bean>
<!-- configuration for jaxb serialization -->
@@ -264,6 +275,7 @@
<bean class="org.apache.nifi.web.api.config.AccessDeniedExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.InvalidAuthenticationExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.AuthenticationCredentialsNotFoundExceptionMapper" scope="singleton"/>
+ <bean class="org.apache.nifi.web.api.config.AccountNotFoundExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.AdministrationExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.ClusterExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.IllegalArgumentExceptionMapper" scope="singleton"/>
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/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
index 5b96c6e..fe48490 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
@@ -41,13 +41,11 @@ import org.apache.nifi.web.util.WebUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
/**
* Access token endpoint test.
*/
-@Ignore
public class AccessTokenEndpointTest {
private static final String CLIENT_ID = "token-endpoint-id";
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AdminAccessControlTest.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/AdminAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AdminAccessControlTest.java
index dd69954..8e0efd1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AdminAccessControlTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/AdminAccessControlTest.java
@@ -53,13 +53,11 @@ import org.apache.commons.collections4.CollectionUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
/**
* Access control test for the admin user.
*/
-@Ignore
public class AdminAccessControlTest {
public static final String ADMIN_USER_DN = "CN=Lastname Firstname Middlename admin, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown";
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/DfmAccessControlTest.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/DfmAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/DfmAccessControlTest.java
index 914cf60..283a4a9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/DfmAccessControlTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/DfmAccessControlTest.java
@@ -78,7 +78,6 @@ import org.junit.Test;
/**
* Access control test for the dfm user.
*/
-@Ignore
public class DfmAccessControlTest {
public static final String DFM_USER_DN = "CN=Lastname Firstname Middlename dfm, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown";
http://git-wip-us.apache.org/repos/asf/nifi/blob/3f4ac315/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ReadOnlyAccessControlTest.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/ReadOnlyAccessControlTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ReadOnlyAccessControlTest.java
index 2ed653a..0ab074f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ReadOnlyAccessControlTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/ReadOnlyAccessControlTest.java
@@ -49,13 +49,11 @@ import org.apache.nifi.web.api.entity.ProcessorsEntity;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
/**
* Access control test for a read only user.
*/
-@Ignore
public class ReadOnlyAccessControlTest {
public static final String READ_ONLY_USER_DN = "CN=Lastname Firstname Middlename monitor, OU=Unknown, OU=Unknown, OU=Unknown, O=Unknown, C=Unknown";