NIFI-1952 Updated StandardPolicyBasedAuthorizerDAO to throw ResourceNotFoundExceptions when user/group/policy not found

Added spec for StandardPolicyBasedAuthorizerDAO
Added exception mapper for AuthorizationAccessException, added mapper to nifi-web-api-context.xml
Added rest endpoints to get all users and user groups
Merged UsersResource and UserGroupsResource into TenantsResource
This closes #582
This commit is contained in:
Jeff Storck 2016-06-22 20:47:53 -04:00 committed by Matt Gilman
parent b911c9dbdf
commit 64719b6f9b
23 changed files with 1671 additions and 974 deletions

View File

@ -29,7 +29,7 @@ import java.util.Set;
public class UserDTO extends ComponentDTO { public class UserDTO extends ComponentDTO {
private String identity; private String identity;
private Set<UserGroupEntity> groups; private Set<UserGroupEntity> userGroups;
/** /**
* @return users identity * @return users identity
@ -49,11 +49,11 @@ public class UserDTO extends ComponentDTO {
* @return groups to which the user belongs * @return groups to which the user belongs
*/ */
@ApiModelProperty(value = "The groups to which the user belongs.") @ApiModelProperty(value = "The groups to which the user belongs.")
public Set<UserGroupEntity> getGroups() { public Set<UserGroupEntity> getUserGroups() {
return groups; return userGroups;
} }
public void setGroups(Set<UserGroupEntity> groups) { public void setUserGroups(Set<UserGroupEntity> userGroups) {
this.groups = groups; this.userGroups = userGroups;
} }
} }

View File

@ -14,18 +14,31 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.nifi.authorization.resource; package org.apache.nifi.web.api.entity;
import org.apache.nifi.authorization.Resource; import javax.xml.bind.annotation.XmlRootElement;
import java.util.Collection;
public class UserGroupsAuthorizable implements Authorizable { /**
@Override * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a collection of
public Authorizable getParentAuthorizable() { * UserGroupEntity objects.
return null; */
@XmlRootElement(name = "userGroupsEntity")
public class UserGroupsEntity {
private Collection<UserGroupEntity> userGroups;
/**
* The collection of UserGroupEntity objects that are being serialized.
*
* @return The UserGroupEntity objects
*/
public Collection<UserGroupEntity> getUserGroups() {
return userGroups;
} }
@Override public void setUserGroups(Collection<UserGroupEntity> userGroups) {
public Resource getResource() { this.userGroups = userGroups;
return ResourceFactory.getUserGroupsResource();
} }
} }

View File

@ -16,45 +16,28 @@
*/ */
package org.apache.nifi.web.api.entity; package org.apache.nifi.web.api.entity;
import java.util.Collection;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.util.Collection;
import org.apache.nifi.web.api.dto.UserDTO;
import org.apache.nifi.web.api.dto.util.TimeAdapter;
/** /**
* A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a collection of UserDTO. * A serialized representation of this class can be placed in the entity body of a request or response to or from the API. This particular entity holds a reference to a collection of UserEntity
* objects.
*/ */
@XmlRootElement(name = "usersEntity") @XmlRootElement(name = "usersEntity")
public class UsersEntity extends Entity { public class UsersEntity extends Entity {
private Collection<UserDTO> users; private Collection<UserEntity> users;
private Date generated;
/** /**
* The collection of UserDTOs that are being serialized. * The collection of UserEntity objects that are being serialized.
* *
* @return The UserDTO object * @return The UserEntity objects
*/ */
public Collection<UserDTO> getUsers() { public Collection<UserEntity> getUsers() {
return users; return users;
} }
public void setUsers(Collection<UserDTO> users) { public void setUsers(Collection<UserEntity> users) {
this.users = users; this.users = users;
} }
/**
* @return When this content was generated
*/
@XmlJavaTypeAdapter(TimeAdapter.class)
public Date getGenerated() {
return generated;
}
public void setGenerated(Date generated) {
this.generated = generated;
}
} }

View File

@ -327,11 +327,8 @@ public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
addAccessPolicy(authorizations, ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, adminUser.getIdentifier(), READ_CODE + WRITE_CODE); addAccessPolicy(authorizations, ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, adminUser.getIdentifier(), READ_CODE + WRITE_CODE);
} }
// grant the user read/write access to the /users resource // grant the user read/write access to the /tenants resource
addAccessPolicy(authorizations, ResourceType.User.getValue(), adminUser.getIdentifier(), READ_CODE + WRITE_CODE); addAccessPolicy(authorizations, ResourceType.Tenant.getValue(), adminUser.getIdentifier(), READ_CODE + WRITE_CODE);
// grant the user read/write access to the /groups resource
addAccessPolicy(authorizations, ResourceType.Group.getValue(), adminUser.getIdentifier(), READ_CODE + WRITE_CODE);
// grant the user read/write access to the /policies resource // grant the user read/write access to the /policies resource
addAccessPolicy(authorizations, ResourceType.Policy.getValue(), adminUser.getIdentifier(), READ_CODE + WRITE_CODE); addAccessPolicy(authorizations, ResourceType.Policy.getValue(), adminUser.getIdentifier(), READ_CODE + WRITE_CODE);

View File

