NIFI-1916 Updating FileAuthorizer to extend AbstractPolicyBasedAuthorizer and adding intial loading of data users, groups, and policies - Implementing CRUD operations and unit tests for Users - Implementing CRUD operations and unit tests for Groups - Implementing CRUD operations and unit tests for AccessPolicies - Adding support for seeding with an initial admin user - Fixing delete for user and group so it removes references from policies - Adding example to authorizations.xml - Adding back the old users schema in preparation for auto-converting to the new format, and providing the AuthorizationConfigurationContext with access to the root process group id - Refactoring some of the FileAuthorizer to ensure thread safety - Adding /groups to policies created for initial admin - This closes #473
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/8d8a9cba Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/8d8a9cba Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/8d8a9cba Branch: refs/heads/master Commit: 8d8a9cba794fbea0d6a940f9aa0f286b5a076784 Parents: 806f4d5 Author: Bryan Bende <[email protected]> Authored: Wed May 25 13:22:05 2016 -0400 Committer: Matt Gilman <[email protected]> Committed: Fri Jun 3 17:26:22 2016 -0400 ---------------------------------------------------------------------- .../AbstractPolicyBasedAuthorizer.java | 13 +- .../apache/nifi/authorization/AccessPolicy.java | 50 +- .../AuthorizerConfigurationContext.java | 5 + .../org/apache/nifi/authorization/Group.java | 25 +- .../org/apache/nifi/authorization/User.java | 22 +- .../authorization/UsersAndAccessPolicies.java | 43 ++ .../TestAbstractPolicyBasedAuthorizer.java | 41 +- .../nifi/authorization/TestAccessPolicy.java | 62 +- .../apache/nifi/authorization/TestGroup.java | 18 +- .../org/apache/nifi/authorization/TestUser.java | 18 +- .../nifi-framework-nar/pom.xml | 4 + .../nifi-framework/nifi-authorizer/pom.xml | 74 ++ .../authorization/AuthorizerFactoryBean.java | 347 ++++++++++ .../main/resources/nifi-authorizer-context.xml | 27 + .../src/main/xsd/authorizers.xsd | 49 ++ .../nifi-framework/nifi-file-authorizer/pom.xml | 18 +- .../authorization/AuthorizationsHolder.java | 355 ++++++++++ .../nifi/authorization/FileAuthorizer.java | 645 +++++++++++++---- .../src/main/xsd/authorizations.xsd | 120 +++- .../nifi-file-authorizer/src/main/xsd/users.xsd | 64 ++ .../nifi/authorization/FileAuthorizerTest.java | 687 ++++++++++++++++++- .../nifi-framework-authorization/pom.xml | 47 +- .../authorization/AuthorizerFactoryBean.java | 341 --------- .../StandardAuthorizerConfigurationContext.java | 9 +- .../nifi-framework-authorization-context.xml | 26 - .../src/main/xsd/authorizers.xsd | 49 -- .../src/main/resources/conf/authorizations.xml | 19 +- .../src/main/resources/conf/authorizers.xml | 2 +- .../nifi/web/NiFiWebApiConfiguration.java | 2 +- .../nifi-framework/pom.xml | 1 + nifi-nar-bundles/nifi-framework-bundle/pom.xml | 5 + 31 files changed, 2415 insertions(+), 773 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java b/nifi-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java index 1200017..f67ac8b 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/AbstractPolicyBasedAuthorizer.java @@ -27,13 +27,15 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer { @Override public final AuthorizationResult authorize(AuthorizationRequest request) throws AuthorizationAccessException { - final Set<AccessPolicy> policies = getAccessPolicies(request.getResource()); + final UsersAndAccessPolicies usersAndAccessPolicies = getUsersAndAccessPolicies(); + final String resourceIdentifier = request.getResource().getIdentifier(); + final Set<AccessPolicy> policies = usersAndAccessPolicies.getAccessPolicies(resourceIdentifier); if (policies == null || policies.isEmpty()) { return AuthorizationResult.resourceNotFound(); } - final User user = getUserByIdentity(request.getIdentity()); + final User user = usersAndAccessPolicies.getUser(request.getIdentity()); if (user == null) { return AuthorizationResult.denied("Unknown user with identity " + request.getIdentity()); @@ -209,12 +211,11 @@ public abstract class AbstractPolicyBasedAuthorizer implements Authorizer { public abstract Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException; /** - * Retrieves the access policies for the given Resource. + * Returns the UserAccessPolicies instance. * - * @param resource the resource to retrieve policies for - * @return a set of policies for the given resource, or an empty set if there are none + * @return the UserAccessPolicies instance * @throws AuthorizationAccessException if there was an unexpected error performing the operation */ - public abstract Set<AccessPolicy> getAccessPolicies(Resource resource) throws AuthorizationAccessException; + public abstract UsersAndAccessPolicies getUsersAndAccessPolicies() throws AuthorizationAccessException; } http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java b/nifi-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java index f5c46e1..7ef40a3 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/AccessPolicy.java @@ -28,7 +28,7 @@ public class AccessPolicy { private final String identifier; - private final Resource resource; + private final String resource; private final Set<String> users; @@ -36,7 +36,7 @@ public class AccessPolicy { private final Set<RequestAction> actions; - private AccessPolicy(final AccessPolicyBuilder builder) { + private AccessPolicy(final Builder builder) { this.identifier = builder.identifier; this.resource = builder.resource; this.users = Collections.unmodifiableSet(new HashSet<>(builder.users)); @@ -51,10 +51,6 @@ public class AccessPolicy { throw new IllegalArgumentException("Resource can not be null"); } - if ((this.users == null || this.users.isEmpty()) && (this.groups == null || this.groups.isEmpty())) { - throw new IllegalArgumentException("Users & Groups can not both be null or empty"); - } - if (this.actions == null || this.actions.isEmpty()) { throw new IllegalArgumentException("Actions can not be null or empty"); } @@ -70,7 +66,7 @@ public class AccessPolicy { /** * @return the resource for this policy */ - public Resource getResource() { + public String getResource() { return resource; } @@ -116,16 +112,16 @@ public class AccessPolicy { @Override public String toString() { return String.format("identifier[%s], resource[%s], users[%s], groups[%s], action[%s]", - getIdentifier(), getResource().getIdentifier(), getUsers(), getGroups(), getActions()); + getIdentifier(), getResource(), getUsers(), getGroups(), getActions()); } /** * Builder for Access Policies. */ - public static class AccessPolicyBuilder { + public static class Builder { private String identifier; - private Resource resource; + private String resource; private Set<String> users = new HashSet<>(); private Set<String> groups = new HashSet<>(); private Set<RequestAction> actions = new HashSet<>(); @@ -134,7 +130,7 @@ public class AccessPolicy { /** * Default constructor for building a new AccessPolicy. */ - public AccessPolicyBuilder() { + public Builder() { this.fromPolicy = false; } @@ -145,7 +141,7 @@ public class AccessPolicy { * * @param other the existing access policy to initialize from */ - public AccessPolicyBuilder(final AccessPolicy other) { + public Builder(final AccessPolicy other) { if (other == null) { throw new IllegalArgumentException("Can not initialize builder with a null access policy"); } @@ -168,7 +164,7 @@ public class AccessPolicy { * @return the builder * @throws IllegalStateException if this method is called when this builder was constructed from an existing Policy */ - public AccessPolicyBuilder identifier(final String identifier) { + public Builder identifier(final String identifier) { if (fromPolicy) { throw new IllegalStateException( "Identifier can not be changed when initialized from an existing policy"); @@ -184,7 +180,7 @@ public class AccessPolicy { * @param resource the resource to set * @return the builder */ - public AccessPolicyBuilder resource(final Resource resource) { + public Builder resource(final String resource) { this.resource = resource; return this; } @@ -195,7 +191,7 @@ public class AccessPolicy { * @param users the users to add * @return the builder */ - public AccessPolicyBuilder addUsers(final Set<String> users) { + public Builder addUsers(final Set<String> users) { if (users != null) { this.users.addAll(users); } @@ -208,7 +204,7 @@ public class AccessPolicy { * @param user the user to add * @return the builder */ - public AccessPolicyBuilder addUser(final String user) { + public Builder addUser(final String user) { if (user != null) { this.users.add(user); } @@ -221,7 +217,7 @@ public class AccessPolicy { * @param users the users to remove * @return the builder */ - public AccessPolicyBuilder removeUsers(final Set<String> users) { + public Builder removeUsers(final Set<String> users) { if (users != null) { this.users.removeAll(users); } @@ -234,7 +230,7 @@ public class AccessPolicy { * @param user the user to remove * @return the builder */ - public AccessPolicyBuilder removeUser(final String user) { + public Builder removeUser(final String user) { if (user != null) { this.users.remove(user); } @@ -246,7 +242,7 @@ public class AccessPolicy { * * @return the builder */ - public AccessPolicyBuilder clearUsers() { + public Builder clearUsers() { this.users.clear(); return this; } @@ -257,7 +253,7 @@ public class AccessPolicy { * @param groups the groups to add * @return the builder */ - public AccessPolicyBuilder addGroups(final Set<String> groups) { + public Builder addGroups(final Set<String> groups) { if (groups != null) { this.groups.addAll(groups); } @@ -270,7 +266,7 @@ public class AccessPolicy { * @param group the group to add * @return the builder */ - public AccessPolicyBuilder addGroup(final String group) { + public Builder addGroup(final String group) { if (group != null) { this.groups.add(group); } @@ -283,7 +279,7 @@ public class AccessPolicy { * @param groups the groups to remove * @return the builder */ - public AccessPolicyBuilder removeGroups(final Set<String> groups) { + public Builder removeGroups(final Set<String> groups) { if (groups != null) { this.groups.removeAll(groups); } @@ -296,7 +292,7 @@ public class AccessPolicy { * @param group the group to remove * @return the builder */ - public AccessPolicyBuilder removeGroup(final String group) { + public Builder removeGroup(final String group) { if (group != null) { this.groups.remove(group); } @@ -308,7 +304,7 @@ public class AccessPolicy { * * @return the builder */ - public AccessPolicyBuilder clearGroups() { + public Builder clearGroups() { this.groups.clear(); return this; } @@ -319,7 +315,7 @@ public class AccessPolicy { * @param action the action to add * @return the builder */ - public AccessPolicyBuilder addAction(final RequestAction action) { + public Builder addAction(final RequestAction action) { this.actions.add(action); return this; } @@ -330,7 +326,7 @@ public class AccessPolicy { * @param action the action to remove * @return the builder */ - public AccessPolicyBuilder removeAction(final RequestAction action) { + public Builder removeAction(final RequestAction action) { this.actions.remove(action); return this; } @@ -340,7 +336,7 @@ public class AccessPolicy { * * @return the builder */ - public AccessPolicyBuilder clearActions() { + public Builder clearActions() { this.actions.clear(); return this; } http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java b/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java index 3721ab4..20cb69e 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/AuthorizerConfigurationContext.java @@ -31,6 +31,11 @@ public interface AuthorizerConfigurationContext { String getIdentifier(); /** + * @return the id of the root process group + */ + String getRootGroupId(); + + /** * Retrieves all properties the component currently understands regardless * of whether a value has been set for them or not. If no value is present * then its value is null and thus any registered default for the property http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/main/java/org/apache/nifi/authorization/Group.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/Group.java b/nifi-api/src/main/java/org/apache/nifi/authorization/Group.java index d7b0f2e..bb5c1e9 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/Group.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/Group.java @@ -32,7 +32,7 @@ public class Group { private final Set<String> users; - private Group(final GroupBuilder builder) { + private Group(final Builder builder) { this.identifier = builder.identifier; this.name = builder.name; this.users = Collections.unmodifiableSet(new HashSet<>(builder.users)); @@ -61,6 +61,9 @@ public class Group { } /** + * NOTE: This set of users is populated when retrieving an existing group and will be ignored when adding a new Group. + * To add a User to a group, it should be done by adding or updating a User with the appropriate Group id. + * * @return an unmodifiable set of user identifiers that belong to this group */ public Set<String> getUsers() { @@ -94,14 +97,14 @@ public class Group { /** * Builder for creating Groups. */ - public static class GroupBuilder { + public static class Builder { private String identifier; private String name; private Set<String> users = new HashSet<>(); private final boolean fromGroup; - public GroupBuilder() { + public Builder() { this.fromGroup = false; } @@ -112,7 +115,7 @@ public class Group { * * @param other the existing access policy to initialize from */ - public GroupBuilder(final Group other) { + public Builder(final Group other) { if (other == null) { throw new IllegalArgumentException("Provided group can not be null"); } @@ -131,7 +134,7 @@ public class Group { * @return the builder * @throws IllegalStateException if this method is called when this builder was constructed from an existing Group */ - public GroupBuilder identifier(final String identifier) { + public Builder identifier(final String identifier) { if (fromGroup) { throw new IllegalStateException( "Identifier can not be changed when initialized from an existing group"); @@ -147,7 +150,7 @@ public class Group { * @param name the name * @return the builder */ - public GroupBuilder name(final String name) { + public Builder name(final String name) { this.name = name; return this; } @@ -158,7 +161,7 @@ public class Group { * @param users a set of users to add * @return the builder */ - public GroupBuilder addUsers(final Set<String> users) { + public Builder addUsers(final Set<String> users) { if (users != null) { this.users.addAll(users); } @@ -171,7 +174,7 @@ public class Group { * @param user the user to add * @return the builder */ - public GroupBuilder addUser(final String user) { + public Builder addUser(final String user) { if (user != null) { this.users.add(user); } @@ -184,7 +187,7 @@ public class Group { * @param user the user to remove * @return the builder */ - public GroupBuilder removeUser(final String user) { + public Builder removeUser(final String user) { if (user != null) { this.users.remove(user); } @@ -197,7 +200,7 @@ public class Group { * @param users the users to remove * @return the builder */ - public GroupBuilder removeUsers(final Set<String> users) { + public Builder removeUsers(final Set<String> users) { if (users != null) { this.users.removeAll(users); } @@ -209,7 +212,7 @@ public class Group { * * @return the builder */ - public GroupBuilder clearUsers() { + public Builder clearUsers() { this.users.clear(); return this; } http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/main/java/org/apache/nifi/authorization/User.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/User.java b/nifi-api/src/main/java/org/apache/nifi/authorization/User.java index e068736..1e62d58 100644 --- a/nifi-api/src/main/java/org/apache/nifi/authorization/User.java +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/User.java @@ -32,7 +32,7 @@ public class User { private final Set<String> groups; - private User(final UserBuilder builder) { + private User(final Builder builder) { this.identifier = builder.identifier; this.identity = builder.identity; this.groups = Collections.unmodifiableSet(new HashSet<>(builder.groups)); @@ -94,7 +94,7 @@ public class User { /** * Builder for Users. */ - public static class UserBuilder { + public static class Builder { private String identifier; private String identity; @@ -104,7 +104,7 @@ public class User { /** * Default constructor for building a new User. */ - public UserBuilder() { + public Builder() { this.fromUser = false; } @@ -115,7 +115,7 @@ public class User { * * @param other the existing user to initialize from */ - public UserBuilder(final User other) { + public Builder(final User other) { if (other == null) { throw new IllegalArgumentException("Provided user can not be null"); } @@ -134,7 +134,7 @@ public class User { * @return the builder * @throws IllegalStateException if this method is called when this builder was constructed from an existing User */ - public UserBuilder identifier(final String identifier) { + public Builder identifier(final String identifier) { if (fromUser) { throw new IllegalStateException( "Identifier can not be changed when initialized from an existing user"); @@ -150,7 +150,7 @@ public class User { * @param identity the identity to set * @return the builder */ - public UserBuilder identity(final String identity) { + public Builder identity(final String identity) { this.identity = identity; return this; } @@ -161,7 +161,7 @@ public class User { * @param groups the groups to add * @return the builder */ - public UserBuilder addGroups(final Set<String> groups) { + public Builder addGroups(final Set<String> groups) { if (groups != null) { this.groups.addAll(groups); } @@ -174,7 +174,7 @@ public class User { * @param group the group to add * @return the builder */ - public UserBuilder addGroup(final String group) { + public Builder addGroup(final String group) { if (group != null) { this.groups.add(group); } @@ -187,7 +187,7 @@ public class User { * @param groups the groups to remove * @return the builder */ - public UserBuilder removeGroups(final Set<String> groups) { + public Builder removeGroups(final Set<String> groups) { if (groups != null) { this.groups.removeAll(groups); } @@ -200,7 +200,7 @@ public class User { * @param group the group to remove * @return the builder */ - public UserBuilder removeGroup(final String group) { + public Builder removeGroup(final String group) { if (group != null) { this.groups.remove(group); } @@ -212,7 +212,7 @@ public class User { * * @return the builder */ - public UserBuilder clearGroups() { + public Builder clearGroups() { this.groups.clear(); return this; } http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/main/java/org/apache/nifi/authorization/UsersAndAccessPolicies.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/main/java/org/apache/nifi/authorization/UsersAndAccessPolicies.java b/nifi-api/src/main/java/org/apache/nifi/authorization/UsersAndAccessPolicies.java new file mode 100644 index 0000000..9a19977 --- /dev/null +++ b/nifi-api/src/main/java/org/apache/nifi/authorization/UsersAndAccessPolicies.java @@ -0,0 +1,43 @@ +/* + * 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.authorization; + +import java.util.Set; + +/** + * A holder object to provide atomic access to policies for a given resource and users by + * identity. Implementations must ensure consistent access to the data backing this instance. + */ +public interface UsersAndAccessPolicies { + + /** + * Retrieves the set of access policies for a given resource. + * + * @param resourceIdentifier the resource identifier to retrieve policies for + * @return the set of access policies for the given resource + */ + public Set<AccessPolicy> getAccessPolicies(final String resourceIdentifier); + + /** + * Retrieves a user by an identity string. + * + * @param identity the identity of the user to retrieve + * @return the user with the given identity + */ + public User getUser(final String identity); + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java b/nifi-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java index 8dbc781..3429a6d 100644 --- a/nifi-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java +++ b/nifi-api/src/test/java/org/apache/nifi/authorization/TestAbstractPolicyBasedAuthorizer.java @@ -42,26 +42,28 @@ public class TestAbstractPolicyBasedAuthorizer { @Test public void testApproveBasedOnUser() { AbstractPolicyBasedAuthorizer authorizer = Mockito.mock(AbstractPolicyBasedAuthorizer.class); + UsersAndAccessPolicies usersAndAccessPolicies = Mockito.mock(UsersAndAccessPolicies.class); + when(authorizer.getUsersAndAccessPolicies()).thenReturn(usersAndAccessPolicies); final String userIdentifier = "userIdentifier1"; final String userIdentity = "userIdentity1"; final Set<AccessPolicy> policiesForResource = new HashSet<>(); - policiesForResource.add(new AccessPolicy.AccessPolicyBuilder() + policiesForResource.add(new AccessPolicy.Builder() .identifier("1") - .resource(TEST_RESOURCE) + .resource(TEST_RESOURCE.getIdentifier()) .addUser(userIdentifier) .addAction(RequestAction.READ) .build()); - when(authorizer.getAccessPolicies(TEST_RESOURCE)).thenReturn(policiesForResource); + when(usersAndAccessPolicies.getAccessPolicies(TEST_RESOURCE.getIdentifier())).thenReturn(policiesForResource); - final User user = new User.UserBuilder() + final User user = new User.Builder() .identity(userIdentity) .identifier(userIdentifier) .build(); - when(authorizer.getUserByIdentity(userIdentity)).thenReturn(user); + when(usersAndAccessPolicies.getUser(userIdentity)).thenReturn(user); final AuthorizationRequest request = new AuthorizationRequest.Builder() .identity(userIdentity) @@ -77,28 +79,30 @@ public class TestAbstractPolicyBasedAuthorizer { @Test public void testApprovedBasedOnGroup() { AbstractPolicyBasedAuthorizer authorizer = Mockito.mock(AbstractPolicyBasedAuthorizer.class); + UsersAndAccessPolicies usersAndAccessPolicies = Mockito.mock(UsersAndAccessPolicies.class); + when(authorizer.getUsersAndAccessPolicies()).thenReturn(usersAndAccessPolicies); final String userIdentifier = "userIdentifier1"; final String userIdentity = "userIdentity1"; final String groupIdentifier = "groupIdentifier1"; final Set<AccessPolicy> policiesForResource = new HashSet<>(); - policiesForResource.add(new AccessPolicy.AccessPolicyBuilder() + policiesForResource.add(new AccessPolicy.Builder() .identifier("1") - .resource(TEST_RESOURCE) + .resource(TEST_RESOURCE.getIdentifier()) .addGroup(groupIdentifier) .addAction(RequestAction.READ) .build()); - when(authorizer.getAccessPolicies(TEST_RESOURCE)).thenReturn(policiesForResource); + when(usersAndAccessPolicies.getAccessPolicies(TEST_RESOURCE.getIdentifier())).thenReturn(policiesForResource); - final User user = new User.UserBuilder() + final User user = new User.Builder() .identity(userIdentity) .identifier(userIdentifier) .addGroup(groupIdentifier) .build(); - when(authorizer.getUserByIdentity(userIdentity)).thenReturn(user); + when(usersAndAccessPolicies.getUser(userIdentity)).thenReturn(user); final AuthorizationRequest request = new AuthorizationRequest.Builder() .identity(userIdentity) @@ -114,26 +118,28 @@ public class TestAbstractPolicyBasedAuthorizer { @Test public void testDeny() { AbstractPolicyBasedAuthorizer authorizer = Mockito.mock(AbstractPolicyBasedAuthorizer.class); + UsersAndAccessPolicies usersAndAccessPolicies = Mockito.mock(UsersAndAccessPolicies.class); + when(authorizer.getUsersAndAccessPolicies()).thenReturn(usersAndAccessPolicies); final String userIdentifier = "userIdentifier1"; final String userIdentity = "userIdentity1"; final Set<AccessPolicy> policiesForResource = new HashSet<>(); - policiesForResource.add(new AccessPolicy.AccessPolicyBuilder() + policiesForResource.add(new AccessPolicy.Builder() .identifier("1") - .resource(TEST_RESOURCE) + .resource(TEST_RESOURCE.getIdentifier()) .addUser("NOT_USER_1") .addAction(RequestAction.READ) .build()); - when(authorizer.getAccessPolicies(TEST_RESOURCE)).thenReturn(policiesForResource); + when(usersAndAccessPolicies.getAccessPolicies(TEST_RESOURCE.getIdentifier())).thenReturn(policiesForResource); - final User user = new User.UserBuilder() + final User user = new User.Builder() .identity(userIdentity) .identifier(userIdentifier) .build(); - when(authorizer.getUserByIdentity(userIdentity)).thenReturn(user); + when(usersAndAccessPolicies.getUser(userIdentity)).thenReturn(user); final AuthorizationRequest request = new AuthorizationRequest.Builder() .identity(userIdentity) @@ -149,7 +155,10 @@ public class TestAbstractPolicyBasedAuthorizer { @Test public void testResourceNotFound() { AbstractPolicyBasedAuthorizer authorizer = Mockito.mock(AbstractPolicyBasedAuthorizer.class); - when(authorizer.getAccessPolicies(TEST_RESOURCE)).thenReturn(new HashSet<>()); + UsersAndAccessPolicies usersAndAccessPolicies = Mockito.mock(UsersAndAccessPolicies.class); + when(authorizer.getUsersAndAccessPolicies()).thenReturn(usersAndAccessPolicies); + + when(usersAndAccessPolicies.getAccessPolicies(TEST_RESOURCE.getIdentifier())).thenReturn(new HashSet<>()); final AuthorizationRequest request = new AuthorizationRequest.Builder() .identity("userIdentity") http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/test/java/org/apache/nifi/authorization/TestAccessPolicy.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/test/java/org/apache/nifi/authorization/TestAccessPolicy.java b/nifi-api/src/test/java/org/apache/nifi/authorization/TestAccessPolicy.java index 28c0b9f..35e1616 100644 --- a/nifi-api/src/test/java/org/apache/nifi/authorization/TestAccessPolicy.java +++ b/nifi-api/src/test/java/org/apache/nifi/authorization/TestAccessPolicy.java @@ -28,17 +28,7 @@ import static org.junit.Assert.fail; public class TestAccessPolicy { - static final Resource TEST_RESOURCE = new Resource() { - @Override - public String getIdentifier() { - return "1"; - } - - @Override - public String getName() { - return "resource1"; - } - }; + static final String TEST_RESOURCE = "1"; @Test public void testSimpleCreation() { @@ -47,7 +37,7 @@ public class TestAccessPolicy { final String user2 = "user2"; final RequestAction action = RequestAction.READ; - final AccessPolicy policy = new AccessPolicy.AccessPolicyBuilder() + final AccessPolicy policy = new AccessPolicy.Builder() .identifier(identifier) .resource(TEST_RESOURCE) .addUser(user1) @@ -58,7 +48,7 @@ public class TestAccessPolicy { assertEquals(identifier, policy.getIdentifier()); assertNotNull(policy.getResource()); - assertEquals(TEST_RESOURCE.getIdentifier(), policy.getResource().getIdentifier()); + assertEquals(TEST_RESOURCE, policy.getResource()); assertNotNull(policy.getUsers()); assertEquals(2, policy.getUsers().size()); @@ -72,7 +62,7 @@ public class TestAccessPolicy { @Test(expected = IllegalArgumentException.class) public void testMissingIdentifier() { - new AccessPolicy.AccessPolicyBuilder() + new AccessPolicy.Builder() .resource(TEST_RESOURCE) .addUser("user1") .addAction(RequestAction.READ) @@ -81,25 +71,27 @@ public class TestAccessPolicy { @Test(expected = IllegalArgumentException.class) public void testMissingResource() { - new AccessPolicy.AccessPolicyBuilder() + new AccessPolicy.Builder() .identifier("1") .addUser("user1") .addAction(RequestAction.READ) .build(); } - @Test(expected = IllegalArgumentException.class) + @Test public void testMissingUsersAndGroups() { - new AccessPolicy.AccessPolicyBuilder() + final AccessPolicy policy = new AccessPolicy.Builder() .identifier("1") .resource(TEST_RESOURCE) .addAction(RequestAction.READ) .build(); + + assertNotNull(policy); } @Test(expected = IllegalArgumentException.class) public void testMissingActions() { - new AccessPolicy.AccessPolicyBuilder() + new AccessPolicy.Builder() .identifier("1") .resource(TEST_RESOURCE) .addUser("user1") @@ -115,7 +107,7 @@ public class TestAccessPolicy { final String group2 = "group2"; final RequestAction action = RequestAction.READ; - final AccessPolicy policy = new AccessPolicy.AccessPolicyBuilder() + final AccessPolicy policy = new AccessPolicy.Builder() .identifier(identifier) .resource(TEST_RESOURCE) .addUser(user1) @@ -128,7 +120,7 @@ public class TestAccessPolicy { assertEquals(identifier, policy.getIdentifier()); assertNotNull(policy.getResource()); - assertEquals(TEST_RESOURCE.getIdentifier(), policy.getResource().getIdentifier()); + assertEquals(TEST_RESOURCE, policy.getResource()); assertNotNull(policy.getUsers()); assertEquals(2, policy.getUsers().size()); @@ -144,28 +136,28 @@ public class TestAccessPolicy { assertEquals(1, policy.getActions().size()); assertTrue(policy.getActions().contains(action)); - final AccessPolicy policy2 = new AccessPolicy.AccessPolicyBuilder(policy).build(); + final AccessPolicy policy2 = new AccessPolicy.Builder(policy).build(); assertEquals(policy.getIdentifier(), policy2.getIdentifier()); - assertEquals(policy.getResource().getIdentifier(), policy2.getResource().getIdentifier()); + assertEquals(policy.getResource(), policy2.getResource()); assertEquals(policy.getUsers(), policy2.getUsers()); assertEquals(policy.getActions(), policy2.getActions()); } @Test(expected = IllegalStateException.class) public void testFromPolicyAndChangeIdentifier() { - final AccessPolicy policy = new AccessPolicy.AccessPolicyBuilder() + final AccessPolicy policy = new AccessPolicy.Builder() .identifier("1") .resource(TEST_RESOURCE) .addUser("user1") .addAction(RequestAction.READ) .build(); - new AccessPolicy.AccessPolicyBuilder(policy).identifier("2").build(); + new AccessPolicy.Builder(policy).identifier("2").build(); } @Test public void testAddRemoveClearUsers() { - final AccessPolicy.AccessPolicyBuilder builder = new AccessPolicy.AccessPolicyBuilder() + final AccessPolicy.Builder builder = new AccessPolicy.Builder() .identifier("1") .resource(TEST_RESOURCE) .addUser("user1") @@ -201,17 +193,14 @@ public class TestAccessPolicy { assertEquals(1, policy4.getUsers().size()); assertTrue(policy4.getUsers().contains("user2")); - try { - builder.clearUsers().build(); - fail("should have thrown exception"); - } catch (IllegalArgumentException e) { - } + final AccessPolicy policy5 = builder.clearUsers().build(); + assertEquals(0, policy5.getUsers().size()); } @Test public void testAddRemoveClearGroups() { - final AccessPolicy.AccessPolicyBuilder builder = new AccessPolicy.AccessPolicyBuilder() + final AccessPolicy.Builder builder = new AccessPolicy.Builder() .identifier("1") .resource(TEST_RESOURCE) .addGroup("group1") @@ -247,18 +236,13 @@ public class TestAccessPolicy { assertEquals(1, policy4.getGroups().size()); assertTrue(policy4.getGroups().contains("group2")); - try { - builder.clearGroups().build(); - fail("should have thrown exception"); - } catch (IllegalArgumentException e) { - - } + final AccessPolicy policy5 = builder.clearGroups().build(); + assertEquals(0, policy5.getUsers().size()); } - @Test public void testAddRemoveClearActions() { - final AccessPolicy.AccessPolicyBuilder builder = new AccessPolicy.AccessPolicyBuilder() + final AccessPolicy.Builder builder = new AccessPolicy.Builder() .identifier("1") .resource(TEST_RESOURCE) .addUser("user1") http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/test/java/org/apache/nifi/authorization/TestGroup.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/test/java/org/apache/nifi/authorization/TestGroup.java b/nifi-api/src/test/java/org/apache/nifi/authorization/TestGroup.java index 39dbab6..8ccdba5 100644 --- a/nifi-api/src/test/java/org/apache/nifi/authorization/TestGroup.java +++ b/nifi-api/src/test/java/org/apache/nifi/authorization/TestGroup.java @@ -34,7 +34,7 @@ public class TestGroup { final String user1 = "user1"; final String user2 = "user2"; - final Group group = new Group.GroupBuilder() + final Group group = new Group.Builder() .identifier(id) .name(name) .addUser(user1) @@ -52,7 +52,7 @@ public class TestGroup { @Test(expected = IllegalArgumentException.class) public void testMissingId() { - new Group.GroupBuilder() + new Group.Builder() .name("group1") .addUser("user1") .addUser("user2") @@ -61,7 +61,7 @@ public class TestGroup { @Test(expected = IllegalArgumentException.class) public void testMissingName() { - new Group.GroupBuilder() + new Group.Builder() .identifier("1") .addUser("user1") .addUser("user2") @@ -73,7 +73,7 @@ public class TestGroup { final String id = "1"; final String name = "group1"; - final Group group = new Group.GroupBuilder() + final Group group = new Group.Builder() .identifier(id) .name(name) .build(); @@ -92,7 +92,7 @@ public class TestGroup { final String user1 = "user1"; final String user2 = "user2"; - final Group group1 = new Group.GroupBuilder() + final Group group1 = new Group.Builder() .identifier(id) .name(name) .addUser(user1) @@ -107,7 +107,7 @@ public class TestGroup { assertTrue(group1.getUsers().contains(user1)); assertTrue(group1.getUsers().contains(user2)); - final Group group2 = new Group.GroupBuilder(group1).build(); + final Group group2 = new Group.Builder(group1).build(); assertEquals(group1.getIdentifier(), group2.getIdentifier()); assertEquals(group1.getName(), group2.getName()); assertEquals(group1.getUsers(), group2.getUsers()); @@ -115,18 +115,18 @@ public class TestGroup { @Test(expected = IllegalStateException.class) public void testFromGroupAndChangeIdentifier() { - final Group group1 = new Group.GroupBuilder() + final Group group1 = new Group.Builder() .identifier("1") .name("group1") .addUser("user1") .build(); - new Group.GroupBuilder(group1).identifier("2").build(); + new Group.Builder(group1).identifier("2").build(); } @Test public void testAddRemoveClearUsers() { - final Group.GroupBuilder builder = new Group.GroupBuilder() + final Group.Builder builder = new Group.Builder() .identifier("1") .name("group1") .addUser("user1"); http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-api/src/test/java/org/apache/nifi/authorization/TestUser.java ---------------------------------------------------------------------- diff --git a/nifi-api/src/test/java/org/apache/nifi/authorization/TestUser.java b/nifi-api/src/test/java/org/apache/nifi/authorization/TestUser.java index 8e6f9aa..483924a 100644 --- a/nifi-api/src/test/java/org/apache/nifi/authorization/TestUser.java +++ b/nifi-api/src/test/java/org/apache/nifi/authorization/TestUser.java @@ -34,7 +34,7 @@ public class TestUser { final String group1 = "group1"; final String group2 = "group2"; - final User user = new User.UserBuilder() + final User user = new User.Builder() .identifier(identifier) .identity(identity) .addGroup(group1) @@ -52,7 +52,7 @@ public class TestUser { @Test(expected = IllegalArgumentException.class) public void testMissingIdentifier() { - new User.UserBuilder() + new User.Builder() .identity("user1") .addGroup("group1") .addGroup("group2") @@ -61,7 +61,7 @@ public class TestUser { @Test(expected = IllegalArgumentException.class) public void testMissingIdentity() { - new User.UserBuilder() + new User.Builder() .identifier("1") .addGroup("group1") .addGroup("group2") @@ -73,7 +73,7 @@ public class TestUser { final String identifier = "1"; final String identity = "user1"; - final User user = new User.UserBuilder() + final User user = new User.Builder() .identifier(identifier) .identity(identity) .build(); @@ -92,7 +92,7 @@ public class TestUser { final String group1 = "group1"; final String group2 = "group2"; - final User user = new User.UserBuilder() + final User user = new User.Builder() .identifier(identifier) .identity(identity) .addGroup(group1) @@ -107,7 +107,7 @@ public class TestUser { assertTrue(user.getGroups().contains(group1)); assertTrue(user.getGroups().contains(group2)); - final User user2 = new User.UserBuilder(user).build(); + final User user2 = new User.Builder(user).build(); assertEquals(user.getIdentifier(), user2.getIdentifier()); assertEquals(user.getIdentity(), user2.getIdentity()); assertEquals(user.getGroups(), user2.getGroups()); @@ -115,19 +115,19 @@ public class TestUser { @Test(expected = IllegalStateException.class) public void testFromUserAndChangeIdentifier() { - final User user = new User.UserBuilder() + final User user = new User.Builder() .identifier("1") .identity("user1") .addGroup("group1") .addGroup("group2") .build(); - new User.UserBuilder(user).identifier("2").build(); + new User.Builder(user).identifier("2").build(); } @Test public void testAddRemoveClearGroups() { - final User.UserBuilder builder = new User.UserBuilder() + final User.Builder builder = new User.Builder() .identifier("1") .identity("user1") .addGroup("group1"); http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/pom.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/pom.xml index 104aa10..6ddaf21 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/pom.xml @@ -35,6 +35,10 @@ <groupId>org.apache.nifi</groupId> <artifactId>nifi-file-authorizer</artifactId> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-authorizer</artifactId> + </dependency> <!-- mark these nifi artifacts as provided since it is included in the lib --> <dependency> http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/pom.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/pom.xml new file mode 100644 index 0000000..b8e1652 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/pom.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>nifi-framework</artifactId> + <groupId>org.apache.nifi</groupId> + <version>1.0.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>nifi-authorizer</artifactId> + + <build> + <resources> + <resource> + <directory>src/main/resources</directory> + </resource> + <resource> + <directory>src/main/xsd</directory> + </resource> + </resources> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>jaxb2-maven-plugin</artifactId> + <executions> + <execution> + <id>current</id> + <goals> + <goal>xjc</goal> + </goals> + <configuration> + <packageName>org.apache.nifi.authorization.generated</packageName> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + <configuration> + <excludes>**/authorization/generated/*.java,</excludes> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-framework-core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-nar-utils</artifactId> + </dependency> + </dependencies> + +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java new file mode 100644 index 0000000..378c805 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizerFactoryBean.java @@ -0,0 +1,347 @@ +/* + * 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.authorization; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.authorization.annotation.AuthorizerContext; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.authorization.generated.Authorizers; +import org.apache.nifi.authorization.generated.Property; +import org.apache.nifi.controller.FlowController; +import org.apache.nifi.nar.ExtensionManager; +import org.apache.nifi.nar.NarCloseable; +import org.apache.nifi.util.NiFiProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.xml.sax.SAXException; + +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * Factory bean for loading the configured authorizer. + */ +public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, AuthorizerLookup { + + private static final Logger logger = LoggerFactory.getLogger(AuthorizerFactoryBean.class); + private static final String AUTHORIZERS_XSD = "/authorizers.xsd"; + private static final String JAXB_GENERATED_PATH = "org.apache.nifi.authorization.generated"; + private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext(); + + /** + * Load the JAXBContext. + */ + private static JAXBContext initializeJaxbContext() { + try { + return JAXBContext.newInstance(JAXB_GENERATED_PATH, AuthorizerFactoryBean.class.getClassLoader()); + } catch (JAXBException e) { + throw new RuntimeException("Unable to create JAXBContext."); + } + } + + private Authorizer authorizer; + private NiFiProperties properties; + private FlowController flowController; + private final Map<String, Authorizer> authorizers = new HashMap<>(); + + public void setFlowController(FlowController flowController) { + this.flowController = flowController; + } + + @Override + public Authorizer getAuthorizer(String identifier) { + return authorizers.get(identifier); + } + + @Override + public Object getObject() throws Exception { + if (authorizer == null) { + if (properties.getSslPort() == null) { + // use a default authorizer... only allowable when running not securely + authorizer = createDefaultAuthorizer(); + } else { + // look up the authorizer to use + final String authorizerIdentifier = properties.getProperty(NiFiProperties.SECURITY_USER_AUTHORIZER); + + // ensure the authorizer class name was specified + if (StringUtils.isBlank(authorizerIdentifier)) { + throw new Exception("When running securely, the authorizer identifier must be specified in the nifi properties file."); + } else { + final Authorizers authorizerConfiguration = loadAuthorizersConfiguration(); + + // create each authorizer + for (final org.apache.nifi.authorization.generated.Authorizer authorizer : authorizerConfiguration.getAuthorizer()) { + authorizers.put(authorizer.getIdentifier(), createAuthorizer(authorizer.getIdentifier(), authorizer.getClazz())); + } + + // configure each authorizer + for (final org.apache.nifi.authorization.generated.Authorizer provider : authorizerConfiguration.getAuthorizer()) { + final Authorizer instance = authorizers.get(provider.getIdentifier()); + instance.onConfigured(loadAuthorizerConfiguration(provider)); + } + + // get the authorizer instance + authorizer = getAuthorizer(authorizerIdentifier); + + // ensure it was found + if (authorizer == null) { + throw new Exception(String.format("The specified authorizer '%s' could not be found.", authorizerIdentifier)); + } + } + } + } + + return authorizer; + } + + private Authorizers loadAuthorizersConfiguration() throws Exception { + final File authorizersConfigurationFile = properties.getAuthorizerConfiguraitonFile(); + + // load the authorizers from the specified file + if (authorizersConfigurationFile.exists()) { + try { + // find the schema + final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + final Schema schema = schemaFactory.newSchema(Authorizers.class.getResource(AUTHORIZERS_XSD)); + + // attempt to unmarshal + final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller(); + unmarshaller.setSchema(schema); + final JAXBElement<Authorizers> element = unmarshaller.unmarshal(new StreamSource(authorizersConfigurationFile), Authorizers.class); + return element.getValue(); + } catch (SAXException | JAXBException e) { + throw new Exception("Unable to load the authorizer configuration file at: " + authorizersConfigurationFile.getAbsolutePath(), e); + } + } else { + throw new Exception("Unable to find the authorizer configuration file at " + authorizersConfigurationFile.getAbsolutePath()); + } + } + + private Authorizer createAuthorizer(final String identifier, final String authorizerClassName) throws Exception { + // get the classloader for the specified authorizer + final ClassLoader authorizerClassLoader = ExtensionManager.getClassLoader(authorizerClassName); + if (authorizerClassLoader == null) { + throw new Exception(String.format("The specified authorizer class '%s' is not known to this nifi.", authorizerClassName)); + } + + // get the current context classloader + final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); + + final Authorizer instance; + try { + // set the appropriate class loader + Thread.currentThread().setContextClassLoader(authorizerClassLoader); + + // attempt to load the class + Class<?> rawAuthorizerClass = Class.forName(authorizerClassName, true, authorizerClassLoader); + Class<? extends Authorizer> authorizerClass = rawAuthorizerClass.asSubclass(Authorizer.class); + + // otherwise create a new instance + Constructor constructor = authorizerClass.getConstructor(); + instance = (Authorizer) constructor.newInstance(); + + // method injection + performMethodInjection(instance, authorizerClass); + + // field injection + performFieldInjection(instance, authorizerClass); + + // call post construction lifecycle event + instance.initialize(new StandardAuthorizerInitializationContext(identifier, this)); + } finally { + if (currentClassLoader != null) { + Thread.currentThread().setContextClassLoader(currentClassLoader); + } + } + + return withNarLoader(instance); + } + + private AuthorizerConfigurationContext loadAuthorizerConfiguration(final org.apache.nifi.authorization.generated.Authorizer authorizer) { + final Map<String, String> authorizerProperties = new HashMap<>(); + + for (final Property property : authorizer.getProperty()) { + authorizerProperties.put(property.getName(), property.getValue()); + } + + return new StandardAuthorizerConfigurationContext(authorizer.getIdentifier(), flowController.getRootGroupId(), authorizerProperties); + } + + private void performMethodInjection(final Authorizer instance, final Class authorizerClass) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + for (final Method method : authorizerClass.getMethods()) { + if (method.isAnnotationPresent(AuthorizerContext.class)) { + // make the method accessible + final boolean isAccessible = method.isAccessible(); + method.setAccessible(true); + + try { + final Class<?>[] argumentTypes = method.getParameterTypes(); + + // look for setters (single argument) + if (argumentTypes.length == 1) { + final Class<?> argumentType = argumentTypes[0]; + + // look for well known types + if (NiFiProperties.class.isAssignableFrom(argumentType)) { + // nifi properties injection + method.invoke(instance, properties); + } + } + } finally { + method.setAccessible(isAccessible); + } + } + } + + final Class parentClass = authorizerClass.getSuperclass(); + if (parentClass != null && Authorizer.class.isAssignableFrom(parentClass)) { + performMethodInjection(instance, parentClass); + } + } + + private void performFieldInjection(final Authorizer instance, final Class authorizerClass) throws IllegalArgumentException, IllegalAccessException { + for (final Field field : authorizerClass.getDeclaredFields()) { + if (field.isAnnotationPresent(AuthorizerContext.class)) { + // make the method accessible + final boolean isAccessible = field.isAccessible(); + field.setAccessible(true); + + try { + // get the type + final Class<?> fieldType = field.getType(); + + // only consider this field if it isn't set yet + if (field.get(instance) == null) { + // look for well known types + if (NiFiProperties.class.isAssignableFrom(fieldType)) { + // nifi properties injection + field.set(instance, properties); + } + } + + } finally { + field.setAccessible(isAccessible); + } + } + } + + final Class parentClass = authorizerClass.getSuperclass(); + if (parentClass != null && Authorizer.class.isAssignableFrom(parentClass)) { + performFieldInjection(instance, parentClass); + } + } + + /** + * @return a default Authorizer to use when running unsecurely with no authorizer configured + */ + private Authorizer createDefaultAuthorizer() { + return new Authorizer() { + @Override + public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException { + return AuthorizationResult.approved(); + } + + @Override + public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + } + }; + } + + /** + * Decorates the base authorizer to ensure the nar context classloader is used when invoking the underlying methods. + * + * @param baseAuthorizer base authorizer + * @return authorizer + */ + public Authorizer withNarLoader(final Authorizer baseAuthorizer) { + return new Authorizer() { + @Override + public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + return baseAuthorizer.authorize(request); + } + } + + @Override + public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseAuthorizer.initialize(initializationContext); + } + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseAuthorizer.onConfigured(configurationContext); + } + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) { + baseAuthorizer.preDestruction(); + } + } + }; + } + + @Override + public Class getObjectType() { + return Authorizer.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + @Override + public void destroy() throws Exception { + if (authorizer != null) { + authorizer.preDestruction(); + } + } + + public void setProperties(NiFiProperties properties) { + this.properties = properties; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/resources/nifi-authorizer-context.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/resources/nifi-authorizer-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/resources/nifi-authorizer-context.xml new file mode 100644 index 0000000..b95db11 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/resources/nifi-authorizer-context.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<beans default-lazy-init="true" + xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> + + <!-- user/entity authorizer --> + <bean id="authorizer" class="org.apache.nifi.authorization.AuthorizerFactoryBean"> + <property name="properties" ref="nifiProperties"/> + <property name="flowController" ref="flowController"/> + </bean> + +</beans> http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/xsd/authorizers.xsd ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/xsd/authorizers.xsd b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/xsd/authorizers.xsd new file mode 100644 index 0000000..4b68b00 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-authorizer/src/main/xsd/authorizers.xsd @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <!-- role --> + <xs:complexType name="Authorizer"> + <xs:sequence> + <xs:element name="identifier" type="NonEmptyStringType"/> + <xs:element name="class" type="NonEmptyStringType"/> + <xs:element name="property" type="Property" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + + <!-- Name/Value properties--> + <xs:complexType name="Property"> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute name="name" type="NonEmptyStringType"></xs:attribute> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:simpleType name="NonEmptyStringType"> + <xs:restriction base="xs:string"> + <xs:minLength value="1"/> + </xs:restriction> + </xs:simpleType> + + <!-- users --> + <xs:element name="authorizers"> + <xs:complexType> + <xs:sequence> + <xs:element name="authorizer" type="Authorizer" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/pom.xml ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/pom.xml index 0673b97..4f97220 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/pom.xml @@ -36,14 +36,28 @@ <artifactId>jaxb2-maven-plugin</artifactId> <executions> <execution> - <id>xjc</id> + <id>authorizations</id> <goals> <goal>xjc</goal> </goals> <configuration> + <schemaDirectory>src/main/xsd</schemaDirectory> + <schemaFiles>authorizations.xsd</schemaFiles> <packageName>org.apache.nifi.authorization.file.generated</packageName> </configuration> </execution> + <execution> + <id>users</id> + <goals> + <goal>xjc</goal> + </goals> + <configuration> + <schemaDirectory>src/main/xsd</schemaDirectory> + <schemaFiles>users.xsd</schemaFiles> + <packageName>org.apache.nifi.user.generated</packageName> + <clearOutputDir>false</clearOutputDir> + </configuration> + </execution> </executions> <configuration> <outputDirectory>${project.build.directory}/generated-sources/jaxb</outputDirectory> @@ -53,7 +67,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-checkstyle-plugin</artifactId> <configuration> - <excludes>**/authorization/file/generated/*.java</excludes> + <excludes>**/authorization/file/generated/*.java,**/user/generated/*.java</excludes> </configuration> </plugin> </plugins> http://git-wip-us.apache.org/repos/asf/nifi/blob/8d8a9cba/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizationsHolder.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizationsHolder.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizationsHolder.java new file mode 100644 index 0000000..e98eed9 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/src/main/java/org/apache/nifi/authorization/AuthorizationsHolder.java @@ -0,0 +1,355 @@ +/* + * 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.authorization; + + +import org.apache.nifi.authorization.file.generated.Authorizations; +import org.apache.nifi.authorization.file.generated.Groups; +import org.apache.nifi.authorization.file.generated.Policies; +import org.apache.nifi.authorization.file.generated.Users; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A holder to provide atomic access to data structures. + */ +public class AuthorizationsHolder implements UsersAndAccessPolicies { + + private final Authorizations authorizations; + + private final Set<AccessPolicy> allPolicies; + private final Map<String, Set<AccessPolicy>> policiesByResource; + private final Map<String, AccessPolicy> policiesById; + + private final Set<User> allUsers; + private final Map<String,User> usersById; + private final Map<String,User> usersByIdentity; + + private final Set<Group> allGroups; + private final Map<String,Group> groupsById; + + /** + * Creates a new holder and populates all convenience data structures. + * + * @param authorizations the current authorizations instance + */ + public AuthorizationsHolder(final Authorizations authorizations) { + this.authorizations = authorizations; + + // load all users + final Users users = authorizations.getUsers(); + final Set<User> allUsers = Collections.unmodifiableSet(createUsers(users)); + + // load all groups + final Groups groups = authorizations.getGroups(); + final Set<Group> allGroups = Collections.unmodifiableSet(createGroups(groups, users)); + + // load all access policies + final Policies policies = authorizations.getPolicies(); + final Set<AccessPolicy> allPolicies = Collections.unmodifiableSet(createAccessPolicies(policies)); + + // create a convenience map to retrieve a user by id + final Map<String, User> userByIdMap = Collections.unmodifiableMap(createUserByIdMap(allUsers)); + + // create a convenience map to retrieve a user by identity + final Map<String, User> userByIdentityMap = Collections.unmodifiableMap(createUserByIdentityMap(allUsers)); + + // create a convenience map to retrieve a group by id + final Map<String, Group> groupByIdMap = Collections.unmodifiableMap(createGroupByIdMap(allGroups)); + + // create a convenience map from resource id to policies + final Map<String, Set<AccessPolicy>> policiesByResourceMap = Collections.unmodifiableMap(createResourcePolicyMap(allPolicies)); + + // create a convenience map from policy id to policy + final Map<String, AccessPolicy> policiesByIdMap = Collections.unmodifiableMap(createPoliciesByIdMap(allPolicies)); + + // set all the holders + this.allUsers = allUsers; + this.allGroups = allGroups; + this.allPolicies = allPolicies; + this.usersById = userByIdMap; + this.usersByIdentity = userByIdentityMap; + this.groupsById = groupByIdMap; + this.policiesByResource = policiesByResourceMap; + this.policiesById = policiesByIdMap; + } + + /** + * Creates AccessPolicies from the JAXB Policies. + * + * @param policies the JAXB Policies element + * @return a set of AccessPolicies corresponding to the provided Resources + */ + private Set<AccessPolicy> createAccessPolicies(org.apache.nifi.authorization.file.generated.Policies policies) { + Set<AccessPolicy> allPolicies = new HashSet<>(); + if (policies == null || policies.getPolicy() == null) { + return allPolicies; + } + + // load the new authorizations + for (final org.apache.nifi.authorization.file.generated.Policy policy : policies.getPolicy()) { + final String policyIdentifier = policy.getIdentifier(); + final String resourceIdentifier = policy.getResource(); + + // start a new builder and set the policy and resource identifiers + final AccessPolicy.Builder builder = new AccessPolicy.Builder() + .identifier(policyIdentifier) + .resource(resourceIdentifier); + + // add each user identifier + for (org.apache.nifi.authorization.file.generated.Policy.User user : policy.getUser()) { + builder.addUser(user.getIdentifier()); + } + + // add each group identifier + for (org.apache.nifi.authorization.file.generated.Policy.Group group : policy.getGroup()) { + builder.addGroup(group.getIdentifier()); + } + + // add the appropriate request actions + final String authorizationCode = policy.getAction(); + if (authorizationCode.contains(FileAuthorizer.READ_CODE)) { + builder.addAction(RequestAction.READ); + } + if (authorizationCode.contains(FileAuthorizer.WRITE_CODE)) { + builder.addAction(RequestAction.WRITE); + } + + // build the policy and add it to the map + allPolicies.add(builder.build()); + } + + return allPolicies; + } + + /** + * Creates a set of Users from the JAXB Users. + * + * @param users the JAXB Users + * @return a set of API Users matching the provided JAXB Users + */ + private Set<User> createUsers(org.apache.nifi.authorization.file.generated.Users users) { + Set<User> allUsers = new HashSet<>(); + if (users == null || users.getUser() == null) { + return allUsers; + } + + for (org.apache.nifi.authorization.file.generated.User user : users.getUser()) { + final User.Builder builder = new User.Builder() + .identity(user.getIdentity()) + .identifier(user.getIdentifier()); + + if (user.getGroup() != null) { + for (org.apache.nifi.authorization.file.generated.User.Group group : user.getGroup()) { + builder.addGroup(group.getIdentifier()); + } + } + + allUsers.add(builder.build()); + } + + return allUsers; + } + + /** + * Creates a set of Groups from the JAXB Groups. + * + * @param groups the JAXB Groups + * @return a set of API Groups matching the provided JAXB Groups + */ + private Set<Group> createGroups(org.apache.nifi.authorization.file.generated.Groups groups, + org.apache.nifi.authorization.file.generated.Users users) { + Set<Group> allGroups = new HashSet<>(); + if (groups == null || groups.getGroup() == null) { + return allGroups; + } + + for (org.apache.nifi.authorization.file.generated.Group group : groups.getGroup()) { + final Group.Builder builder = new Group.Builder() + .identifier(group.getIdentifier()) + .name(group.getName()); + + // need to figured out what users are in this group by going through the users list + final Set<String> groupUsers = getUsersForGroup(users, group.getIdentifier()); + builder.addUsers(groupUsers); + + allGroups.add(builder.build()); + } + + return allGroups; + } + + /** + * Gets the set of user identifiers that are part of the given group. + * + * @param users the JAXB Users element + * @param groupId the group id to get the users for + * @return the user identifiers that belong to the group with the given identifier + */ + private Set<String> getUsersForGroup(org.apache.nifi.authorization.file.generated.Users users, final String groupId) { + Set<String> groupUsers = new HashSet<>(); + + if (users != null && users.getUser()!= null) { + for (org.apache.nifi.authorization.file.generated.User user : users.getUser()) { + if (user.getGroup() != null) { + for (org.apache.nifi.authorization.file.generated.User.Group group : user.getGroup()) { + if (group.getIdentifier().equals(groupId)) { + groupUsers.add(user.getIdentifier()); + break; + } + } + } + } + } + + return groupUsers; + } + + /** + * Creates a map from resource identifier to the set of policies for the given resource. + * + * @param allPolicies the set of all policies + * @return a map from resource identifier to policies + */ + private Map<String, Set<AccessPolicy>> createResourcePolicyMap(final Set<AccessPolicy> allPolicies) { + Map<String, Set<AccessPolicy>> resourcePolicies = new HashMap<>(); + + for (AccessPolicy policy : allPolicies) { + Set<AccessPolicy> policies = resourcePolicies.get(policy.getResource()); + if (policies == null) { + policies = new HashSet<>(); + resourcePolicies.put(policy.getResource(), policies); + } + policies.add(policy); + } + + return resourcePolicies; + } + + /** + * Creates a Map from user identifier to User. + * + * @param users the set of all users + * @return the Map from user identifier to User + */ + private Map<String,User> createUserByIdMap(final Set<User> users) { + Map<String,User> usersMap = new HashMap<>(); + for (User user : users) { + usersMap.put(user.getIdentifier(), user); + } + return usersMap; + } + + /** + * Creates a Map from user identity to User. + * + * @param users the set of all users + * @return the Map from user identity to User + */ + private Map<String,User> createUserByIdentityMap(final Set<User> users) { + Map<String,User> usersMap = new HashMap<>(); + for (User user : users) { + usersMap.put(user.getIdentity(), user); + } + return usersMap; + } + + /** + * Creates a Map from group identifier to Group. + * + * @param groups the set of all groups + * @return the Map from group identifier to Group + */ + private Map<String,Group> createGroupByIdMap(final Set<Group> groups) { + Map<String,Group> groupsMap = new HashMap<>(); + for (Group group : groups) { + groupsMap.put(group.getIdentifier(), group); + } + return groupsMap; + } + + /** + * Creates a Map from policy identifier to AccessPolicy. + * + * @param policies the set of all access policies + * @return the Map from policy identifier to AccessPolicy + */ + private Map<String, AccessPolicy> createPoliciesByIdMap(final Set<AccessPolicy> policies) { + Map<String,AccessPolicy> policyMap = new HashMap<>(); + for (AccessPolicy policy : policies) { + policyMap.put(policy.getIdentifier(), policy); + } + return policyMap; + } + + public Authorizations getAuthorizations() { + return authorizations; + } + + public Set<AccessPolicy> getAllPolicies() { + return allPolicies; + } + + public Map<String, Set<AccessPolicy>> getPoliciesByResource() { + return policiesByResource; + } + + public Map<String, AccessPolicy> getPoliciesById() { + return policiesById; + } + + public Set<User> getAllUsers() { + return allUsers; + } + + public Map<String, User> getUsersById() { + return usersById; + } + + public Map<String, User> getUsersByIdentity() { + return usersByIdentity; + } + + public Set<Group> getAllGroups() { + return allGroups; + } + + public Map<String, Group> getGroupsById() { + return groupsById; + } + + @Override + public Set<AccessPolicy> getAccessPolicies(String resourceIdentifier) { + if (resourceIdentifier == null) { + throw new IllegalArgumentException("Resource Identifier cannot be null"); + } + return policiesByResource.get(resourceIdentifier); + } + + @Override + public User getUser(String identity) { + if (identity == null) { + throw new IllegalArgumentException("Identity cannot be null"); + } + return usersByIdentity.get(identity); + } + +}
