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
This commit is contained in:
Bryan Bende 2016-05-25 13:22:05 -04:00 committed by Matt Gilman
parent 806f4d549d
commit 8d8a9cba79
28 changed files with 2016 additions and 374 deletions

View File

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

View File

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

View File

@ -30,6 +30,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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@ 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;
@ -71,8 +72,13 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
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);
@ -189,7 +195,7 @@ public class AuthorizerFactoryBean implements FactoryBean, DisposableBean, Autho
authorizerProperties.put(property.getName(), property.getValue());
}
return new StandardAuthorizerConfigurationContext(authorizer.getIdentifier(), authorizerProperties);
return new StandardAuthorizerConfigurationContext(authorizer.getIdentifier(), flowController.getRootGroupId(), authorizerProperties);
}
private void performMethodInjection(final Authorizer instance, final Class authorizerClass) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {

View File

@ -21,6 +21,7 @@
<!-- user/entity authorizer -->
<bean id="authorizer" class="org.apache.nifi.authorization.AuthorizerFactoryBean">
<property name="properties" ref="nifiProperties"/>
<property name="flowController" ref="flowController"/>
</bean>
</beans>

View File

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

View File

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

View File

@ -20,51 +20,49 @@ 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.file.generated.Authorization;
import org.apache.nifi.authorization.file.generated.Resources;
import org.apache.nifi.authorization.file.generated.Resource;
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.Policy;
import org.apache.nifi.authorization.file.generated.Users;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.file.FileUtils;
import org.apache.nifi.util.file.monitor.MD5SumMonitor;
import org.apache.nifi.util.file.monitor.SynchronousFileWatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.Marshaller;
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.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
/**
* Provides identity checks and grants authorities.
*/
public class FileAuthorizer implements Authorizer {
public class FileAuthorizer extends AbstractPolicyBasedAuthorizer {
private static final Logger logger = LoggerFactory.getLogger(FileAuthorizer.class);
private static final String READ_CODE = "R";
private static final String WRITE_CODE = "W";
private static final String USERS_XSD = "/authorizations.xsd";
private static final String JAXB_GENERATED_PATH = "org.apache.nifi.authorization.file.generated";
private static final JAXBContext JAXB_CONTEXT = initializeJaxbContext();
static final String READ_CODE = "R";
static final String WRITE_CODE = "W";
/**
* Load the JAXBContext.
*/
@ -76,16 +74,23 @@ public class FileAuthorizer implements Authorizer {
}
}
private Schema schema;
private SchemaFactory schemaFactory;
private NiFiProperties properties;
private File authorizationsFile;
private File restoreAuthorizationsFile;
private SynchronousFileWatcher fileWatcher;
private ScheduledExecutorService fileWatcherExecutorService;
private String rootGroupId;
private final AtomicReference<Map<String, Map<String, Set<RequestAction>>>> authorizations = new AtomicReference<>();
private final AtomicReference<AuthorizationsHolder> authorizationsHolder = new AtomicReference<>();
@Override
public void initialize(final AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException {
try {
schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schema = schemaFactory.newSchema(FileAuthorizer.class.getResource(USERS_XSD));
} catch (Exception e) {
throw new AuthorizerCreationException(e);
}
}
@Override
@ -127,142 +132,142 @@ public class FileAuthorizer implements Authorizer {
}
}
final PropertyValue rawReloadInterval = configurationContext.getProperty("Reload Interval");
final PropertyValue initialAdminIdentityProp = configurationContext.getProperty("Initial Admin Identity");
final String initialAdminIdentity = initialAdminIdentityProp == null ? null : initialAdminIdentityProp.getValue();
long reloadInterval;
try {
reloadInterval = rawReloadInterval.asTimePeriod(TimeUnit.MILLISECONDS);
} catch (final Exception iae) {
logger.info(String.format("Unable to interpret reload interval '%s'. Using default of 30 seconds.", rawReloadInterval));
reloadInterval = 30000L;
// load the authorizations
load(initialAdminIdentity);
// if we've copied the authorizations file to a restore directory synchronize it
if (restoreAuthorizationsFile != null) {
FileUtils.copyFile(authorizationsFile, restoreAuthorizationsFile, false, false, logger);
}
// reload the authorizations
reload();
logger.info(String.format("Authorizations file loaded at %s", new Date().toString()));
// watch the file for modifications
fileWatcher = new SynchronousFileWatcher(authorizationsFile.toPath(), new MD5SumMonitor());
fileWatcherExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(final Runnable r) {
return new Thread(r, "Authorization File Reload Thread");
}
});
fileWatcherExecutorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
if (fileWatcher.checkAndReset()) {
reload();
}
} catch (final Exception e) {
logger.warn("Unable to reload Authorizations file do to: " + e, e);
}
}
}, reloadInterval, reloadInterval, TimeUnit.MILLISECONDS);
} catch (IOException | AuthorizerCreationException | SAXException | JAXBException | IllegalStateException e) {
this.rootGroupId = configurationContext.getRootGroupId();
} catch (IOException | AuthorizerCreationException | JAXBException | IllegalStateException e) {
throw new AuthorizerCreationException(e);
}
}
@Override
public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException {
// get the current authorizations
final Map<String, Map<String, Set<RequestAction>>> currentAuthorizations = authorizations.get();
// get the requested resource
final org.apache.nifi.authorization.Resource requestedResource = request.getResource();
// get the authorizations for the requested resources
final Map<String, Set<RequestAction>> resourceAuthorizations = currentAuthorizations.get(requestedResource.getIdentifier());
// ensure the resource has authorizations
if (resourceAuthorizations == null) {
return AuthorizationResult.resourceNotFound();
}
// get the user authorizations
final Set<RequestAction> userAuthorizations = resourceAuthorizations.get(request.getIdentity());
// ensure the user has authorizations
if (userAuthorizations == null) {
return AuthorizationResult.denied();
}
// ensure the appropriate response
if (userAuthorizations.contains(request.getAction())) {
return AuthorizationResult.approved();
} else {
return AuthorizationResult.denied();
}
}
/**
* Reloads the authorized users file.
* Loads the authorizations file and populates the AuthorizationsHolder, only called during start-up.
*
* @throws SAXException Unable to reload the authorized users file
* @throws JAXBException Unable to reload the authorized users file
* @throws IOException Unable to sync file with restore
* @throws IllegalStateException Unable to sync file with restore
*/
private void reload() throws SAXException, JAXBException, IOException, IllegalStateException {
// find the schema
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
final Schema schema = schemaFactory.newSchema(FileAuthorizer.class.getResource(USERS_XSD));
private synchronized void load(final String initialAdminIdentity) throws JAXBException, IOException, IllegalStateException {
// attempt to unmarshal
final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
unmarshaller.setSchema(schema);
final JAXBElement<Resources> element = unmarshaller.unmarshal(new StreamSource(authorizationsFile), Resources.class);
final Resources resources = element.getValue();
final JAXBElement<Authorizations> element = unmarshaller.unmarshal(new StreamSource(authorizationsFile), Authorizations.class);
// new authorizations
final Map<String, Map<String, Set<RequestAction>>> newAuthorizations = new HashMap<>();
final Authorizations authorizations = element.getValue();
// load the new authorizations
for (final Resource authorizedResource : resources.getResource()) {
final String identifier = authorizedResource.getIdentifier();
// ensure the entry exists
if (!newAuthorizations.containsKey(identifier)) {
newAuthorizations.put(identifier, new HashMap<String, Set<RequestAction>>());
}
// go through each authorization
for (final Authorization authorization : authorizedResource.getAuthorization()) {
final String identity = authorization.getIdentity();
// get the authorizations for this resource
final Map<String, Set<RequestAction>> resourceAuthorizations = newAuthorizations.get(identifier);
// ensure the entry exists
if (!resourceAuthorizations.containsKey(identity)) {
resourceAuthorizations.put(identity, EnumSet.noneOf(RequestAction.class));
}
final Set<RequestAction> authorizedActions = resourceAuthorizations.get(identity);
final String authorizationCode = authorization.getAction();
// updated the actions for this identity
if (authorizationCode.contains(READ_CODE)) {
authorizedActions.add(RequestAction.READ);
}
if (authorizationCode.contains(WRITE_CODE)) {
authorizedActions.add(RequestAction.WRITE);
}
}
if (authorizations.getUsers() == null) {
authorizations.setUsers(new Users());
}
if (authorizations.getGroups() == null) {
authorizations.setGroups(new Groups());
}
if (authorizations.getPolicies() == null) {
authorizations.setPolicies(new Policies());
}
// set the new authorizations
authorizations.set(newAuthorizations);
final AuthorizationsHolder authorizationsHolder = new AuthorizationsHolder(authorizations);
final boolean hasInitialAdminIdentity = (initialAdminIdentity != null && !StringUtils.isBlank(initialAdminIdentity));
// if we've copied a the authorizations file to a restore directory synchronize it
if (restoreAuthorizationsFile != null) {
FileUtils.copyFile(authorizationsFile, restoreAuthorizationsFile, false, false, logger);
// if an initial admin was provided and there are no users or policies then automatically create the admin user & policies
if (hasInitialAdminIdentity && authorizationsHolder.getAllUsers().isEmpty() && authorizationsHolder.getAllPolicies().isEmpty()) {
populateInitialAdmin(authorizations, initialAdminIdentity);
saveAndRefreshHolder(authorizations);
} else {
this.authorizationsHolder.set(authorizationsHolder);
}
}
/**
* Creates the initial admin user and policies for access the flow and managing users and policies.
*
* @param adminIdentity the identity of the admin user
*/
private void populateInitialAdmin(final Authorizations authorizations, final String adminIdentity) {
// generate an identifier and add a User with the given identifier and identity
final UUID adminIdentifier = UUID.nameUUIDFromBytes(adminIdentity.getBytes(StandardCharsets.UTF_8));
final User adminUser = new User.Builder().identifier(adminIdentifier.toString()).identity(adminIdentity).build();
final org.apache.nifi.authorization.file.generated.User jaxbAdminUser = createJAXBUser(adminUser);
authorizations.getUsers().getUser().add(jaxbAdminUser);
// grant the user read access to the /flow resource
final AccessPolicy flowPolicy = createInitialAdminPolicy("/flow", adminUser.getIdentifier(), RequestAction.READ);
final Policy jaxbFlowPolicy = createJAXBPolicy(flowPolicy);
authorizations.getPolicies().getPolicy().add(jaxbFlowPolicy);
// grant the user read/write access to the /users resource
final AccessPolicy usersPolicy = createInitialAdminPolicy("/users", adminUser.getIdentifier(), RequestAction.READ, RequestAction.WRITE);
final Policy jaxbUsersPolicy = createJAXBPolicy(usersPolicy);
authorizations.getPolicies().getPolicy().add(jaxbUsersPolicy);
// grant the user read/write access to the /groups resource
final AccessPolicy groupsPolicy = createInitialAdminPolicy("/groups", adminUser.getIdentifier(), RequestAction.READ, RequestAction.WRITE);
final Policy jaxbGroupsPolicy = createJAXBPolicy(groupsPolicy);
authorizations.getPolicies().getPolicy().add(jaxbGroupsPolicy);
// grant the user read/write access to the /policies resource
final AccessPolicy policiesPolicy = createInitialAdminPolicy("/policies", adminUser.getIdentifier(), RequestAction.READ, RequestAction.WRITE);
final Policy jaxbPoliciesPolicy = createJAXBPolicy(policiesPolicy);
authorizations.getPolicies().getPolicy().add(jaxbPoliciesPolicy);
}
/**
* Creates an AccessPolicy based on the given parameters, generating an identifier from the resource and admin identity.
*
* @param resource the resource for the policy
* @param adminIdentity the identity of the admin user to add to the policy
* @param actions the actions for the policy
* @return the AccessPolicy based on the given parameters
*/
private AccessPolicy createInitialAdminPolicy(final String resource, final String adminIdentity, final RequestAction ... actions) {
final String uuidSeed = resource + adminIdentity;
final UUID flowPolicyIdentifier = UUID.nameUUIDFromBytes(uuidSeed.getBytes(StandardCharsets.UTF_8));
final AccessPolicy.Builder builder = new AccessPolicy.Builder()
.identifier(flowPolicyIdentifier.toString())
.resource(resource)
.addUser(adminIdentity);
for (RequestAction action : actions) {
builder.addAction(action);
}
logger.info(String.format("Authorizations file loaded at %s", new Date().toString()));
return builder.build();
}
/**
* Saves the Authorizations instance by marshalling to a file, then re-populates the
* in-memory data structures and sets the new holder.
*
* Synchronized to ensure only one thread writes the file at a time.
*
* @param authorizations the authorizations to save and populate from
* @throws AuthorizationAccessException if an error occurs saving the authorizations
*/
private synchronized void saveAndRefreshHolder(final Authorizations authorizations) throws AuthorizationAccessException {
try {
final Marshaller marshaller = JAXB_CONTEXT.createMarshaller();
marshaller.setSchema(schema);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(authorizations, authorizationsFile);
final AuthorizationsHolder authorizationsHolder = new AuthorizationsHolder(authorizations);
this.authorizationsHolder.set(authorizationsHolder);
} catch (JAXBException e) {
throw new AuthorizationAccessException("Unable to save Authorizations", e);
}
}
@AuthorizerContext
@ -272,8 +277,404 @@ public class FileAuthorizer implements Authorizer {
@Override
public void preDestruction() {
if (fileWatcherExecutorService != null) {
fileWatcherExecutorService.shutdown();
}
// ------------------ Groups ------------------
@Override
public synchronized Group addGroup(Group group) throws AuthorizationAccessException {
if (group == null) {
throw new IllegalArgumentException("Group cannot be null");
}
// create a new JAXB Group based on the incoming Group
final org.apache.nifi.authorization.file.generated.Group jaxbGroup = new org.apache.nifi.authorization.file.generated.Group();
jaxbGroup.setIdentifier(group.getIdentifier());
jaxbGroup.setName(group.getName());
final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations();
authorizations.getGroups().getGroup().add(jaxbGroup);
saveAndRefreshHolder(authorizations);
final AuthorizationsHolder holder = this.authorizationsHolder.get();
return holder.getGroupsById().get(group.getIdentifier());
}
@Override
public Group getGroup(String identifier) throws AuthorizationAccessException {
if (identifier == null) {
return null;
}
return authorizationsHolder.get().getGroupsById().get(identifier);
}
@Override
public synchronized Group updateGroup(Group group) throws AuthorizationAccessException {
if (group == null) {
throw new IllegalArgumentException("Group cannot be null");
}
final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations();
final List<org.apache.nifi.authorization.file.generated.Group> groups = authorizations.getGroups().getGroup();
// find the group that needs to be update
org.apache.nifi.authorization.file.generated.Group updateGroup = null;
for (org.apache.nifi.authorization.file.generated.Group jaxbGroup : groups) {
if (jaxbGroup.getIdentifier().equals(group.getIdentifier())) {
updateGroup = jaxbGroup;
break;
}
}
// if the group wasn't found return null, otherwise update the group and save changes
if (updateGroup == null) {
return null;
}
updateGroup.setName(group.getName());
saveAndRefreshHolder(authorizations);
final AuthorizationsHolder holder = this.authorizationsHolder.get();
return holder.getGroupsById().get(group.getIdentifier());
}
@Override
public synchronized Group deleteGroup(Group group) throws AuthorizationAccessException {
final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations();
final List<org.apache.nifi.authorization.file.generated.Group> groups = authorizations.getGroups().getGroup();
// for each user iterate over the group references and remove the group reference if it matches the group being deleted
for (org.apache.nifi.authorization.file.generated.User user : authorizations.getUsers().getUser()) {
Iterator<org.apache.nifi.authorization.file.generated.User.Group> userGroupIter = user.getGroup().iterator();
while (userGroupIter.hasNext()) {
org.apache.nifi.authorization.file.generated.User.Group userGroup = userGroupIter.next();
if (userGroup.getIdentifier().equals(group.getIdentifier())) {
userGroupIter.remove();
break;
}
}
}
// for each policy iterate over the group reference and remove the group reference if it matches the group being deleted
for (Policy policy : authorizations.getPolicies().getPolicy()) {
Iterator<Policy.Group> policyGroupIter = policy.getGroup().iterator();
while (policyGroupIter.hasNext()) {
Policy.Group policyGroup = policyGroupIter.next();
if (policyGroup.getIdentifier().equals(group.getIdentifier())) {
policyGroupIter.remove();
break;
}
}
}
// now remove the actual group from the top-level list of groups
boolean removedGroup = false;
Iterator<org.apache.nifi.authorization.file.generated.Group> iter = groups.iterator();
while (iter.hasNext()) {
org.apache.nifi.authorization.file.generated.Group jaxbGroup = iter.next();
if (group.getIdentifier().equals(jaxbGroup.getIdentifier())) {
iter.remove();
removedGroup = true;
break;
}
}
if (removedGroup) {
saveAndRefreshHolder(authorizations);
return group;
} else {
return null;
}
}
@Override
public Set<Group> getGroups() throws AuthorizationAccessException {
return authorizationsHolder.get().getAllGroups();
}
// ------------------ Users ------------------
@Override
public synchronized User addUser(final User user) throws AuthorizationAccessException {
if (user == null) {
throw new IllegalArgumentException("User cannot be null");
}
final org.apache.nifi.authorization.file.generated.User jaxbUser = createJAXBUser(user);
final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations();
authorizations.getUsers().getUser().add(jaxbUser);
saveAndRefreshHolder(authorizations);
final AuthorizationsHolder holder = authorizationsHolder.get();
return holder.getUsersById().get(user.getIdentifier());
}
private org.apache.nifi.authorization.file.generated.User createJAXBUser(User user) {
final org.apache.nifi.authorization.file.generated.User jaxbUser = new org.apache.nifi.authorization.file.generated.User();
jaxbUser.setIdentifier(user.getIdentifier());
jaxbUser.setIdentity(user.getIdentity());
for (String groupIdentifier : user.getGroups()) {
org.apache.nifi.authorization.file.generated.User.Group group = new org.apache.nifi.authorization.file.generated.User.Group();
group.setIdentifier(groupIdentifier);
jaxbUser.getGroup().add(group);
}
return jaxbUser;
}
@Override
public User getUser(final String identifier) throws AuthorizationAccessException {
if (identifier == null) {
return null;
}
final AuthorizationsHolder holder = authorizationsHolder.get();
return holder.getUsersById().get(identifier);
}
@Override
public User getUserByIdentity(final String identity) throws AuthorizationAccessException {
if (identity == null) {
return null;
}
final AuthorizationsHolder holder = authorizationsHolder.get();
return holder.getUsersByIdentity().get(identity);
}
@Override
public synchronized User updateUser(final User user) throws AuthorizationAccessException {
if (user == null) {
throw new IllegalArgumentException("User cannot be null");
}
final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations();
final List<org.apache.nifi.authorization.file.generated.User> users = authorizations.getUsers().getUser();
// fine the User that needs to be updated
org.apache.nifi.authorization.file.generated.User updateUser = null;
for (org.apache.nifi.authorization.file.generated.User jaxbUser : users) {
if (user.getIdentifier().equals(jaxbUser.getIdentifier())) {
updateUser = jaxbUser;
break;
}
}
// if user wasn't found return null, otherwise update the user and save changes
if (updateUser == null) {
return null;
} else {
updateUser.setIdentity(user.getIdentity());
updateUser.getGroup().clear();
for (String groupIdentifier : user.getGroups()) {
org.apache.nifi.authorization.file.generated.User.Group group = new org.apache.nifi.authorization.file.generated.User.Group();
group.setIdentifier(groupIdentifier);
updateUser.getGroup().add(group);
}
saveAndRefreshHolder(authorizations);
final AuthorizationsHolder holder = authorizationsHolder.get();
return holder.getUsersById().get(user.getIdentifier());
}
}
@Override
public synchronized User deleteUser(final User user) throws AuthorizationAccessException {
if (user == null) {
throw new IllegalArgumentException("User cannot be null");
}
final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations();
final List<org.apache.nifi.authorization.file.generated.User> users = authorizations.getUsers().getUser();
// remove any references to the user being deleted from policies
for (Policy policy : authorizations.getPolicies().getPolicy()) {
Iterator<Policy.User> policyUserIter = policy.getUser().iterator();
while (policyUserIter.hasNext()) {
Policy.User policyUser = policyUserIter.next();
if (policyUser.getIdentifier().equals(user.getIdentifier())) {
policyUserIter.remove();
break;
}
}
}
// remove the actual user if it exists
boolean removedUser = false;
Iterator<org.apache.nifi.authorization.file.generated.User> iter = users.iterator();
while (iter.hasNext()) {
org.apache.nifi.authorization.file.generated.User jaxbUser = iter.next();
if (user.getIdentifier().equals(jaxbUser.getIdentifier())) {
iter.remove();
removedUser = true;
break;
}
}
if (removedUser) {
saveAndRefreshHolder(authorizations);
return user;
} else {
return null;
}
}
@Override
public Set<User> getUsers() throws AuthorizationAccessException {
return authorizationsHolder.get().getAllUsers();
}
// ------------------ AccessPolicies ------------------
@Override
public synchronized AccessPolicy addAccessPolicy(final AccessPolicy accessPolicy) throws AuthorizationAccessException {
if (accessPolicy == null) {
throw new IllegalArgumentException("AccessPolicy cannot be null");
}
// create the new JAXB Policy
final Policy policy = createJAXBPolicy(accessPolicy);
// add the new Policy to the top-level list of policies
final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations();
authorizations.getPolicies().getPolicy().add(policy);
saveAndRefreshHolder(authorizations);
final AuthorizationsHolder holder = authorizationsHolder.get();
return holder.getPoliciesById().get(accessPolicy.getIdentifier());
}
private Policy createJAXBPolicy(final AccessPolicy accessPolicy) {
final Policy policy = new Policy();
policy.setIdentifier(accessPolicy.getIdentifier());
transferState(accessPolicy, policy);
return policy;
}
@Override
public AccessPolicy getAccessPolicy(final String identifier) throws AuthorizationAccessException {
if (identifier == null) {
return null;
}
final AuthorizationsHolder holder = authorizationsHolder.get();
return holder.getPoliciesById().get(identifier);
}
@Override
public synchronized AccessPolicy updateAccessPolicy(final AccessPolicy accessPolicy) throws AuthorizationAccessException {
if (accessPolicy == null) {
throw new IllegalArgumentException("AccessPolicy cannot be null");
}
final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations();
// try to find an existing Authorization that matches the policy id
Policy updatePolicy = null;
for (Policy policy : authorizations.getPolicies().getPolicy()) {
if (policy.getIdentifier().equals(accessPolicy.getIdentifier())) {
updatePolicy = policy;
break;
}
}
// no matching Policy so return null
if (updatePolicy == null) {
return null;
}
// update the Policy, save, reload, and return
transferState(accessPolicy, updatePolicy);
saveAndRefreshHolder(authorizations);
final AuthorizationsHolder holder = authorizationsHolder.get();
return holder.getPoliciesById().get(accessPolicy.getIdentifier());
}
@Override
public synchronized AccessPolicy deleteAccessPolicy(final AccessPolicy accessPolicy) throws AuthorizationAccessException {
if (accessPolicy == null) {
throw new IllegalArgumentException("AccessPolicy cannot be null");
}
final Authorizations authorizations = this.authorizationsHolder.get().getAuthorizations();
// find the matching Policy and remove it
boolean deletedPolicy = false;
Iterator<Policy> policyIter = authorizations.getPolicies().getPolicy().iterator();
while (policyIter.hasNext()) {
final Policy policy = policyIter.next();
if (policy.getIdentifier().equals(accessPolicy.getIdentifier())) {
policyIter.remove();
deletedPolicy = true;
break;
}
}
// never found a matching Policy so return null
if (!deletedPolicy) {
return null;
}
saveAndRefreshHolder(authorizations);
return accessPolicy;
}
@Override
public Set<AccessPolicy> getAccessPolicies() throws AuthorizationAccessException {
return authorizationsHolder.get().getAllPolicies();
}
@Override
public UsersAndAccessPolicies getUsersAndAccessPolicies() throws AuthorizationAccessException {
return authorizationsHolder.get();
}
/**
* Sets the given Policy to the state of the provided AccessPolicy. Users and Groups will be cleared and
* set to match the AccessPolicy, the resource and action will be set to match the AccessPolicy.
*
* Does not set the identifier.
*
* @param accessPolicy the AccessPolicy to transfer state from
* @param policy the Policy to transfer state to
*/
private void transferState(AccessPolicy accessPolicy, Policy policy) {
policy.setResource(accessPolicy.getResource());
// add users to the policy
policy.getUser().clear();
for (String userIdentifier : accessPolicy.getUsers()) {
Policy.User policyUser = new Policy.User();
policyUser.setIdentifier(userIdentifier);
policy.getUser().add(policyUser);
}
// add groups to the policy
policy.getGroup().clear();
for (String groupIdentifier : accessPolicy.getGroups()) {
Policy.Group policyGroup = new Policy.Group();
policyGroup.setIdentifier(groupIdentifier);
policy.getGroup().add(policyGroup);
}
// add the action to the policy
boolean containsRead = accessPolicy.getActions().contains(RequestAction.READ);
boolean containsWrite = accessPolicy.getActions().contains(RequestAction.WRITE);
if (containsRead && containsWrite) {
policy.setAction(READ_CODE + WRITE_CODE);
} else if (containsRead) {
policy.setAction(READ_CODE);
} else {
policy.setAction(WRITE_CODE);
}
}
}

View File

@ -14,8 +14,58 @@
limitations under the License.
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- authorization -->
<xs:complexType name="Authorization">
<!-- group -->
<xs:complexType name="Group">
<xs:attribute name="identifier">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="name">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
<!-- groups -->
<xs:complexType name="Groups">
<xs:sequence>
<xs:element name="group" type="Group" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- user -->
<xs:complexType name="User">
<xs:sequence>
<xs:element name="group" minOccurs="0" maxOccurs="unbounded" >
<xs:complexType>
<xs:attribute name="identifier">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="identifier">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="identity">
<xs:simpleType>
<xs:restriction base="xs:string">
@ -24,6 +74,59 @@
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
<!-- users -->
<xs:complexType name="Users">
<xs:sequence>
<xs:element name="user" type="User" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- authorization -->
<xs:complexType name="Policy">
<xs:sequence>
<xs:element name="group" minOccurs="0" maxOccurs="unbounded" >
<xs:complexType>
<xs:attribute name="identifier">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="user" minOccurs="0" maxOccurs="unbounded" >
<xs:complexType>
<xs:attribute name="identifier">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="identifier">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="resource">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="action">
<xs:simpleType>
<xs:restriction base="xs:string">
@ -34,27 +137,22 @@
</xs:attribute>
</xs:complexType>
<!-- resource -->
<xs:complexType name="Resource">
<!-- resources -->
<xs:complexType name="Policies">
<xs:sequence>
<xs:element name="authorization" type="Authorization" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="policy" type="Policy" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="identifier">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
<!-- resources -->
<xs:element name="resources">
<!-- top-level authorizations element -->
<xs:element name="authorizations">
<xs:complexType>
<xs:sequence>
<xs:element name="resource" type="Resource" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="groups" type="Groups" minOccurs="0" maxOccurs="1" />
<xs:element name="users" type="Users" minOccurs="0" maxOccurs="1" />
<xs:element name="policies" type="Policies" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -0,0 +1,64 @@
<?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="Role">
<xs:attribute name="name">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="ROLE_MONITOR"/>
<xs:enumeration value="ROLE_PROVENANCE"/>
<xs:enumeration value="ROLE_DFM"/>
<xs:enumeration value="ROLE_ADMIN"/>
<xs:enumeration value="ROLE_PROXY"/>
<xs:enumeration value="ROLE_NIFI"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
<!-- user -->
<xs:complexType name="User">
<xs:sequence>
<xs:element name="role" type="Role" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="dn">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="group">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*[^\s].*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
<!-- users -->
<xs:element name="users">
<xs:complexType>
<xs:sequence>
<xs:element name="user" type="User" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -31,9 +31,12 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -42,25 +45,57 @@ public class FileAuthorizerTest {
private static final String EMPTY_AUTHORIZATIONS_CONCISE =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<resources/>";
+ "<authorizations/>";
private static final String EMPTY_AUTHORIZATIONS =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<resources>"
+ "</resources>";
+ "<authorizations>"
+ "</authorizations>";
private static final String BAD_SCHEMA_AUTHORIZATIONS =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<resource>"
+ "</resource>";
+ "<authorization>"
+ "</authorization>";
private static final String SIMPLE_AUTHORIZATION_BY_USER =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
"<authorizations>" +
" <users>" +
" <user identifier=\"user-1\" identity=\"user-1\"/>" +
" <user identifier=\"user-2\" identity=\"user-2\"/>" +
" </users>" +
" <policies>" +
" <policy identifier=\"policy-1\" resource=\"/flow\" action=\"R\">" +
" <user identifier=\"user-1\" />" +
" </policy>" +
" </policies>" +
"</authorizations>";
private static final String AUTHORIZATIONS =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "<resources>"
+ "<resource identifier=\"/flow\">"
+ "<authorization identity=\"user-1\" action=\"R\"/>"
+ "</resource>"
+ "</resources>";
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
"<authorizations>" +
" <groups>" +
" <group identifier=\"group-1\" name=\"group-1\" />" +
" <group identifier=\"group-2\" name=\"group-2\" />" +
" </groups>" +
" <users>" +
" <user identifier=\"user-1\" identity=\"user-1\">" +
" <group identifier=\"group-1\" />" +
" <group identifier=\"group-2\" />" +
" </user>\n" +
" <user identifier=\"user-2\" identity=\"user-2\" />" +
" </users>" +
" <policies>" +
" <policy identifier=\"policy-1\" resource=\"/flow\" action=\"RW\">" +
" <group identifier=\"group-1\" />" +
" <group identifier=\"group-2\" />" +
" <user identifier=\"user-1\" />" +
" </policy>" +
" <policy identifier=\"policy-2\" resource=\"/flow\" action=\"RW\">" +
" <user identifier=\"user-2\" />" +
" </policy>" +
" </policies>" +
"</authorizations>";
private static final String UPDATED_AUTHORIZATIONS =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
@ -104,7 +139,39 @@ public class FileAuthorizerTest {
}
@Test
public void testPostConstructionWhenRestoreDoesNotExist() throws Exception {
public void testOnConfiguredWhenInitialAdminProvided() throws Exception {
final String adminIdentity = "admin-user";
when(configurationContext.getProperty(Mockito.eq("Initial Admin Identity")))
.thenReturn(new StandardPropertyValue(adminIdentity, null));
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS_CONCISE);
authorizer.onConfigured(configurationContext);
final Set<User> users = authorizer.getUsers();
assertEquals(1, users.size());
final User adminUser = users.iterator().next();
assertEquals(adminIdentity, adminUser.getIdentity());
final Set<AccessPolicy> policies = authorizer.getAccessPolicies();
assertEquals(4, policies.size());
}
@Test
public void testOnConfiguredWhenInitialAdminNotProvided() throws Exception {
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS_CONCISE);
authorizer.onConfigured(configurationContext);
final Set<User> users = authorizer.getUsers();
assertEquals(0, users.size());
final Set<AccessPolicy> policies = authorizer.getAccessPolicies();
assertEquals(0, policies.size());
}
@Test
public void testOnConfiguredWhenRestoreDoesNotExist() throws Exception {
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS_CONCISE);
authorizer.onConfigured(configurationContext);
@ -112,78 +179,622 @@ public class FileAuthorizerTest {
}
@Test(expected = AuthorizerCreationException.class)
public void testPostConstructionWhenPrimaryDoesNotExist() throws Exception {
public void testOnConfiguredWhenPrimaryDoesNotExist() throws Exception {
writeAuthorizationsFile(restore, EMPTY_AUTHORIZATIONS_CONCISE);
authorizer.onConfigured(configurationContext);
}
@Test(expected = AuthorizerCreationException.class)
public void testPostConstructionWhenPrimaryDifferentThanRestore() throws Exception {
public void testOnConfiguredWhenPrimaryDifferentThanRestore() throws Exception {
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS);
writeAuthorizationsFile(restore, EMPTY_AUTHORIZATIONS_CONCISE);
authorizer.onConfigured(configurationContext);
}
@Test(expected = AuthorizerCreationException.class)
public void testBadSchema() throws Exception {
public void testOnConfiguredWithBadSchema() throws Exception {
writeAuthorizationsFile(primary, BAD_SCHEMA_AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
}
@Test
public void testAuthorizedUserAction() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
writeAuthorizationsFile(primary, SIMPLE_AUTHORIZATION_BY_USER);
authorizer.onConfigured(configurationContext);
final AuthorizationRequest request = new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-1").anonymous(false).accessAttempt(true).action(RequestAction
.READ).build();
final AuthorizationRequest request = new AuthorizationRequest.Builder()
.resource(ResourceFactory.getFlowResource())
.identity("user-1")
.anonymous(false)
.accessAttempt(true)
.action(RequestAction.READ)
.build();
final AuthorizationResult result = authorizer.authorize(request);
assertTrue(Result.Approved.equals(result.getResult()));
}
@Test
public void testUnauthorizedUser() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
writeAuthorizationsFile(primary, SIMPLE_AUTHORIZATION_BY_USER);
authorizer.onConfigured(configurationContext);
final AuthorizationRequest request =
new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-2").anonymous(false).accessAttempt(true).action(RequestAction.READ).build();
final AuthorizationRequest request = new AuthorizationRequest.Builder()
.resource(ResourceFactory.getFlowResource())
.identity("user-2")
.anonymous(false)
.accessAttempt(true)
.action(RequestAction.READ)
.build();
final AuthorizationResult result = authorizer.authorize(request);
assertFalse(Result.Approved.equals(result.getResult()));
}
@Test
public void testUnauthorizedAction() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
writeAuthorizationsFile(primary, SIMPLE_AUTHORIZATION_BY_USER);
authorizer.onConfigured(configurationContext);
final AuthorizationRequest request =
new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-1").anonymous(false).accessAttempt(true).action(RequestAction.WRITE).build();
final AuthorizationRequest request = new AuthorizationRequest.Builder()
.resource(ResourceFactory.getFlowResource())
.identity("user-1")
.anonymous(false)
.accessAttempt(true)
.action(RequestAction.WRITE)
.build();
final AuthorizationResult result = authorizer.authorize(request);
assertFalse(Result.Approved.equals(result.getResult()));
}
@Test
public void testReloadAuthorizations() throws Exception {
public void testGetAllUsersGroupsPolicies() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
when(configurationContext.getProperty(Mockito.eq("Reload Interval"))).thenReturn(new StandardPropertyValue("1 sec", null));
authorizer.onConfigured(configurationContext);
// ensure the user currently does not have write access
final AuthorizationRequest request =
new AuthorizationRequest.Builder().resource(ResourceFactory.getFlowResource()).identity("user-1").anonymous(false).accessAttempt(true).action(RequestAction.WRITE).build();
AuthorizationResult result = authorizer.authorize(request);
assertFalse(Result.Approved.equals(result.getResult()));
final Set<Group> groups = authorizer.getGroups();
assertEquals(2, groups.size());
// add write access for the user
writeAuthorizationsFile(primary, UPDATED_AUTHORIZATIONS);
boolean foundGroup1 = false;
boolean foundGroup2 = false;
// wait at least one second for the file to be stale
Thread.sleep(4000L);
for (Group group : groups) {
if (group.getIdentifier().equals("group-1") && group.getName().equals("group-1")
&& group.getUsers().size() == 1 && group.getUsers().contains("user-1")) {
foundGroup1 = true;
} else if (group.getIdentifier().equals("group-2") && group.getName().equals("group-2")
&& group.getUsers().size() == 1 && group.getUsers().contains("user-1")) {
foundGroup2 = true;
}
}
// ensure the user does have write access now using the same request
result = authorizer.authorize(request);
assertTrue(Result.Approved.equals(result.getResult()));
assertTrue(foundGroup1);
assertTrue(foundGroup2);
final Set<User> users = authorizer.getUsers();
assertEquals(2, users.size());
boolean foundUser1 = false;
boolean foundUser2 = false;
for (User user : users) {
if (user.getIdentifier().equals("user-1") && user.getIdentity().equals("user-1")
&& user.getGroups().size() == 2 && user.getGroups().contains("group-1")
&& user.getGroups().contains("group-2")) {
foundUser1 = true;
} else if (user.getIdentifier().equals("user-2") && user.getIdentity().equals("user-2")
&& user.getGroups().size() == 0) {
foundUser2 = true;
}
}
assertTrue(foundUser1);
assertTrue(foundUser2);
final Set<AccessPolicy> policies = authorizer.getAccessPolicies();
assertEquals(2, policies.size());
boolean foundPolicy1 = false;
boolean foundPolicy2 = false;
for (AccessPolicy policy : policies) {
if (policy.getIdentifier().equals("policy-1")
&& policy.getResource().equals("/flow")
&& policy.getActions().size() == 2
&& policy.getActions().contains(RequestAction.READ)
&& policy.getActions().contains(RequestAction.WRITE)
&& policy.getGroups().size() == 2
&& policy.getGroups().contains("group-1")
&& policy.getGroups().contains("group-2")
&& policy.getUsers().size() == 1
&& policy.getUsers().contains("user-1")) {
foundPolicy1 = true;
} else if (policy.getIdentifier().equals("policy-2")
&& policy.getResource().equals("/flow")
&& policy.getActions().size() == 2
&& policy.getActions().contains(RequestAction.READ)
&& policy.getActions().contains(RequestAction.WRITE)
&& policy.getGroups().size() == 0
&& policy.getUsers().size() == 1
&& policy.getUsers().contains("user-2")) {
foundPolicy2 = true;
}
}
assertTrue(foundPolicy1);
assertTrue(foundPolicy2);
}
// --------------- User Tests ------------------------
@Test
public void testAddUser() throws Exception {
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(0, authorizer.getUsers().size());
final User user = new User.Builder()
.identifier("user-1")
.identity("user-identity-1")
.addGroup("group1")
.addGroup("group2")
.build();
final User addedUser = authorizer.addUser(user);
assertNotNull(addedUser);
assertEquals(user.getIdentifier(), addedUser.getIdentifier());
assertEquals(user.getIdentity(), addedUser.getIdentity());
assertEquals(2, addedUser.getGroups().size());
assertTrue(addedUser.getGroups().contains("group1"));
assertTrue(addedUser.getGroups().contains("group2"));
final Set<User> users = authorizer.getUsers();
assertEquals(1, users.size());
}
@Test
public void testGetUserByIdentifierWhenFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getUsers().size());
final String identifier = "user-1";
final User user = authorizer.getUser(identifier);
assertNotNull(user);
assertEquals(identifier, user.getIdentifier());
}
@Test
public void testGetUserByIdentifierWhenNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getUsers().size());
final String identifier = "user-X";
final User user = authorizer.getUser(identifier);
assertNull(user);
}
@Test
public void testGetUserByIdentityWhenFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getUsers().size());
final String identity = "user-1";
final User user = authorizer.getUserByIdentity(identity);
assertNotNull(user);
assertEquals(identity, user.getIdentifier());
}
@Test
public void testGetUserByIdentityWhenNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getUsers().size());
final String identity = "user-X";
final User user = authorizer.getUserByIdentity(identity);
assertNull(user);
}
@Test
public void testDeleteUser() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getUsers().size());
// retrieve user-1 and verify it exsits
final User user = authorizer.getUser("user-1");
assertEquals("user-1", user.getIdentifier());
final AccessPolicy policy1 = authorizer.getAccessPolicy("policy-1");
assertTrue(policy1.getUsers().contains("user-1"));
// delete user-1
final User deletedUser = authorizer.deleteUser(user);
assertNotNull(deletedUser);
assertEquals("user-1", deletedUser.getIdentifier());
// should be one less user
assertEquals(1, authorizer.getUsers().size());
assertNull(authorizer.getUser(user.getIdentifier()));
// verify policy-1 no longer has a reference to user-1
final AccessPolicy updatedPolicy1 = authorizer.getAccessPolicy("policy-1");
assertFalse(updatedPolicy1.getUsers().contains("user-1"));
}
@Test
public void testDeleteUserWhenNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getUsers().size());
//user that doesn't exist
final User user = new User.Builder().identifier("user-X").identity("user-identity-X").build();
// should return null and still have 2 users because nothing was deleted
final User deletedUser = authorizer.deleteUser(user);
assertNull(deletedUser);
assertEquals(2, authorizer.getUsers().size());
}
@Test
public void testUpdateUserWhenFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getUsers().size());
final User user = new User.Builder()
.identifier("user-1")
.identity("new-identity")
.addGroup("new-group")
.build();
final User updatedUser = authorizer.updateUser(user);
assertNotNull(updatedUser);
assertEquals(user.getIdentifier(), updatedUser.getIdentifier());
assertEquals(user.getIdentity(), updatedUser.getIdentity());
assertEquals(1, updatedUser.getGroups().size());
assertTrue(updatedUser.getGroups().contains("new-group"));
}
@Test
public void testUpdateUserWhenNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getUsers().size());
final User user = new User.Builder()
.identifier("user-X")
.identity("new-identity")
.addGroup("new-group")
.build();
final User updatedUser = authorizer.updateUser(user);
assertNull(updatedUser);
}
// --------------- Group Tests ------------------------
@Test
public void testAddGroup() throws Exception {
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(0, authorizer.getGroups().size());
final Group group = new Group.Builder()
.identifier("group-id-1")
.name("group-name-1")
.addUser("user1") // should be ignored
.build();
final Group addedGroup = authorizer.addGroup(group);
assertNotNull(addedGroup);
assertEquals(group.getIdentifier(), addedGroup.getIdentifier());
assertEquals(group.getName(), addedGroup.getName());
assertEquals(0, addedGroup.getUsers().size());
final Set<Group> groups = authorizer.getGroups();
assertEquals(1, groups.size());
}
@Test
public void testGetGroupByIdentifierWhenFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getGroups().size());
final String identifier = "group-1";
final Group group = authorizer.getGroup(identifier);
assertNotNull(group);
assertEquals(identifier, group.getIdentifier());
}
@Test
public void testGetGroupByIdentifierWhenNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getGroups().size());
final String identifier = "group-X";
final Group group = authorizer.getGroup(identifier);
assertNull(group);
}
@Test
public void testDeleteGroupWhenFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getGroups().size());
// retrieve user-1 and verify its in group-1
final User user1 = authorizer.getUser("user-1");
assertNotNull(user1);
assertEquals(2, user1.getGroups().size());
assertTrue(user1.getGroups().contains("group-1"));
final AccessPolicy policy1 = authorizer.getAccessPolicy("policy-1");
assertTrue(policy1.getGroups().contains("group-1"));
// retrieve group-1
final Group group = authorizer.getGroup("group-1");
assertEquals("group-1", group.getIdentifier());
// delete group-1
final Group deletedGroup = authorizer.deleteGroup(group);
assertNotNull(deletedGroup);
assertEquals("group-1", deletedGroup.getIdentifier());
// verify there is one less overall group
assertEquals(1, authorizer.getGroups().size());
// verify we can no longer retrieve group-1 by identifier
assertNull(authorizer.getGroup(group.getIdentifier()));
// verify user-1 is no longer in group-1
final User updatedUser1 = authorizer.getUser("user-1");
assertNotNull(updatedUser1);
assertEquals(1, updatedUser1.getGroups().size());
assertFalse(updatedUser1.getGroups().contains("group-1"));
// verify group-1 is no longer in policy-1
final AccessPolicy updatedPolicy1 = authorizer.getAccessPolicy("policy-1");
assertFalse(updatedPolicy1.getGroups().contains("group-1"));
}
@Test
public void testDeleteGroupWhenNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getGroups().size());
final Group group = new Group.Builder()
.identifier("group-id-X")
.name("group-name-X")
.build();
final Group deletedGroup = authorizer.deleteGroup(group);
assertNull(deletedGroup);
assertEquals(2, authorizer.getGroups().size());
}
@Test
public void testUpdateGroupWhenFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getGroups().size());
final Group group = new Group.Builder()
.identifier("group-1")
.name("new-name")
.build();
final Group updatedGroup = authorizer.updateGroup(group);
assertEquals(group.getIdentifier(), updatedGroup.getIdentifier());
assertEquals(group.getName(), updatedGroup.getName());
}
@Test
public void testUpdateGroupWhenNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getGroups().size());
final Group group = new Group.Builder()
.identifier("group-X")
.name("group-X")
.build();
final Group updatedGroup = authorizer.updateGroup(group);
assertNull(updatedGroup);
assertEquals(2, authorizer.getGroups().size());
}
// --------------- AccessPolicy Tests ------------------------
@Test
public void testAddAccessPolicy() throws Exception {
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(0, authorizer.getAccessPolicies().size());
final AccessPolicy policy1 = new AccessPolicy.Builder()
.identifier("policy-1")
.resource("resource-1")
.addUser("user-1")
.addGroup("group-1")
.addAction(RequestAction.READ)
.build();
final AccessPolicy returnedPolicy1 = authorizer.addAccessPolicy(policy1);
assertNotNull(returnedPolicy1);
assertEquals(policy1.getIdentifier(), returnedPolicy1.getIdentifier());
assertEquals(policy1.getResource(), returnedPolicy1.getResource());
assertEquals(policy1.getUsers(), returnedPolicy1.getUsers());
assertEquals(policy1.getGroups(), returnedPolicy1.getGroups());
assertEquals(policy1.getActions(), returnedPolicy1.getActions());
assertEquals(1, authorizer.getAccessPolicies().size());
// second policy for the same resource
final AccessPolicy policy2 = new AccessPolicy.Builder()
.identifier("policy-2")
.resource("resource-1")
.addUser("user-1")
.addGroup("group-1")
.addAction(RequestAction.READ)
.build();
final AccessPolicy returnedPolicy2 = authorizer.addAccessPolicy(policy2);
assertNotNull(returnedPolicy2);
assertEquals(2, authorizer.getAccessPolicies().size());
}
@Test
public void testAddAccessPolicyWithEmptyUsersAndGroups() throws Exception {
writeAuthorizationsFile(primary, EMPTY_AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(0, authorizer.getAccessPolicies().size());
final AccessPolicy policy1 = new AccessPolicy.Builder()
.identifier("policy-1")
.resource("resource-1")
.addAction(RequestAction.READ)
.build();
final AccessPolicy returnedPolicy1 = authorizer.addAccessPolicy(policy1);
assertNotNull(returnedPolicy1);
assertEquals(policy1.getIdentifier(), returnedPolicy1.getIdentifier());
assertEquals(policy1.getResource(), returnedPolicy1.getResource());
assertEquals(policy1.getUsers(), returnedPolicy1.getUsers());
assertEquals(policy1.getGroups(), returnedPolicy1.getGroups());
assertEquals(policy1.getActions(), returnedPolicy1.getActions());
assertEquals(1, authorizer.getAccessPolicies().size());
}
@Test
public void testGetAccessPolicy() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getAccessPolicies().size());
final AccessPolicy policy = authorizer.getAccessPolicy("policy-1");
assertNotNull(policy);
assertEquals("policy-1", policy.getIdentifier());
assertEquals("/flow", policy.getResource());
assertEquals(2, policy.getActions().size());
assertTrue(policy.getActions().contains(RequestAction.WRITE));
assertTrue(policy.getActions().contains(RequestAction.READ));
assertEquals(1, policy.getUsers().size());
assertTrue(policy.getUsers().contains("user-1"));
assertEquals(2, policy.getGroups().size());
assertTrue(policy.getGroups().contains("group-1"));
assertTrue(policy.getGroups().contains("group-2"));
}
@Test
public void testGetAccessPolicyWhenNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getAccessPolicies().size());
final AccessPolicy policy = authorizer.getAccessPolicy("policy-X");
assertNull(policy);
}
@Test
public void testUpdateAccessPolicy() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getAccessPolicies().size());
final AccessPolicy policy = new AccessPolicy.Builder()
.identifier("policy-1")
.resource("resource-A")
.addUser("user-A")
.addGroup("group-A")
.addAction(RequestAction.READ)
.build();
final AccessPolicy updateAccessPolicy = authorizer.updateAccessPolicy(policy);
assertNotNull(updateAccessPolicy);
assertEquals("policy-1", updateAccessPolicy.getIdentifier());
assertEquals("resource-A", updateAccessPolicy.getResource());
assertEquals(1, updateAccessPolicy.getUsers().size());
assertTrue(updateAccessPolicy.getUsers().contains("user-A"));
assertEquals(1, updateAccessPolicy.getGroups().size());
assertTrue(updateAccessPolicy.getGroups().contains("group-A"));
assertEquals(1, updateAccessPolicy.getActions().size());
assertTrue(updateAccessPolicy.getActions().contains(RequestAction.READ));
}
@Test
public void testUpdateAccessPolicyWhenResourceNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getAccessPolicies().size());
final AccessPolicy policy = new AccessPolicy.Builder()
.identifier("policy-XXX")
.resource("resource-A")
.addUser("user-A")
.addGroup("group-A")
.addAction(RequestAction.READ)
.build();
final AccessPolicy updateAccessPolicy = authorizer.updateAccessPolicy(policy);
assertNull(updateAccessPolicy);
}
@Test
public void testDeleteAccessPolicy() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getAccessPolicies().size());
final AccessPolicy policy = new AccessPolicy.Builder()
.identifier("policy-1")
.resource("resource-A")
.addUser("user-A")
.addGroup("group-A")
.addAction(RequestAction.READ)
.build();
final AccessPolicy deletedAccessPolicy = authorizer.deleteAccessPolicy(policy);
assertNotNull(deletedAccessPolicy);
assertEquals(policy.getIdentifier(), deletedAccessPolicy.getIdentifier());
// should have one less policy, and get by policy id should return null
assertEquals(1, authorizer.getAccessPolicies().size());
assertNull(authorizer.getAccessPolicy(policy.getIdentifier()));
}
@Test
public void testDeleteAccessPolicyWhenNotFound() throws Exception {
writeAuthorizationsFile(primary, AUTHORIZATIONS);
authorizer.onConfigured(configurationContext);
assertEquals(2, authorizer.getAccessPolicies().size());
final AccessPolicy policy = new AccessPolicy.Builder()
.identifier("policy-XXX")
.resource("resource-A")
.addUser("user-A")
.addGroup("group-A")
.addAction(RequestAction.READ)
.build();
final AccessPolicy deletedAccessPolicy = authorizer.deleteAccessPolicy(policy);
assertNull(deletedAccessPolicy);
}
private static void writeAuthorizationsFile(final File file, final String content) throws Exception {

View File

@ -21,40 +21,7 @@
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-framework-authorization</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>
@ -64,22 +31,10 @@
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-expression-language</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-nar-utils</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>

View File

@ -29,10 +29,12 @@ import java.util.Map;
public class StandardAuthorizerConfigurationContext implements AuthorizerConfigurationContext {
private final String identifier;
private final String rootGroupId;
private final Map<String, String> properties;
public StandardAuthorizerConfigurationContext(String identifier, Map<String, String> properties) {
public StandardAuthorizerConfigurationContext(String identifier, String rootGroupId, Map<String, String> properties) {
this.identifier = identifier;
this.rootGroupId = rootGroupId;
this.properties = Collections.unmodifiableMap(new HashMap<String, String>(properties));
}
@ -41,6 +43,11 @@ public class StandardAuthorizerConfigurationContext implements AuthorizerConfigu
return identifier;
}
@Override
public String getRootGroupId() {
return rootGroupId;
}
@Override
public Map<String, String> getProperties() {
return properties;

View File

@ -56,12 +56,17 @@
/{type}/{id}/provenance - READ - allows user/entity to view provenance data from the underlying component
- WRITE - NA
-->
<resources>
<authorizations>
<!--
<resource identifier="/flow">
<authorization identity="user-identity-1" action="R"></authorization>
<authorization identity="user-identity-2" action="W"></authorization>
<authorization identity="user-identity-3" action="RW"></authorization>
</resource>
<users>
<user identifier="1" identity="" />
</users>
<policies>
<policy identifier="1" resource="/flow" action="RW">
<user identifier="1" />
</policy>
</policies>
-->
</resources>
</authorizations>

View File

@ -23,6 +23,6 @@
<identifier>file-provider</identifier>
<class>org.apache.nifi.authorization.FileAuthorizer</class>
<property name="Authorizations File">./conf/authorizations.xml</property>
<property name="Reload Interval">30 secs</property>
<property name="Initial Admin Identity"></property>
</authorizer>
</authorizers>

View File

@ -27,7 +27,7 @@ import org.springframework.context.annotation.ImportResource;
@Import({NiFiWebApiSecurityConfiguration.class})
@ImportResource({"classpath:nifi-context.xml",
"classpath:nifi-administration-context.xml",
"classpath:nifi-framework-authorization-context.xml",
"classpath:nifi-authorizer-context.xml",
"classpath:nifi-cluster-manager-context.xml",
"classpath:nifi-cluster-protocol-context.xml",
"classpath:nifi-web-security-context.xml",

View File

@ -39,6 +39,7 @@
<module>nifi-web</module>
<module>nifi-resources</module>
<module>nifi-documentation</module>
<module>nifi-authorizer</module>
</modules>
<dependencies>
<dependency>

View File

@ -113,6 +113,11 @@
<artifactId>nifi-file-authorizer</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-authorizer</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>