@ -79,8 +79,7 @@ public final class RoleAccessPolicy {
if (rootGroupId != null) { if (rootGroupId != null) {
adminPolicies.add(new RoleAccessPolicy(ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, READ_ACTION)); adminPolicies.add(new RoleAccessPolicy(ResourceType.ProcessGroup.getValue() + "/" + rootGroupId, READ_ACTION));
} }
adminPolicies.add(new RoleAccessPolicy(ResourceType.User.getValue(), READ_WRITE_ACTION)); adminPolicies.add(new RoleAccessPolicy(ResourceType.Tenant.getValue(), READ_WRITE_ACTION));
adminPolicies.add(new RoleAccessPolicy(ResourceType.Group.getValue(), READ_WRITE_ACTION));
adminPolicies.add(new RoleAccessPolicy(ResourceType.Policy.getValue(), READ_WRITE_ACTION)); adminPolicies.add(new RoleAccessPolicy(ResourceType.Policy.getValue(), READ_WRITE_ACTION));
roleAccessPolicies.put(Role.ROLE_ADMIN, Collections.unmodifiableSet(adminPolicies)); roleAccessPolicies.put(Role.ROLE_ADMIN, Collections.unmodifiableSet(adminPolicies));

View File

@ -235,7 +235,7 @@ public class FileAuthorizerTest {
// verify user4's policies // verify user4's policies
final Map<String,Set<RequestAction>> user4Policies = getResourceActions(policies, user4); final Map<String,Set<RequestAction>> user4Policies = getResourceActions(policies, user4);
assertEquals(6, user4Policies.size()); assertEquals(5, user4Policies.size());
assertTrue(user4Policies.containsKey(ResourceType.Flow.getValue())); assertTrue(user4Policies.containsKey(ResourceType.Flow.getValue()));
assertEquals(1, user4Policies.get(ResourceType.Flow.getValue()).size()); assertEquals(1, user4Policies.get(ResourceType.Flow.getValue()).size());
@ -245,11 +245,8 @@ public class FileAuthorizerTest {
assertEquals(1, user4Policies.get(ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID).size()); assertEquals(1, user4Policies.get(ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID).size());
assertTrue(user4Policies.get(ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID).contains(RequestAction.READ)); assertTrue(user4Policies.get(ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID).contains(RequestAction.READ));
assertTrue(user4Policies.containsKey(ResourceType.User.getValue())); assertTrue(user4Policies.containsKey(ResourceType.Tenant.getValue()));
assertEquals(2, user4Policies.get(ResourceType.User.getValue()).size()); assertEquals(2, user4Policies.get(ResourceType.Tenant.getValue()).size());
assertTrue(user4Policies.containsKey(ResourceType.Group.getValue()));
assertEquals(2, user4Policies.get(ResourceType.Group.getValue()).size());
assertTrue(user4Policies.containsKey(ResourceType.Policy.getValue())); assertTrue(user4Policies.containsKey(ResourceType.Policy.getValue()));
assertEquals(2, user4Policies.get(ResourceType.Policy.getValue()).size()); assertEquals(2, user4Policies.get(ResourceType.Policy.getValue()).size());
@ -338,7 +335,7 @@ public class FileAuthorizerTest {
assertEquals(adminIdentity, adminUser.getIdentity()); assertEquals(adminIdentity, adminUser.getIdentity());
final Set<AccessPolicy> policies = authorizer.getAccessPolicies(); final Set<AccessPolicy> policies = authorizer.getAccessPolicies();
assertEquals(5, policies.size()); assertEquals(4, policies.size());
final String rootGroupResource = ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID; final String rootGroupResource = ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID;
@ -375,7 +372,7 @@ public class FileAuthorizerTest {
assertEquals(adminIdentity, adminUser.getIdentity()); assertEquals(adminIdentity, adminUser.getIdentity());
final Set<AccessPolicy> policies = authorizer.getAccessPolicies(); final Set<AccessPolicy> policies = authorizer.getAccessPolicies();
assertEquals(4, policies.size()); assertEquals(3, policies.size());
final String rootGroupResource = ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID; final String rootGroupResource = ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID;
@ -412,7 +409,7 @@ public class FileAuthorizerTest {
assertEquals(adminIdentity, adminUser.getIdentity()); assertEquals(adminIdentity, adminUser.getIdentity());
final Set<AccessPolicy> policies = authorizer.getAccessPolicies(); final Set<AccessPolicy> policies = authorizer.getAccessPolicies();
assertEquals(4, policies.size()); assertEquals(3, policies.size());
final String rootGroupResource = ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID; final String rootGroupResource = ResourceType.ProcessGroup.getValue() + "/" + ROOT_GROUP_ID;

View File

@ -17,9 +17,7 @@
package org.apache.nifi.authorization.resource; package org.apache.nifi.authorization.resource;
import org.apache.nifi.authorization.AccessPolicy; import org.apache.nifi.authorization.AccessPolicy;
import org.apache.nifi.authorization.Group;
import org.apache.nifi.authorization.Resource; import org.apache.nifi.authorization.Resource;
import org.apache.nifi.authorization.User;
import java.util.Objects; import java.util.Objects;
@ -85,18 +83,6 @@ public final class ResourceFactory {
} }
}; };
private final static Resource GROUP_RESOURCE = new Resource() {
@Override
public String getIdentifier() {
return ResourceType.Group.getValue();
}
@Override
public String getName() {
return "Group";
}
};
private final static Resource INPUT_PORT_RESOURCE = new Resource() { private final static Resource INPUT_PORT_RESOURCE = new Resource() {
@Override @Override
public String getIdentifier() { public String getIdentifier() {
@ -277,6 +263,18 @@ public final class ResourceFactory {
} }
}; };
private final static Resource TENANT_RESOURCE = new Resource() {
@Override
public String getIdentifier() {
return ResourceType.Tenant.getValue();
}
@Override
public String getName() {
return "Tenant";
}
};
private final static Resource TOKEN_RESOURCE = new Resource() { private final static Resource TOKEN_RESOURCE = new Resource() {
@Override @Override
public String getIdentifier() { public String getIdentifier() {
@ -289,18 +287,6 @@ public final class ResourceFactory {
} }
}; };
private final static Resource USER_RESOURCE = new Resource() {
@Override
public String getIdentifier() {
return ResourceType.User.getValue();
}
@Override
public String getName() {
return "User";
}
};
private final static Resource POLICIES_RESOURCE = new Resource() { private final static Resource POLICIES_RESOURCE = new Resource() {
@Override @Override
@ -314,32 +300,6 @@ public final class ResourceFactory {
} }
}; };
private final static Resource USERS_RESOURCE = new Resource() {
@Override
public String getIdentifier() {
return "/users";
}
@Override
public String getName() {
return "Users";
}
};
private final static Resource USERGROUPS_RESOURCE = new Resource() {
@Override
public String getIdentifier() {
return "/user-groups";
}
@Override
public String getName() {
return "User Groups";
}
};
/** /**
* Gets the Resource for accessing Connections. * Gets the Resource for accessing Connections.
* *
@ -385,15 +345,6 @@ public final class ResourceFactory {
return FLOW_RESOURCE; return FLOW_RESOURCE;
} }
/**
* Gets the Resource for accessing Groups which allows management of user groups.
*
* @return The resource for accessing Groups
*/
public static Resource getGroupResource() {
return GROUP_RESOURCE;
}
/** /**
* Gets the Resource for accessing Input Ports. * Gets the Resource for accessing Input Ports.
* *
@ -541,12 +492,12 @@ public final class ResourceFactory {
} }
/** /**
* Gets the Resource for accessing Users which includes creating, modifying, and deleting Users. * Gets the Resource for accessing Tenants which includes creating, modifying, and deleting Users and UserGroups.
* *
* @return The Resource for accessing Users * @return The Resource for accessing Tenants
*/ */
public static Resource getUserResource() { public static Resource getTenantResource() {
return USER_RESOURCE; return TENANT_RESOURCE;
} }
/** /**
@ -602,24 +553,6 @@ public final class ResourceFactory {
}; };
} }
/**
* Gets a Resource for accessing {@link User} configurations.
*
* @return The resource
*/
public static Resource getUsersResource() {
return USERS_RESOURCE;
}
/**
* Gets a Resource for accessing {@link Group}s configuration.
*
* @return The resource
*/
public static Resource getUserGroupsResource() {
return USERGROUPS_RESOURCE;
}
/** /**
* Gets a Resource for accessing a component configuration. * Gets a Resource for accessing a component configuration.
* *

View File

@ -20,9 +20,9 @@ public enum ResourceType {
Connection("/connections"), Connection("/connections"),
Controller("/controller"), Controller("/controller"),
ControllerService("/controller-services"), ControllerService("/controller-services"),
Counters("/counters"),
Funnel("/funnel"), Funnel("/funnel"),
Flow("/flow"), Flow("/flow"),
Group("/groups"),
InputPort("/input-ports"), InputPort("/input-ports"),
Label("/labels"), Label("/labels"),
OutputPort("/output-ports"), OutputPort("/output-ports"),
@ -37,9 +37,8 @@ public enum ResourceType {
SiteToSite("/site-to-site"), SiteToSite("/site-to-site"),
System("/system"), System("/system"),
Template("/templates"), Template("/templates"),
Token("/token"), Tenant("/tenants"),
Counters("/counters"), Token("/token");
User("/users");
final String value; final String value;

View File

@ -18,7 +18,7 @@ package org.apache.nifi.authorization.resource;
import org.apache.nifi.authorization.Resource; import org.apache.nifi.authorization.Resource;
public class UsersAuthorizable implements Authorizable { public class TenantAuthorizable implements Authorizable {
@Override @Override
public Authorizable getParentAuthorizable() { public Authorizable getParentAuthorizable() {
@ -27,7 +27,7 @@ public class UsersAuthorizable implements Authorizable {
@Override @Override
public Resource getResource() { public Resource getResource() {
return ResourceFactory.getUsersResource(); return ResourceFactory.getTenantResource();
} }
} }

View File

@ -17,8 +17,6 @@
package org.apache.nifi.web; package org.apache.nifi.web;
import org.apache.nifi.authorization.AccessPolicy; import org.apache.nifi.authorization.AccessPolicy;
import org.apache.nifi.authorization.Group;
import org.apache.nifi.authorization.User;
import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.controller.Snippet; import org.apache.nifi.controller.Snippet;
@ -163,16 +161,10 @@ public interface AuthorizableLookup {
Snippet getSnippet(String id); Snippet getSnippet(String id);
/** /**
* Get the {@link Authorizable} that represents the resource of {@link User}s. * Get the {@link Authorizable} that represents the resource of users and user groups.
* @return authorizable * @return authorizable
*/ */
Authorizable getUsersAuthorizable(); Authorizable getTenantAuthorizable();
/**
* Get the {@link Authorizable} that represents the resource of {@link Group}s.
* @return authorizable
*/
Authorizable getUserGroupsAuthorizable();
/** /**
* Get the {@link Authorizable} the represents the parent resource of {@link AccessPolicy} resources. * Get the {@link Authorizable} the represents the parent resource of {@link AccessPolicy} resources.

View File

@ -1220,6 +1220,13 @@ public interface NiFiServiceFacade {
*/ */
UserEntity getUser(String userId, boolean prune); UserEntity getUser(String userId, boolean prune);
/**
* Gets all the users.
* @param prune If true, the users in the groups to which the users belong will not be returned
* @return The user transfer objects
*/
Set<UserEntity> getUsers(boolean prune);
/** /**
* Updates the specified user. * Updates the specified user.
* @param revision Revision to compare with current base revision * @param revision Revision to compare with current base revision
@ -1255,6 +1262,13 @@ public interface NiFiServiceFacade {
*/ */
UserGroupEntity getUserGroup(String userGroupId, boolean prune); UserGroupEntity getUserGroup(String userGroupId, boolean prune);
/**
* Gets all user groups.
* @param prune If true, the user groups of the users in the user groups will not be returned
* @return The user group transfer objects
*/
Set<UserGroupEntity> getUserGroups(boolean prune);
/** /**
* Updates the specified user group. * Updates the specified user group.
* @param revision Revision to compare with current base revision * @param revision Revision to compare with current base revision

View File

@ -19,8 +19,7 @@ package org.apache.nifi.web;
import org.apache.nifi.authorization.resource.AccessPoliciesAuthorizable; import org.apache.nifi.authorization.resource.AccessPoliciesAuthorizable;
import org.apache.nifi.authorization.resource.AccessPolicyAuthorizable; import org.apache.nifi.authorization.resource.AccessPolicyAuthorizable;
import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.authorization.resource.UserGroupsAuthorizable; import org.apache.nifi.authorization.resource.TenantAuthorizable;
import org.apache.nifi.authorization.resource.UsersAuthorizable;
import org.apache.nifi.controller.ConfiguredComponent; import org.apache.nifi.controller.ConfiguredComponent;
import org.apache.nifi.controller.Snippet; import org.apache.nifi.controller.Snippet;
import org.apache.nifi.controller.service.ControllerServiceNode; import org.apache.nifi.controller.service.ControllerServiceNode;
@ -44,8 +43,7 @@ import org.apache.nifi.web.dao.TemplateDAO;
class StandardAuthorizableLookup implements AuthorizableLookup { class StandardAuthorizableLookup implements AuthorizableLookup {
private static final UsersAuthorizable USERS_AUTHORIZABLE = new UsersAuthorizable(); private static final TenantAuthorizable TENANT_AUTHORIZABLE = new TenantAuthorizable();
private static final UserGroupsAuthorizable USER_GROUPS_AUTHORIZABLE = new UserGroupsAuthorizable();
private static final Authorizable ACCESS_POLICIES_AUTHORIZABLE = new AccessPoliciesAuthorizable(); private static final Authorizable ACCESS_POLICIES_AUTHORIZABLE = new AccessPoliciesAuthorizable();
// nifi core components // nifi core components
@ -159,13 +157,8 @@ class StandardAuthorizableLookup implements AuthorizableLookup {
} }
@Override @Override
public Authorizable getUsersAuthorizable() { public Authorizable getTenantAuthorizable() {
return USERS_AUTHORIZABLE; return TENANT_AUTHORIZABLE;
}
@Override
public Authorizable getUserGroupsAuthorizable() {
return USER_GROUPS_AUTHORIZABLE;
} }
@Override @Override

View File

@ -535,7 +535,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override @Override
public UserEntity updateUser(final Revision revision, final UserDTO userDTO) { public UserEntity updateUser(final Revision revision, final UserDTO userDTO) {
final Authorizable usersAuthorizable = authorizableLookup.getUsersAuthorizable(); final Authorizable usersAuthorizable = authorizableLookup.getTenantAuthorizable();
final RevisionUpdate<UserDTO> snapshot = updateComponent(revision, final RevisionUpdate<UserDTO> snapshot = updateComponent(revision,
usersAuthorizable, usersAuthorizable,
() -> userDAO.updateUser(userDTO), () -> userDAO.updateUser(userDTO),
@ -547,7 +547,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override @Override
public UserGroupEntity updateUserGroup(final Revision revision, final UserGroupDTO userGroupDTO) { public UserGroupEntity updateUserGroup(final Revision revision, final UserGroupDTO userGroupDTO) {
final Authorizable userGroupsAuthorizable = authorizableLookup.getUserGroupsAuthorizable(); final Authorizable userGroupsAuthorizable = authorizableLookup.getTenantAuthorizable();
final RevisionUpdate<UserGroupDTO> snapshot = updateComponent(revision, final RevisionUpdate<UserGroupDTO> snapshot = updateComponent(revision,
userGroupsAuthorizable, userGroupsAuthorizable,
() -> userGroupDAO.updateUserGroup(userGroupDTO), () -> userGroupDAO.updateUserGroup(userGroupDTO),
@ -985,7 +985,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final Set<UserGroupEntity> userGroups = user != null ? user.getGroups().stream().map(userGroupId -> getUserGroup(userGroupId, true)).collect(Collectors.toSet()) : null; final Set<UserGroupEntity> userGroups = user != null ? user.getGroups().stream().map(userGroupId -> getUserGroup(userGroupId, true)).collect(Collectors.toSet()) : null;
final UserDTO snapshot = deleteComponent( final UserDTO snapshot = deleteComponent(
revision, revision,
authorizableLookup.getUsersAuthorizable(), authorizableLookup.getTenantAuthorizable(),
() -> userDAO.deleteUser(userId), () -> userDAO.deleteUser(userId),
dtoFactory.createUserDto(user, userGroups)); dtoFactory.createUserDto(user, userGroups));
@ -999,7 +999,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
null; null;
final UserGroupDTO snapshot = deleteComponent( final UserGroupDTO snapshot = deleteComponent(
revision, revision,
authorizableLookup.getUserGroupsAuthorizable(), authorizableLookup.getTenantAuthorizable(),
() -> userGroupDAO.deleteUserGroup(userGroupId), () -> userGroupDAO.deleteUserGroup(userGroupId),
dtoFactory.createUserGroupDto(userGroup, users)); dtoFactory.createUserGroupDto(userGroup, users));
@ -1283,7 +1283,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final User newUser = userDAO.createUser(userDTO); final User newUser = userDAO.createUser(userDTO);
final UserDTO newUserDto = dtoFactory.createUserDto(newUser, newUser.getGroups().stream().map(userGroupId -> getUserGroup(userGroupId, true)).collect(Collectors.toSet())); final UserDTO newUserDto = dtoFactory.createUserDto(newUser, newUser.getGroups().stream().map(userGroupId -> getUserGroup(userGroupId, true)).collect(Collectors.toSet()));
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(authorizableLookup.getUsersAuthorizable()); final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable());
return entityFactory.createUserEntity(newUserDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), accessPolicy); return entityFactory.createUserEntity(newUserDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), accessPolicy);
} }
@ -1296,7 +1296,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
final Group newUserGroup = userGroupDAO.createUserGroup(userGroupDTO); final Group newUserGroup = userGroupDAO.createUserGroup(userGroupDTO);
final UserGroupDTO newUserGroupDto = dtoFactory.createUserGroupDto(newUserGroup, newUserGroup.getUsers().stream().map(userId -> getUser(userId, true)).collect(Collectors.toSet())); final UserGroupDTO newUserGroupDto = dtoFactory.createUserGroupDto(newUserGroup, newUserGroup.getUsers().stream().map(userId -> getUser(userId, true)).collect(Collectors.toSet()));
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(authorizableLookup.getUserGroupsAuthorizable()); final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(authorizableLookup.getTenantAuthorizable());
return entityFactory.createUserGroupEntity(newUserGroupDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), accessPolicy); return entityFactory.createUserGroupEntity(newUserGroupDto, dtoFactory.createRevisionDTO(new FlowModification(revision, creator)), accessPolicy);
} }
@ -2354,7 +2354,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override @Override
public UserEntity getUser(final String userId, final boolean prune) { public UserEntity getUser(final String userId, final boolean prune) {
return revisionManager.get(userId, rev -> { return revisionManager.get(userId, rev -> {
final Authorizable usersAuthorizable = authorizableLookup.getUsersAuthorizable(); final Authorizable usersAuthorizable = authorizableLookup.getTenantAuthorizable();
final RevisionDTO revision = dtoFactory.createRevisionDTO(rev); final RevisionDTO revision = dtoFactory.createRevisionDTO(rev);
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(usersAuthorizable); final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(usersAuthorizable);
final User user = userDAO.getUser(userId); final User user = userDAO.getUser(userId);
@ -2367,7 +2367,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
private UserEntity getUserPruned(final String userId) { private UserEntity getUserPruned(final String userId) {
return revisionManager.get(userId, rev -> { return revisionManager.get(userId, rev -> {
final Authorizable usersAuthorizable = authorizableLookup.getUsersAuthorizable(); final Authorizable usersAuthorizable = authorizableLookup.getTenantAuthorizable();
final RevisionDTO revision = dtoFactory.createRevisionDTO(rev); final RevisionDTO revision = dtoFactory.createRevisionDTO(rev);
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(usersAuthorizable); final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(usersAuthorizable);
final User user = userDAO.getUser(userId); final User user = userDAO.getUser(userId);
@ -2375,10 +2375,29 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
}); });
} }
@Override
public Set<UserEntity> getUsers(boolean prune) {
final Authorizable userAuthorizable = authorizableLookup.getTenantAuthorizable();
final Set<User> users = userDAO.getUsers();
final Set<String> ids = users.stream().map(user -> user.getIdentifier()).collect(Collectors.toSet());
return revisionManager.get(ids, () -> {
return users.stream()
.map(user -> {
final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(user.getIdentifier()));
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(userAuthorizable);
final Set<UserGroupEntity> userGroups = user.getGroups().stream()
.map(userGroupId -> prune ? getUserGroupPruned(userGroupId) : getUserGroup(userGroupId, false))
.collect(Collectors.toSet());
return entityFactory.createUserEntity(dtoFactory.createUserDto(user, userGroups), revision, accessPolicy);
})
.collect(Collectors.toSet());
});
}
@Override @Override
public UserGroupEntity getUserGroup(final String userGroupId, final boolean prune) { public UserGroupEntity getUserGroup(final String userGroupId, final boolean prune) {
return revisionManager.get(userGroupId, rev -> { return revisionManager.get(userGroupId, rev -> {
final Authorizable userGroupsAuthorizable = authorizableLookup.getUserGroupsAuthorizable(); final Authorizable userGroupsAuthorizable = authorizableLookup.getTenantAuthorizable();
final RevisionDTO revision = dtoFactory.createRevisionDTO(rev); final RevisionDTO revision = dtoFactory.createRevisionDTO(rev);
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(userGroupsAuthorizable); final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(userGroupsAuthorizable);
final Group userGroup = userGroupDAO.getUserGroup(userGroupId); final Group userGroup = userGroupDAO.getUserGroup(userGroupId);
@ -2390,7 +2409,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
private UserGroupEntity getUserGroupPruned(final String userGroupId) { private UserGroupEntity getUserGroupPruned(final String userGroupId) {
return revisionManager.get(userGroupId, rev -> { return revisionManager.get(userGroupId, rev -> {
final Authorizable userGroupsAuthorizable = authorizableLookup.getUserGroupsAuthorizable(); final Authorizable userGroupsAuthorizable = authorizableLookup.getTenantAuthorizable();
final RevisionDTO revision = dtoFactory.createRevisionDTO(rev); final RevisionDTO revision = dtoFactory.createRevisionDTO(rev);
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(userGroupsAuthorizable); final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(userGroupsAuthorizable);
final Group userGroup = userGroupDAO.getUserGroup(userGroupId); final Group userGroup = userGroupDAO.getUserGroup(userGroupId);
@ -2398,6 +2417,25 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
}); });
} }
@Override
public Set<UserGroupEntity> getUserGroups(boolean prune) {
final Authorizable userGroupAuthorizable = authorizableLookup.getTenantAuthorizable();
final Set<Group> userGroups = userGroupDAO.getUserGroups();
final Set<String> ids = userGroups.stream().map(userGroup -> userGroup.getIdentifier()).collect(Collectors.toSet());
return revisionManager.get(ids, () -> {
return userGroups.stream()
.map(userGroup -> {
final RevisionDTO revision = dtoFactory.createRevisionDTO(revisionManager.getRevision(userGroup.getIdentifier()));
final AccessPolicyDTO accessPolicy = dtoFactory.createAccessPolicyDto(userGroupAuthorizable);
final Set<UserEntity> users = userGroup.getUsers().stream()
.map(userGroupId -> prune ? getUserPruned(userGroupId) : getUser(userGroupId, false))
.collect(Collectors.toSet());
return entityFactory.createUserGroupEntity(dtoFactory.createUserGroupDto(userGroup, users), revision, accessPolicy);
})
.collect(Collectors.toSet());
});
}
@Override @Override
public Set<LabelEntity> getLabels(final String groupId) { public Set<LabelEntity> getLabels(final String groupId) {
final Set<Label> labels = labelDAO.getLabels(groupId); final Set<Label> labels = labelDAO.getLabels(groupId);

View File

@ -0,0 +1,848 @@
/*
* 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 org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.NiFiServiceFacade;
import org.apache.nifi.web.Revision;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.dto.UserDTO;
import org.apache.nifi.web.api.dto.UserGroupDTO;
import org.apache.nifi.web.api.entity.UserEntity;
import org.apache.nifi.web.api.entity.UserGroupEntity;
import org.apache.nifi.web.api.entity.UserGroupsEntity;
import org.apache.nifi.web.api.entity.UsersEntity;
import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.nifi.web.api.request.LongParameter;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
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.Response;
import java.net.URI;
import java.util.Set;
@Path("tenants")
@Api(
value = "tenants",
description = "Endpoint for managing users and user groups."
)
public class TenantsResource extends ApplicationResource {
private final NiFiServiceFacade serviceFacade;
private final Authorizer authorizer;
public TenantsResource(NiFiServiceFacade serviceFacade, Authorizer authorizer, NiFiProperties properties, RequestReplicator requestReplicator, ClusterCoordinator clusterCoordinator) {
this.serviceFacade = serviceFacade;
this.authorizer = authorizer;
setProperties(properties);
setRequestReplicator(requestReplicator);
setClusterCoordinator(clusterCoordinator);
}
/**
* Populates the uri for the specified users.
*
* @param userEntities users
* @return user entities
*/
public Set<UserEntity> populateRemainingUserEntitiesContent(Set<UserEntity> userEntities) {
for (UserEntity userEntity : userEntities) {
populateRemainingUserEntityContent(userEntity);
}
return userEntities;
}
/**
* Populates the uri for the specified user.
*
* @param userEntity userEntity
* @return userEntity
*/
public UserEntity populateRemainingUserEntityContent(UserEntity userEntity) {
if (userEntity.getComponent() != null) {
populateRemainingUserContent(userEntity.getComponent());
}
return userEntity;
}
/**
* Populates the uri for the specified user.
*/
public UserDTO populateRemainingUserContent(UserDTO user) {
// populate the user href
user.setUri(generateResourceUri("tenants/users", user.getId()));
return user;
}
/**
* Populates the uri for the specified users.
*
* @param users users
* @return user data transfer objects
*/
public Set<UserDTO> populateRemainingUsersContent(Set<UserDTO> users) {
for (UserDTO userDTO : users) {
populateRemainingUserContent(userDTO);
}
return users;
}
/**
* Creates a new user.
*
* @param httpServletRequest request
* @param userEntity An userEntity.
* @return An userEntity.
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("users")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Creates a user",
response = UserEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 createUser(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The user configuration details.",
required = true
) final UserEntity userEntity) {
if (userEntity == null || userEntity.getComponent() == null) {
throw new IllegalArgumentException("User details must be specified.");
}
if (userEntity.getRevision() == null || (userEntity.getRevision().getVersion() == null || userEntity.getRevision().getVersion() != 0)) {
throw new IllegalArgumentException("A revision of 0 must be specified when creating a new Processor.");
}
if (userEntity.getComponent().getId() != null) {
throw new IllegalArgumentException("User ID cannot be specified.");
}
if (isReplicateRequest()) {
return replicate(HttpMethod.POST, userEntity);
}
// handle expects request (usually from the cluster manager)
final boolean validationPhase = isValidationPhase(httpServletRequest);
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable users = lookup.getTenantAuthorizable();
users.authorize(authorizer, RequestAction.WRITE);
});
}
if (validationPhase) {
return generateContinueResponse().build();
}
// set the user id as appropriate
userEntity.getComponent().setId(generateUuid());
// get revision from the config
final RevisionDTO revisionDTO = userEntity.getRevision();
Revision revision = new Revision(revisionDTO.getVersion(), revisionDTO.getClientId(), userEntity.getComponent().getId());
// create the user and generate the json
final UserEntity entity = serviceFacade.createUser(revision, userEntity.getComponent());
populateRemainingUserEntityContent(entity);
// build the response
return clusterContext(generateCreatedResponse(URI.create(entity.getComponent().getUri()), entity)).build();
}
/**
* Retrieves the specified user.
*
* @param id The id of the user to retrieve
* @return An userEntity.
*/
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("users/{id}")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
@ApiOperation(
value = "Gets a user",
response = UserEntity.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 getUser(
@ApiParam(
value = "The user id.",
required = true
)
@PathParam("id") final String id) {
if (isReplicateRequest()) {
return replicate(HttpMethod.GET);
}
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable users = lookup.getTenantAuthorizable();
users.authorize(authorizer, RequestAction.READ);
});
// get the user
final UserEntity entity = serviceFacade.getUser(id, true);
populateRemainingUserEntityContent(entity);
return clusterContext(generateOkResponse(entity)).build();
}
/**
* Retrieves all the of users in this NiFi.
*
* @return A UsersEntity.
*/
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("users")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
@ApiOperation(
value = "Gets all users",
response = UsersEntity.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 getUsers() {
if (isReplicateRequest()) {
return replicate(HttpMethod.GET);
}
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable users = lookup.getTenantAuthorizable();
users.authorize(authorizer, RequestAction.READ);
});
// get all the users
final Set<UserEntity> users = serviceFacade.getUsers(true);
// create the response entity
final UsersEntity entity = new UsersEntity();
entity.setUsers(populateRemainingUserEntitiesContent(users));
// generate the response
return clusterContext(generateOkResponse(entity)).build();
}
/**
* Updates a user.
*
* @param httpServletRequest request
* @param id The id of the user to update.
* @param userEntity An userEntity.
* @return An userEntity.
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("users/{id}")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Updates a user",
response = UserEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The user id.",
required = true
)
@PathParam("id") final String id,
@ApiParam(
value = "The user configuration details.",
required = true
) final UserEntity userEntity) {
if (userEntity == null || userEntity.getComponent() == null) {
throw new IllegalArgumentException("User details must be specified.");
}
if (userEntity.getRevision() == null) {
throw new IllegalArgumentException("Revision must be specified.");
}
// ensure the ids are the same
final UserDTO userDTO = userEntity.getComponent();
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));
}
if (isReplicateRequest()) {
return replicate(HttpMethod.PUT, userEntity);
}
// Extract the revision
final Revision revision = getRevision(userEntity, id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
final Authorizable users = lookup.getTenantAuthorizable();
users.authorize(authorizer, RequestAction.WRITE);
},
null,
() -> {
// update the user
final UserEntity entity = serviceFacade.updateUser(revision, userDTO);
populateRemainingUserEntityContent(entity);
return clusterContext(generateOkResponse(entity)).build();
}
);
}
/**
* Removes the specified user.
*
* @param httpServletRequest request
* @param version The revision is used to verify the client is working with
* the latest version of the flow.
* @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 remove.
* @return A entity containing the client id and an updated revision.
*/
@DELETE
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("users/{id}")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Deletes a user",
response = UserEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 removeUser(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The revision is used to verify the client is working with the latest version of the flow.",
required = false
)
@QueryParam(VERSION) final LongParameter version,
@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) final ClientIdParameter clientId,
@ApiParam(
value = "The user id.",
required = true
)
@PathParam("id") final String id) {
if (isReplicateRequest()) {
return replicate(HttpMethod.DELETE);
}
// handle expects request (usually from the cluster manager)
final Revision revision = new Revision(version == null ? null : version.getLong(), clientId.getClientId(), id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
final Authorizable users = lookup.getTenantAuthorizable();
users.authorize(authorizer, RequestAction.READ);
},
null,
() -> {
// delete the specified user
final UserEntity entity = serviceFacade.deleteUser(revision, id);
return clusterContext(generateOkResponse(entity)).build();
}
);
}
/**
* Populates the uri for the specified user groups.
*
* @param userGroupEntities user groups
* @return user group entities
*/
public Set<UserGroupEntity> populateRemainingUserGroupEntitiesContent(Set<UserGroupEntity> userGroupEntities) {
for (UserGroupEntity userGroupEntity : userGroupEntities) {
populateRemainingUserGroupEntityContent(userGroupEntity);
}
return userGroupEntities;
}
/**
* Populates the uri for the specified user group.
*
* @param userGroupEntity userGroupEntity
* @return userGroupEntity
*/
public UserGroupEntity populateRemainingUserGroupEntityContent(UserGroupEntity userGroupEntity) {
if (userGroupEntity.getComponent() != null) {
populateRemainingUserGroupContent(userGroupEntity.getComponent());
}
return userGroupEntity;
}
/**
* Populates the uri for the specified userGroup.
*/
public UserGroupDTO populateRemainingUserGroupContent(UserGroupDTO userGroup) {
// populate the user group href
userGroup.setUri(generateResourceUri("tenants/user-groups", userGroup.getId()));
return userGroup;
}
/**
* Populates the uri for the specified user groups.
*
* @param userGroups user groups
* @return user group data transfer objects
*/
public Set<UserGroupDTO> populateRemainingUserGroupsContent(Set<UserGroupDTO> userGroups) {
for (UserGroupDTO userGroup : userGroups) {
populateRemainingUserGroupContent(userGroup);
}
return userGroups;
}
/**
* Creates a new user group.
*
* @param httpServletRequest request
* @param userGroupEntity An userGroupEntity.
* @return An userGroupEntity.
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("user-groups")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Creates a user group",
response = UserGroupEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 createUserGroup(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The user group configuration details.",
required = true
) final UserGroupEntity userGroupEntity) {
if (userGroupEntity == null || userGroupEntity.getComponent() == null) {
throw new IllegalArgumentException("User group details must be specified.");
}
if (userGroupEntity.getRevision() == null || (userGroupEntity.getRevision().getVersion() == null || userGroupEntity.getRevision().getVersion() != 0)) {
throw new IllegalArgumentException("A revision of 0 must be specified when creating a new Processor.");
}
if (userGroupEntity.getComponent().getId() != null) {
throw new IllegalArgumentException("User group ID cannot be specified.");
}
if (isReplicateRequest()) {
return replicate(HttpMethod.POST, userGroupEntity);
}
// handle expects request (usually from the cluster manager)
final boolean validationPhase = isValidationPhase(httpServletRequest);
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable userGroups = lookup.getTenantAuthorizable();
userGroups.authorize(authorizer, RequestAction.WRITE);
});
}
if (validationPhase) {
return generateContinueResponse().build();
}
// set the user group id as appropriate
userGroupEntity.getComponent().setId(generateUuid());
// get revision from the config
final RevisionDTO revisionDTO = userGroupEntity.getRevision();
Revision revision = new Revision(revisionDTO.getVersion(), revisionDTO.getClientId(), userGroupEntity.getComponent().getId());
// create the user group and generate the json
final UserGroupEntity entity = serviceFacade.createUserGroup(revision, userGroupEntity.getComponent());
populateRemainingUserGroupEntityContent(entity);
// build the response
return clusterContext(generateCreatedResponse(URI.create(entity.getComponent().getUri()), entity)).build();
}
/**
* Retrieves the specified user group.
*
* @param id The id of the user group to retrieve
* @return An userGroupEntity.
*/
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("user-groups/{id}")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
@ApiOperation(
value = "Gets a user group",
response = UserGroupEntity.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 getUserGroup(
@ApiParam(
value = "The user group id.",
required = true
)
@PathParam("id") final String id) {
if (isReplicateRequest()) {
return replicate(HttpMethod.GET);
}
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable userGroups = lookup.getTenantAuthorizable();
userGroups.authorize(authorizer, RequestAction.READ);
});
// get the user group
final UserGroupEntity entity = serviceFacade.getUserGroup(id, true);
populateRemainingUserGroupEntityContent(entity);
return clusterContext(generateOkResponse(entity)).build();
}
/**
* Retrieves all the of user groups in this NiFi.
*
* @return A UserGroupsEntity.
*/
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("user-groups")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
@ApiOperation(
value = "Gets all user groups",
response = UserGroupsEntity.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 getUserGroups() {
if (isReplicateRequest()) {
return replicate(HttpMethod.GET);
}
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable userGroups = lookup.getTenantAuthorizable();
userGroups.authorize(authorizer, RequestAction.READ);
});
// get all the user groups
final Set<UserGroupEntity> users = serviceFacade.getUserGroups(true);
// create the response entity
final UserGroupsEntity entity = new UserGroupsEntity();
entity.setUserGroups(populateRemainingUserGroupEntitiesContent(users));
// generate the response
return clusterContext(generateOkResponse(entity)).build();
}
/**
* Updates a user group.
*
* @param httpServletRequest request
* @param id The id of the user group to update.
* @param userGroupEntity An userGroupEntity.
* @return An userGroupEntity.
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("user-groups/{id}")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Updates a user group",
response = UserGroupEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The user group id.",
required = true
)
@PathParam("id") final String id,
@ApiParam(
value = "The user group configuration details.",
required = true
) final UserGroupEntity userGroupEntity) {
if (userGroupEntity == null || userGroupEntity.getComponent() == null) {
throw new IllegalArgumentException("User group details must be specified.");
}
if (userGroupEntity.getRevision() == null) {
throw new IllegalArgumentException("Revision must be specified.");
}
// ensure the ids are the same
final UserGroupDTO userGroupDTO = userGroupEntity.getComponent();
if (!id.equals(userGroupDTO.getId())) {
throw new IllegalArgumentException(String.format("The user group id (%s) in the request body does not equal the "
+ "user group id of the requested resource (%s).", userGroupDTO.getId(), id));
}
if (isReplicateRequest()) {
return replicate(HttpMethod.PUT, userGroupEntity);
}
// Extract the revision
final Revision revision = getRevision(userGroupEntity, id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
final Authorizable userGroups = lookup.getTenantAuthorizable();
userGroups.authorize(authorizer, RequestAction.WRITE);
},
null,
() -> {
// update the user group
final UserGroupEntity entity = serviceFacade.updateUserGroup(revision, userGroupDTO);
populateRemainingUserGroupEntityContent(entity);
return clusterContext(generateOkResponse(entity)).build();
}
);
}
/**
* Removes the specified user group.
*
* @param httpServletRequest request
* @param version The revision is used to verify the client is working with
* the latest version of the flow.
* @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 group to remove.
* @return A entity containing the client id and an updated revision.
*/
@DELETE
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("user-groups/{id}")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Deletes a user group",
response = UserGroupEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 removeUserGroup(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The revision is used to verify the client is working with the latest version of the flow.",
required = false
)
@QueryParam(VERSION) final LongParameter version,
@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) final ClientIdParameter clientId,
@ApiParam(
value = "The user group id.",
required = true
)
@PathParam("id") final String id) {
if (isReplicateRequest()) {
return replicate(HttpMethod.DELETE);
}
// handle expects request (usually from the cluster manager)
final Revision revision = new Revision(version == null ? null : version.getLong(), clientId.getClientId(), id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
final Authorizable userGroups = lookup.getTenantAuthorizable();
userGroups.authorize(authorizer, RequestAction.READ);
},
null,
() -> {
// delete the specified user group
final UserGroupEntity entity = serviceFacade.deleteUserGroup(revision, id);
return clusterContext(generateOkResponse(entity)).build();
}
);
}
}

View File

@ -1,381 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.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 org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.NiFiServiceFacade;
import org.apache.nifi.web.Revision;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.dto.UserGroupDTO;
import org.apache.nifi.web.api.entity.UserGroupEntity;
import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.nifi.web.api.request.LongParameter;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
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.Response;
import java.net.URI;
@Path("/user-groups")
@Api(
value = "/user-groups",
description = "Endpoint for managing user groups."
)
public class UserGroupsResource extends ApplicationResource {
private final NiFiServiceFacade serviceFacade;
private final Authorizer authorizer;
public UserGroupsResource(NiFiServiceFacade serviceFacade, Authorizer authorizer, NiFiProperties properties, RequestReplicator requestReplicator, ClusterCoordinator clusterCoordinator) {
this.serviceFacade = serviceFacade;
this.authorizer = authorizer;
setProperties(properties);
setRequestReplicator(requestReplicator);
setClusterCoordinator(clusterCoordinator);
}
/**
* Populates the uri for the specified user group.
*
* @param userGroupEntity userGroupEntity
* @return userGroupEntity
*/
public UserGroupEntity populateRemainingUserGroupEntityContent(UserGroupEntity userGroupEntity) {
if (userGroupEntity.getComponent() != null) {
populateRemainingUserGroupContent(userGroupEntity.getComponent());
}
return userGroupEntity;
}
/**
* Populates the uri for the specified userGroup.
*/
public UserGroupDTO populateRemainingUserGroupContent(UserGroupDTO userGroup) {
// populate the user group href
userGroup.setUri(generateResourceUri("user-groups", userGroup.getId()));
return userGroup;
}
/**
* Creates a new user group.
*
* @param httpServletRequest request
* @param userGroupEntity An userGroupEntity.
* @return An userGroupEntity.
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Creates a user group",
response = UserGroupEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 createUserGroup(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The user group configuration details.",
required = true
) final UserGroupEntity userGroupEntity) {
if (userGroupEntity == null || userGroupEntity.getComponent() == null) {
throw new IllegalArgumentException("User group details must be specified.");
}
if (userGroupEntity.getRevision() == null || (userGroupEntity.getRevision().getVersion() == null || userGroupEntity.getRevision().getVersion() != 0)) {
throw new IllegalArgumentException("A revision of 0 must be specified when creating a new Processor.");
}
if (userGroupEntity.getComponent().getId() != null) {
throw new IllegalArgumentException("User group ID cannot be specified.");
}
if (isReplicateRequest()) {
return replicate(HttpMethod.POST, userGroupEntity);
}
// handle expects request (usually from the cluster manager)
final boolean validationPhase = isValidationPhase(httpServletRequest);
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable userGroups = lookup.getUserGroupsAuthorizable();
userGroups.authorize(authorizer, RequestAction.WRITE);
});
}
if (validationPhase) {
return generateContinueResponse().build();
}
// set the user group id as appropriate
userGroupEntity.getComponent().setId(generateUuid());
// get revision from the config
final RevisionDTO revisionDTO = userGroupEntity.getRevision();
Revision revision = new Revision(revisionDTO.getVersion(), revisionDTO.getClientId(), userGroupEntity.getComponent().getId());
// create the user group and generate the json
final UserGroupEntity entity = serviceFacade.createUserGroup(revision, userGroupEntity.getComponent());
populateRemainingUserGroupEntityContent(entity);
// build the response
return clusterContext(generateCreatedResponse(URI.create(entity.getComponent().getUri()), entity)).build();
}
/**
* Retrieves the specified user group.
*
* @param id The id of the user group to retrieve
* @return An userGroupEntity.
*/
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
@ApiOperation(
value = "Gets a user group",
response = UserGroupEntity.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 getUserGroup(
@ApiParam(
value = "The user group id.",
required = true
)
@PathParam("id") final String id) {
if (isReplicateRequest()) {
return replicate(HttpMethod.GET);
}
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable userGroups = lookup.getUserGroupsAuthorizable();
userGroups.authorize(authorizer, RequestAction.READ);
});
// get the user group
final UserGroupEntity entity = serviceFacade.getUserGroup(id, true);
populateRemainingUserGroupEntityContent(entity);
return clusterContext(generateOkResponse(entity)).build();
}
/**
* Updates a user group.
*
* @param httpServletRequest request
* @param id The id of the user group to update.
* @param userGroupEntity An userGroupEntity.
* @return An userGroupEntity.
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Updates a user group",
response = UserGroupEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The user group id.",
required = true
)
@PathParam("id") final String id,
@ApiParam(
value = "The user group configuration details.",
required = true
) final UserGroupEntity userGroupEntity) {
if (userGroupEntity == null || userGroupEntity.getComponent() == null) {
throw new IllegalArgumentException("User group details must be specified.");
}
if (userGroupEntity.getRevision() == null) {
throw new IllegalArgumentException("Revision must be specified.");
}
// ensure the ids are the same
final UserGroupDTO userGroupDTO = userGroupEntity.getComponent();
if (!id.equals(userGroupDTO.getId())) {
throw new IllegalArgumentException(String.format("The user group id (%s) in the request body does not equal the "
+ "user group id of the requested resource (%s).", userGroupDTO.getId(), id));
}
if (isReplicateRequest()) {
return replicate(HttpMethod.PUT, userGroupEntity);
}
// Extract the revision
final Revision revision = getRevision(userGroupEntity, id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
final Authorizable userGroups = lookup.getUserGroupsAuthorizable();
userGroups.authorize(authorizer, RequestAction.WRITE);
},
null,
() -> {
// update the user group
final UserGroupEntity entity = serviceFacade.updateUserGroup(revision, userGroupDTO);
populateRemainingUserGroupEntityContent(entity);
return clusterContext(generateOkResponse(entity)).build();
}
);
}
/**
* Removes the specified user group.
*
* @param httpServletRequest request
* @param version The revision is used to verify the client is working with
* the latest version of the flow.
* @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 group to remove.
* @return A entity containing the client id and an updated revision.
*/
@DELETE
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Deletes a user group",
response = UserGroupEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 removeUserGroup(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The revision is used to verify the client is working with the latest version of the flow.",
required = false
)
@QueryParam(VERSION) final LongParameter version,
@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) final ClientIdParameter clientId,
@ApiParam(
value = "The user group id.",
required = true
)
@PathParam("id") final String id) {
if (isReplicateRequest()) {
return replicate(HttpMethod.DELETE);
}
// handle expects request (usually from the cluster manager)
final Revision revision = new Revision(version == null ? null : version.getLong(), clientId.getClientId(), id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
final Authorizable userGroups = lookup.getUserGroupsAuthorizable();
userGroups.authorize(authorizer, RequestAction.READ);
},
() -> {
},
() -> {
// delete the specified user group
final UserGroupEntity entity = serviceFacade.deleteUserGroup(revision, id);
return clusterContext(generateOkResponse(entity)).build();
}
);
}
}

View File

@ -1,381 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.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 org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.cluster.coordination.ClusterCoordinator;
import org.apache.nifi.cluster.coordination.http.replication.RequestReplicator;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.web.NiFiServiceFacade;
import org.apache.nifi.web.Revision;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.dto.UserDTO;
import org.apache.nifi.web.api.entity.UserEntity;
import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.nifi.web.api.request.LongParameter;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
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.Response;
import java.net.URI;
@Path("/users")
@Api(
value = "/users",
description = "Endpoint for managing users."
)
public class UsersResource extends ApplicationResource {
private final NiFiServiceFacade serviceFacade;
private final Authorizer authorizer;
public UsersResource(NiFiServiceFacade serviceFacade, Authorizer authorizer, NiFiProperties properties, RequestReplicator requestReplicator, ClusterCoordinator clusterCoordinator) {
this.serviceFacade = serviceFacade;
this.authorizer = authorizer;
setProperties(properties);
setRequestReplicator(requestReplicator);
setClusterCoordinator(clusterCoordinator);
}
/**
* Populates the uri for the specified user.
*
* @param userEntity userEntity
* @return userEntity
*/
public UserEntity populateRemainingUserEntityContent(UserEntity userEntity) {
if (userEntity.getComponent() != null) {
populateRemainingUserContent(userEntity.getComponent());
}
return userEntity;
}
/**
* Populates the uri for the specified user.
*/
public UserDTO populateRemainingUserContent(UserDTO user) {
// populate the user href
user.setUri(generateResourceUri("users", user.getId()));
return user;
}
/**
* Creates a new user.
*
* @param httpServletRequest request
* @param userEntity An userEntity.
* @return An userEntity.
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Creates a user",
response = UserEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 createUser(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The user configuration details.",
required = true
) final UserEntity userEntity) {
if (userEntity == null || userEntity.getComponent() == null) {
throw new IllegalArgumentException("User details must be specified.");
}
if (userEntity.getRevision() == null || (userEntity.getRevision().getVersion() == null || userEntity.getRevision().getVersion() != 0)) {
throw new IllegalArgumentException("A revision of 0 must be specified when creating a new Processor.");
}
if (userEntity.getComponent().getId() != null) {
throw new IllegalArgumentException("User ID cannot be specified.");
}
if (isReplicateRequest()) {
return replicate(HttpMethod.POST, userEntity);
}
// handle expects request (usually from the cluster manager)
final boolean validationPhase = isValidationPhase(httpServletRequest);
if (validationPhase || !isTwoPhaseRequest(httpServletRequest)) {
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable users = lookup.getUsersAuthorizable();
users.authorize(authorizer, RequestAction.WRITE);
});
}
if (validationPhase) {
return generateContinueResponse().build();
}
// set the user id as appropriate
userEntity.getComponent().setId(generateUuid());
// get revision from the config
final RevisionDTO revisionDTO = userEntity.getRevision();
Revision revision = new Revision(revisionDTO.getVersion(), revisionDTO.getClientId(), userEntity.getComponent().getId());
// create the user and generate the json
final UserEntity entity = serviceFacade.createUser(revision, userEntity.getComponent());
populateRemainingUserEntityContent(entity);
// build the response
return clusterContext(generateCreatedResponse(URI.create(entity.getComponent().getUri()), entity)).build();
}
/**
* Retrieves the specified user.
*
* @param id The id of the user to retrieve
* @return An userEntity.
*/
@GET
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
// TODO - @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
@ApiOperation(
value = "Gets a user",
response = UserEntity.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 getUser(
@ApiParam(
value = "The user id.",
required = true
)
@PathParam("id") final String id) {
if (isReplicateRequest()) {
return replicate(HttpMethod.GET);
}
// authorize access
serviceFacade.authorizeAccess(lookup -> {
final Authorizable users = lookup.getUsersAuthorizable();
users.authorize(authorizer, RequestAction.READ);
});
// get the user
final UserEntity entity = serviceFacade.getUser(id, true);
populateRemainingUserEntityContent(entity);
return clusterContext(generateOkResponse(entity)).build();
}
/**
* Updates a user.
*
* @param httpServletRequest request
* @param id The id of the user to update.
* @param userEntity An userEntity.
* @return An userEntity.
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Updates a user",
response = UserEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The user id.",
required = true
)
@PathParam("id") final String id,
@ApiParam(
value = "The user configuration details.",
required = true
) final UserEntity userEntity) {
if (userEntity == null || userEntity.getComponent() == null) {
throw new IllegalArgumentException("User details must be specified.");
}
if (userEntity.getRevision() == null) {
throw new IllegalArgumentException("Revision must be specified.");
}
// ensure the ids are the same
final UserDTO userDTO = userEntity.getComponent();
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));
}
if (isReplicateRequest()) {
return replicate(HttpMethod.PUT, userEntity);
}
// Extract the revision
final Revision revision = getRevision(userEntity, id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
final Authorizable users = lookup.getUsersAuthorizable();
users.authorize(authorizer, RequestAction.WRITE);
},
null,
() -> {
// update the user
final UserEntity entity = serviceFacade.updateUser(revision, userDTO);
populateRemainingUserEntityContent(entity);
return clusterContext(generateOkResponse(entity)).build();
}
);
}
/**
* Removes the specified user.
*
* @param httpServletRequest request
* @param version The revision is used to verify the client is working with
* the latest version of the flow.
* @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 remove.
* @return A entity containing the client id and an updated revision.
*/
@DELETE
@Consumes(MediaType.WILDCARD)
@Produces(MediaType.APPLICATION_JSON)
@Path("{id}")
// TODO - @PreAuthorize("hasRole('ROLE_DFM')")
@ApiOperation(
value = "Deletes a user",
response = UserEntity.class,
authorizations = {
@Authorization(value = "Data Flow Manager", type = "ROLE_DFM")
}
)
@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 removeUser(
@Context final HttpServletRequest httpServletRequest,
@ApiParam(
value = "The revision is used to verify the client is working with the latest version of the flow.",
required = false
)
@QueryParam(VERSION) final LongParameter version,
@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) final ClientIdParameter clientId,
@ApiParam(
value = "The user id.",
required = true
)
@PathParam("id") final String id) {
if (isReplicateRequest()) {
return replicate(HttpMethod.DELETE);
}
// handle expects request (usually from the cluster manager)
final Revision revision = new Revision(version == null ? null : version.getLong(), clientId.getClientId(), id);
return withWriteLock(
serviceFacade,
revision,
lookup -> {
final Authorizable users = lookup.getUsersAuthorizable();
users.authorize(authorizer, RequestAction.READ);
},
() -> {
},
() -> {
// delete the specified user
final UserEntity entity = serviceFacade.deleteUser(revision, id);
return clusterContext(generateOkResponse(entity)).build();
}
);
}
}

View File

@ -0,0 +1,44 @@
/*
* 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 org.apache.nifi.authorization.exception.AuthorizationAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
/**
* Maps authorization access exceptions into client responses.
*/
@Provider
public class AuthorizationAccessExceptionMapper implements ExceptionMapper<AuthorizationAccessException> {
private static final Logger logger = LoggerFactory.getLogger(AdministrationExceptionMapper.class);
@Override
public Response toResponse(AuthorizationAccessException e) {
// log the error
logger.error(String.format("%s. Returning %s response.", e, Response.Status.INTERNAL_SERVER_ERROR), e);
// generate the response
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).type("text/plain").build();
}
}

View File

@ -698,7 +698,7 @@ public final class DtoFactory {
final UserDTO dto = new UserDTO(); final UserDTO dto = new UserDTO();
dto.setId(user.getIdentifier()); dto.setId(user.getIdentifier());
dto.setGroups(groups); dto.setUserGroups(groups);
dto.setIdentity(user.getIdentity()); dto.setIdentity(user.getIdentity());
return dto; return dto;

View File

@ -19,6 +19,8 @@ package org.apache.nifi.web.dao;
import org.apache.nifi.authorization.User; import org.apache.nifi.authorization.User;
import org.apache.nifi.web.api.dto.UserDTO; import org.apache.nifi.web.api.dto.UserDTO;
import java.util.Set;
public interface UserDAO { public interface UserDAO {
/** /**
@ -43,6 +45,13 @@ public interface UserDAO {
*/ */
User getUser(String userId); User getUser(String userId);
/**
* Gets all users.
*
* @return The user transfer objects
*/
Set<User> getUsers();
/** /**
* Updates the specified user. * Updates the specified user.
* *

View File

@ -19,6 +19,8 @@ package org.apache.nifi.web.dao;
import org.apache.nifi.authorization.Group; import org.apache.nifi.authorization.Group;
import org.apache.nifi.web.api.dto.UserGroupDTO; import org.apache.nifi.web.api.dto.UserGroupDTO;
import java.util.Set;
public interface UserGroupDAO { public interface UserGroupDAO {
/** /**
@ -43,6 +45,13 @@ public interface UserGroupDAO {
*/ */
Group getUserGroup(String userGroupId); Group getUserGroup(String userGroupId);
/**
* Gets all user groups.
*
* @return The user group transfer objects
*/
Set<Group> getUserGroups();
/** /**
* Updates the specified user group. * Updates the specified user group.
* *

View File

@ -28,10 +28,13 @@ import org.apache.nifi.authorization.UsersAndAccessPolicies;
import org.apache.nifi.authorization.exception.AuthorizationAccessException; import org.apache.nifi.authorization.exception.AuthorizationAccessException;
import org.apache.nifi.authorization.exception.AuthorizerCreationException; import org.apache.nifi.authorization.exception.AuthorizerCreationException;
import org.apache.nifi.authorization.exception.AuthorizerDestructionException; import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
import org.apache.nifi.web.ResourceNotFoundException;
import org.apache.nifi.web.api.dto.AccessPolicyDTO; import org.apache.nifi.web.api.dto.AccessPolicyDTO;
import org.apache.nifi.web.api.dto.UserDTO; import org.apache.nifi.web.api.dto.UserDTO;
import org.apache.nifi.web.api.dto.UserGroupDTO; import org.apache.nifi.web.api.dto.UserGroupDTO;
import org.apache.nifi.web.api.entity.ComponentEntity; import org.apache.nifi.web.api.entity.ComponentEntity;
import org.apache.nifi.web.api.entity.UserEntity;
import org.apache.nifi.web.api.entity.UserGroupEntity;
import org.apache.nifi.web.dao.AccessPolicyDAO; import org.apache.nifi.web.dao.AccessPolicyDAO;
import org.apache.nifi.web.dao.UserDAO; import org.apache.nifi.web.dao.UserDAO;
import org.apache.nifi.web.dao.UserGroupDAO; import org.apache.nifi.web.dao.UserGroupDAO;
@ -41,7 +44,7 @@ import java.util.stream.Collectors;
public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGroupDAO, UserDAO { public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGroupDAO, UserDAO {
private static final String MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER = "authorizer is not of type AbstractPolicyBasedAuthorizer"; static final String MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER = "This NiFi is not configured to internally manage users, groups, and policies. Please contact your system administrator.";
private final AbstractPolicyBasedAuthorizer authorizer; private final AbstractPolicyBasedAuthorizer authorizer;
public StandardPolicyBasedAuthorizerDAO(final Authorizer authorizer) { public StandardPolicyBasedAuthorizerDAO(final Authorizer authorizer) {
@ -156,34 +159,44 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
@Override @Override
public AccessPolicy createAccessPolicy(final AccessPolicyDTO accessPolicyDTO) { public AccessPolicy createAccessPolicy(final AccessPolicyDTO accessPolicyDTO) {
return authorizer.addAccessPolicy(buildAccessPolicy(accessPolicyDTO)); return authorizer.addAccessPolicy(buildAccessPolicy(accessPolicyDTO.getId(), accessPolicyDTO));
} }
@Override @Override
public AccessPolicy getAccessPolicy(final String accessPolicyId) { public AccessPolicy getAccessPolicy(final String accessPolicyId) {
return authorizer.getAccessPolicy(accessPolicyId); final AccessPolicy accessPolicy = authorizer.getAccessPolicy(accessPolicyId);
if (accessPolicy == null) {
throw new ResourceNotFoundException(String.format("Unable to find access policy with id '%s'.", accessPolicyId));
}
return accessPolicy;
} }
@Override @Override
public AccessPolicy updateAccessPolicy(final AccessPolicyDTO accessPolicyDTO) { public AccessPolicy updateAccessPolicy(final AccessPolicyDTO accessPolicyDTO) {
return authorizer.updateAccessPolicy(buildAccessPolicy(accessPolicyDTO)); return authorizer.updateAccessPolicy(buildAccessPolicy(getAccessPolicy(accessPolicyDTO.getId()).getIdentifier(), accessPolicyDTO));
} }
@Override @Override
public AccessPolicy deleteAccessPolicy(final String accessPolicyId) { public AccessPolicy deleteAccessPolicy(final String accessPolicyId) {
return authorizer.deleteAccessPolicy(authorizer.getAccessPolicy(accessPolicyId)); return authorizer.deleteAccessPolicy(getAccessPolicy(accessPolicyId));
} }
private AccessPolicy buildAccessPolicy(final AccessPolicyDTO accessPolicyDTO) { private AccessPolicy buildAccessPolicy(final String identifier, final AccessPolicyDTO accessPolicyDTO) {
final Set<UserGroupEntity> userGroups = accessPolicyDTO.getUserGroups();
final Set<UserEntity> users = accessPolicyDTO.getUsers();
final AccessPolicy.Builder builder = new AccessPolicy.Builder() final AccessPolicy.Builder builder = new AccessPolicy.Builder()
.identifier(accessPolicyDTO.getId()) .identifier(identifier)
.addGroups(accessPolicyDTO.getUserGroups().stream().map(ComponentEntity::getId).collect(Collectors.toSet()))
.addUsers(accessPolicyDTO.getUsers().stream().map(ComponentEntity::getId).collect(Collectors.toSet()))
.resource(accessPolicyDTO.getResource()); .resource(accessPolicyDTO.getResource());
if (accessPolicyDTO.getCanRead()) { if (userGroups != null) {
builder.addGroups(userGroups.stream().map(ComponentEntity::getId).collect(Collectors.toSet()));
}
if (users != null) {
builder.addUsers(users.stream().map(ComponentEntity::getId).collect(Collectors.toSet()));
}
if (Boolean.TRUE == accessPolicyDTO.getCanRead()) {
builder.addAction(RequestAction.READ); builder.addAction(RequestAction.READ);
} }
if (accessPolicyDTO.getCanWrite()) { if (Boolean.TRUE == accessPolicyDTO.getCanWrite()) {
builder.addAction(RequestAction.WRITE); builder.addAction(RequestAction.WRITE);
} }
return builder.build(); return builder.build();
@ -196,28 +209,40 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
@Override @Override
public Group createUserGroup(final UserGroupDTO userGroupDTO) { public Group createUserGroup(final UserGroupDTO userGroupDTO) {
return authorizer.addGroup(buildUserGroup(userGroupDTO)); return authorizer.addGroup(buildUserGroup(userGroupDTO.getId(), userGroupDTO));
} }
@Override @Override
public Group getUserGroup(final String userGroupId) { public Group getUserGroup(final String userGroupId) {
return authorizer.getGroup(userGroupId); final Group userGroup = authorizer.getGroup(userGroupId);
if (userGroup == null) {
throw new ResourceNotFoundException(String.format("Unable to find user group with id '%s'.", userGroupId));
}
return userGroup;
}
@Override
public Set<Group> getUserGroups() {
return authorizer.getGroups();
} }
@Override @Override
public Group updateUserGroup(final UserGroupDTO userGroupDTO) { public Group updateUserGroup(final UserGroupDTO userGroupDTO) {
return authorizer.updateGroup(buildUserGroup(userGroupDTO)); return authorizer.updateGroup(buildUserGroup(getUserGroup(userGroupDTO.getId()).getIdentifier(), userGroupDTO));
} }
@Override @Override
public Group deleteUserGroup(final String userGroupId) { public Group deleteUserGroup(final String userGroupId) {
return authorizer.deleteGroup(authorizer.getGroup(userGroupId)); return authorizer.deleteGroup(getUserGroup(userGroupId));
} }
private Group buildUserGroup(final UserGroupDTO userGroupDTO) { private Group buildUserGroup(final String identifier, final UserGroupDTO userGroupDTO) {
return new Group.Builder() final Set<UserEntity> users = userGroupDTO.getUsers();
.addUsers(userGroupDTO.getUsers().stream().map(ComponentEntity::getId).collect(Collectors.toSet())) final Group.Builder builder = new Group.Builder().identifier(identifier).name(userGroupDTO.getName());
.identifier(userGroupDTO.getId()).name(userGroupDTO.getName()).build(); if (users != null) {
builder.addUsers(users.stream().map(ComponentEntity::getId).collect(Collectors.toSet()));
}
return builder.build();
} }
@Override @Override
@ -227,29 +252,40 @@ public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGr
@Override @Override
public User createUser(final UserDTO userDTO) { public User createUser(final UserDTO userDTO) {
final User user = buildUser(userDTO); return authorizer.addUser(buildUser(userDTO.getId(), userDTO));
return authorizer.addUser(user);
} }
@Override @Override
public User getUser(final String userId) { public User getUser(final String userId) {
return authorizer.getUser(userId); final User user = authorizer.getUser(userId);
if (user == null) {
throw new ResourceNotFoundException(String.format("Unable to find user with id '%s'.", userId));
}
return user;
}
@Override
public Set<User> getUsers() {
return authorizer.getUsers();
} }
@Override @Override
public User updateUser(final UserDTO userDTO) { public User updateUser(final UserDTO userDTO) {
return authorizer.updateUser(buildUser(userDTO)); return authorizer.updateUser(buildUser(getUser(userDTO.getId()).getIdentifier(), userDTO));
} }
@Override @Override
public User deleteUser(final String userId) { public User deleteUser(final String userId) {
return authorizer.deleteUser(authorizer.getUser(userId)); return authorizer.deleteUser(getUser(userId));
} }
private User buildUser(final UserDTO userDTO) { private User buildUser(final String identifier, final UserDTO userDTO) {
return new User.Builder() final Set<UserGroupEntity> groups = userDTO.getUserGroups();
.addGroups(userDTO.getGroups().stream().map(ComponentEntity::getId).collect(Collectors.toSet())) final User.Builder builder = new User.Builder().identifier(identifier).identity(userDTO.getIdentity());
.identifier(userDTO.getIdentity()).identity(userDTO.getIdentity()).build(); if (groups != null) {
builder.addGroups(groups.stream().map(ComponentEntity::getId).collect(Collectors.toSet()));
}
return builder.build();
} }
} }

View File

@ -361,14 +361,7 @@
<constructor-arg ref="clusterCoordinator"/> <constructor-arg ref="clusterCoordinator"/>
<constructor-arg ref="requestReplicator" /> <constructor-arg ref="requestReplicator" />
</bean> </bean>
<bean id="userGroupsResource" class="org.apache.nifi.web.api.UserGroupsResource" scope="singleton"> <bean id="tenantsResource" class="org.apache.nifi.web.api.TenantsResource" scope="singleton">
<constructor-arg ref="serviceFacade"/>
<constructor-arg ref="authorizer"/>
<constructor-arg ref="nifiProperties"/>
<constructor-arg ref="clusterCoordinator"/>
<constructor-arg ref="requestReplicator" />
</bean>
<bean id="usersResource" class="org.apache.nifi.web.api.UsersResource" scope="singleton">
<constructor-arg ref="serviceFacade"/> <constructor-arg ref="serviceFacade"/>
<constructor-arg ref="authorizer"/> <constructor-arg ref="authorizer"/>
<constructor-arg ref="nifiProperties"/> <constructor-arg ref="nifiProperties"/>
@ -381,6 +374,7 @@
<!-- exception mapping --> <!-- exception mapping -->
<bean class="org.apache.nifi.web.api.config.AccessDeniedExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.AccessDeniedExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.AuthorizationAccessExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.InvalidAuthenticationExceptionMapper" 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.AuthenticationCredentialsNotFoundExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.AdministrationExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.AdministrationExceptionMapper" scope="singleton"/>

View File

@ -0,0 +1,561 @@
/*
* 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.dao.impl
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer
import org.apache.nifi.authorization.AccessPolicy
import org.apache.nifi.authorization.Authorizer
import org.apache.nifi.authorization.Group
import org.apache.nifi.authorization.RequestAction
import org.apache.nifi.authorization.User
import org.apache.nifi.web.ResourceNotFoundException
import org.apache.nifi.web.api.dto.AccessPolicyDTO
import org.apache.nifi.web.api.dto.UserDTO
import org.apache.nifi.web.api.dto.UserGroupDTO
import org.apache.nifi.web.api.entity.UserEntity
import org.apache.nifi.web.api.entity.UserGroupEntity
import spock.lang.Specification
import spock.lang.Unroll
class StandardPolicyBasedAuthorizerDAOSpec extends Specification {
@Unroll
def "test non-policy-based authorizer #method throws IllegalStateException"() {
when:
daoMethod()
then:
def e = thrown(IllegalStateException)
assert e.message.equalsIgnoreCase(StandardPolicyBasedAuthorizerDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER)
where:
method | daoMethod
'createAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', canRead: true)) }
'createUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUser(new UserDTO(id: '1', identity: 'a')) }
'createUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).createUserGroup(new UserGroupDTO(id: '1', name: 'a')) }
'deleteAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteAccessPolicy('1') }
'deleteUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteUser('1') }
'deleteUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).deleteUserGroup('1') }
'getAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).getAccessPolicy('1') }
'getUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).getUser('1') }
'getUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).getUserGroup('1') }
'hasAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasAccessPolicy('1') }
'hasUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasUser('1') }
'hasUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).hasUserGroup('1') }
'updateAccessPolicy' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateAccessPolicy(new AccessPolicyDTO(id: '1', resource: '/1', canRead: true)) }
'updateUser' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateUser(new UserDTO(id: '1', identity: 'a')) }
'updateUserGroup' | { new StandardPolicyBasedAuthorizerDAO(Mock(Authorizer)).updateUserGroup(new UserGroupDTO(id: '1', name: 'a')) }
}
@Unroll
def "HasAccessPolicy: accessPolicy: #accessPolicy"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.hasAccessPolicy('policy-id-1')
then:
1 * authorizer.getAccessPolicy('policy-id-1') >> accessPolicy
0 * _
result == (accessPolicy != null)
where:
accessPolicy | _
new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1')
.addAction(RequestAction.READ).addAction(RequestAction.WRITE).build() | _
null | _
}
@Unroll
def "CreateAccessPolicy: accessPolicy=#accessPolicy"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', canRead: true,
canWrite: true,
users: [new UserEntity(id: 'user-id-1')] as Set,
userGroups: [new UserGroupEntity(id: 'user-group-id-1')] as Set)
when:
def result = dao.createAccessPolicy(requestDTO)
then:
noExceptionThrown()
then:
1 * authorizer.addAccessPolicy(accessPolicy) >> accessPolicy
0 * _
result?.equals accessPolicy
where:
accessPolicy | _
new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1')
.addAction(RequestAction.READ).addAction(RequestAction.WRITE).build() | _
}
@Unroll
def "GetAccessPolicy: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.getAccessPolicy('policy-id-1')
then:
1 * authorizer.getAccessPolicy('policy-id-1') >> accessPolicy
0 * _
assert result?.equals(accessPolicy)
where:
accessPolicy | _
new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1')
.addAction(RequestAction.READ).addAction(RequestAction.WRITE).build() | _
}
@Unroll
def "GetAccessPolicy: failure"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
dao.getAccessPolicy('policy-id-1')
then:
1 * authorizer.getAccessPolicy('policy-id-1') >> null
0 * _
thrown ResourceNotFoundException
}
@Unroll
def "UpdateAccessPolicy: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', canRead: true,
canWrite: true,
users: [new UserEntity(id: 'user-id-1')] as Set,
userGroups: [new UserGroupEntity(id: 'user-group-id-1')] as Set)
when:
def result = dao.updateAccessPolicy(requestDTO)
then:
1 * authorizer.getAccessPolicy(requestDTO.id) >> accessPolicy
1 * authorizer.updateAccessPolicy(accessPolicy) >> accessPolicy
0 * _
result?.equals(accessPolicy)
where:
accessPolicy | _
new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1')
.addAction(RequestAction.READ).addAction(RequestAction.WRITE).build() | _
}
@Unroll
def "UpdateAccessPolicy: failure"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
def requestDTO = new AccessPolicyDTO(id: 'policy-id-1', resource: '/fake/resource', canRead: true,
canWrite: true,
users: [new UserEntity(id: 'user-id-1')] as Set,
userGroups: [new UserGroupEntity(id: 'user-group-id-1')] as Set)
when:
dao.updateAccessPolicy(requestDTO)
then:
1 * authorizer.getAccessPolicy(requestDTO.id) >> null
0 * _
thrown ResourceNotFoundException
}
@Unroll
def "DeleteAccessPolicy: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.deleteAccessPolicy('policy-id-1')
then:
1 * authorizer.getAccessPolicy('policy-id-1') >> accessPolicy
1 * authorizer.deleteAccessPolicy(accessPolicy) >> accessPolicy
0 * _
result?.equals(accessPolicy)
where:
accessPolicy | _
new AccessPolicy.Builder().identifier('policy-id-1').resource('/fake/resource').addUser('user-id-1').addGroup('user-group-id-1')
.addAction(RequestAction.READ).addAction(RequestAction.WRITE).build() | _
}
@Unroll
def "DeleteAccessPolicy: failure"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
dao.deleteAccessPolicy('policy-id-1')
then:
1 * authorizer.getAccessPolicy('policy-id-1') >> null
0 * _
thrown ResourceNotFoundException
}
@Unroll
def "HasUserGroup: userGroup=#userGroup"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.hasUserGroup('user-group-id-1')
then:
1 * authorizer.getGroup('user-group-id-1') >> userGroup
0 * _
result == (userGroup != null)
where:
userGroup | _
new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build() | _
null | _
}
@Unroll
def "CreateUserGroup: userGroup=#userGroup"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
def requestDTO = new UserGroupDTO(id: 'user-group-id-1', name: 'user group identity', users: [new UserEntity(id: 'user-id-1')] as Set)
when:
def result = dao.createUserGroup(requestDTO)
then:
noExceptionThrown()
then:
1 * authorizer.addGroup(userGroup) >> userGroup
0 * _
result?.equals userGroup
where:
userGroup | _
new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build() | _
}
@Unroll
def "GetUserGroup: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.getUserGroup('user-group-id-1')
then:
1 * authorizer.getGroup('user-group-id-1') >> userGroup
0 * _
result?.equals(userGroup)
where:
userGroup | _
new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build() | _
}
@Unroll
def "GetUserGroup: failure"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
dao.getUserGroup('user-group-id-1')
then:
1 * authorizer.getGroup('user-group-id-1') >> null
0 * _
thrown ResourceNotFoundException
}
@Unroll
def "GetUserGroups: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.getUserGroups()
then:
1 * authorizer.getGroups() >> userGroups
0 * _
result?.equals(userGroups)
where:
userGroups | _
[new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build()] as Set | _
}
@Unroll
def "UpdateUserGroup: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
def requestDTO = new UserGroupDTO(id: 'user-group-id-1', name: 'user group identity', users: [new UserEntity(id: 'user-id-1')] as Set)
when:
def result = dao.updateUserGroup(requestDTO)
then:
1 * authorizer.getGroup(requestDTO.id) >> userGroup
1 * authorizer.updateGroup(userGroup) >> userGroup
0 * _
result?.equals(userGroup)
where:
userGroup | _
new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build() | _
}
@Unroll
def "UpdateUserGroup: failure"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
def requestDTO = new UserGroupDTO(id: 'user-group-id-1', name: 'user group identity', users: [new UserEntity(id: 'user-id-1')] as Set)
when:
dao.updateUserGroup(requestDTO)
then:
1 * authorizer.getGroup(requestDTO.id) >> null
0 * _
thrown ResourceNotFoundException
}
@Unroll
def "DeleteUserGroup: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.deleteUserGroup('user-group-id-1')
then:
1 * authorizer.getGroup('user-group-id-1') >> userGroup
1 * authorizer.deleteGroup(userGroup) >> userGroup
0 * _
assert result?.equals(userGroup)
where:
userGroup | _
new Group.Builder().identifier('user-group-id-1').name('user-group-id-1').addUser('user-id-1').build() | _
}
@Unroll
def "DeleteUserGroup: failure"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
dao.deleteUserGroup('user-group-id-1')
then:
1 * authorizer.getGroup('user-group-id-1') >> null
0 * _
thrown ResourceNotFoundException
}
@Unroll
def "HasUser: user=#user"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.hasUser('user-id-1')
then:
1 * authorizer.getUser('user-id-1') >> user
0 * _
result == (user != null)
where:
user | _
new User.Builder().identifier('user-id-1').identity('user identity').addGroup('user-group-id-1').build() | _
}
@Unroll
def "CreateUser: user=#user"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
def requestDTO = new UserDTO(id: 'user-id-1', identity: 'user identity', userGroups: [new UserGroupEntity(id: 'user-group-id-1')] as Set)
when:
def result = dao.createUser(requestDTO)
then:
noExceptionThrown()
then:
1 * authorizer.addUser(user) >> user
0 * _
result?.equals user
where:
user | _
new User.Builder().identifier('user-id-1').identity('user identity').addGroup('user-group-id-1').build() | _
}
@Unroll
def "GetUser: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.getUser('user-id-1')
then:
1 * authorizer.getUser('user-id-1') >> user
result?.equals(user)
0 * _
where:
user | _
new User.Builder().identifier('user-id-1').identity('user identity').addGroup('user-group-id-1').build() | _
}
@Unroll
def "GetUser: failure"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
dao.getUser('user-id-1')
then:
1 * authorizer.getUser('user-id-1') >> null
0 * _
thrown ResourceNotFoundException
}
@Unroll
def "GetUsers: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.getUsers()
then:
1 * authorizer.getUsers() >> users
result?.containsAll(users)
0 * _
where:
users | _
[new User.Builder().identifier('user-id-1').identity('user identity').addGroup('user-group-id-1').build()] as Set | _
}
@Unroll
def "UpdateUser: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
def requestDTO = new UserDTO(id: 'user-id-1', identity: 'user identity', userGroups: [new UserGroupEntity(id: 'user-group-id-1')] as Set)
when:
def result = dao.updateUser(requestDTO)
then:
1 * authorizer.getUser(requestDTO.id) >> user
1 * authorizer.updateUser(user) >> user
0 * _
result?.equals(user)
where:
user | _
new User.Builder().identifier('user-id-1').identity('user identity').addGroup('user-group-id-1').build() | _
}
@Unroll
def "UpdateUser: failure"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
def requestDTO = new UserDTO(id: 'user-id-1', identity: 'user identity', userGroups: [new UserGroupEntity(id: 'user-group-id-1')] as Set)
when:
dao.updateUser(requestDTO)
then:
1 * authorizer.getUser(requestDTO.id) >> null
0 * _
thrown ResourceNotFoundException
}
@Unroll
def "DeleteUser: success"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
def result = dao.deleteUser('user-id-1')
then:
1 * authorizer.getUser('user-id-1') >> user
1 * authorizer.deleteUser(user) >> user
0 * _
result?.equals(user)
where:
user | _
new User.Builder().identifier('user-id-1').identity('user identity').addGroup('user-group-id-1').build() | _
}
@Unroll
def "DeleteUser: failure"() {
given:
def authorizer = Mock AbstractPolicyBasedAuthorizer
def dao = new StandardPolicyBasedAuthorizerDAO(authorizer)
when:
dao.deleteUser('user-id-1')
then:
1 * authorizer.getUser('user-id-1') >> null
0 * _
thrown ResourceNotFoundException
}
}