Repository: nifi Updated Branches: refs/heads/master ad6af1d94 -> 6bc6f955c
http://git-wip-us.apache.org/repos/asf/nifi/blob/6bc6f955/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/tenants/TenantHolder.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/tenants/TenantHolder.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/tenants/TenantHolder.java new file mode 100644 index 0000000..2c0680e --- /dev/null +++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/java/org/apache/nifi/ldap/tenants/TenantHolder.java @@ -0,0 +1,165 @@ +/* + * 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.ldap.tenants; + + +import org.apache.nifi.authorization.Group; +import org.apache.nifi.authorization.User; + +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 user group data structures. + */ +public class TenantHolder { + + 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; + private final Map<String, Set<Group>> groupsByUserIdentity; + + /** + * Creates a new holder and populates all convenience data structures. + */ + public TenantHolder(final Set<User> allUsers, final Set<Group> allGroups) { + // 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 to retrieve the groups for a user identity + final Map<String, Set<Group>> groupsByUserIdentityMap = Collections.unmodifiableMap(createGroupsByUserIdentityMap(allGroups, allUsers)); + + // set all the holders + this.allUsers = allUsers; + this.allGroups = allGroups; + this.usersById = userByIdMap; + this.usersByIdentity = userByIdentityMap; + this.groupsById = groupByIdMap; + this.groupsByUserIdentity = groupsByUserIdentityMap; + } + + /** + * 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 user identity to the set of Groups for that identity. + * + * @param groups all groups + * @param users all users + * @return a Map from User identity to the set of Groups for that identity + */ + private Map<String, Set<Group>> createGroupsByUserIdentityMap(final Set<Group> groups, final Set<User> users) { + Map<String, Set<Group>> groupsByUserIdentity = new HashMap<>(); + + for (User user : users) { + Set<Group> userGroups = new HashSet<>(); + for (Group group : groups) { + for (String groupUser : group.getUsers()) { + if (groupUser.equals(user.getIdentifier())) { + userGroups.add(group); + } + } + } + + groupsByUserIdentity.put(user.getIdentity(), userGroups); + } + + return groupsByUserIdentity; + } + + public Set<User> getAllUsers() { + return allUsers; + } + + public Map<String, User> getUsersById() { + return usersById; + } + + public Set<Group> getAllGroups() { + return allGroups; + } + + public Map<String, Group> getGroupsById() { + return groupsById; + } + + public User getUser(String identity) { + if (identity == null) { + throw new IllegalArgumentException("Identity cannot be null"); + } + return usersByIdentity.get(identity); + } + + public Set<Group> getGroups(String userIdentity) { + if (userIdentity == null) { + throw new IllegalArgumentException("User Identity cannot be null"); + } + return groupsByUserIdentity.get(userIdentity); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/6bc6f955/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authorization.UserGroupProvider ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authorization.UserGroupProvider b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authorization.UserGroupProvider new file mode 100755 index 0000000..1e80cc2 --- /dev/null +++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/main/resources/META-INF/services/org.apache.nifi.authorization.UserGroupProvider @@ -0,0 +1,15 @@ +# 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. +org.apache.nifi.ldap.tenants.LdapUserGroupProvider http://git-wip-us.apache.org/repos/asf/nifi/blob/6bc6f955/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/test/java/org/apache/nifi/ldap/tenants/LdapUserGroupProviderTest.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/test/java/org/apache/nifi/ldap/tenants/LdapUserGroupProviderTest.java b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/test/java/org/apache/nifi/ldap/tenants/LdapUserGroupProviderTest.java new file mode 100644 index 0000000..f2cc28e --- /dev/null +++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/test/java/org/apache/nifi/ldap/tenants/LdapUserGroupProviderTest.java @@ -0,0 +1,552 @@ +/* + * 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.ldap.tenants; + +import org.apache.directory.server.annotations.CreateLdapServer; +import org.apache.directory.server.annotations.CreateTransport; +import org.apache.directory.server.core.annotations.ApplyLdifFiles; +import org.apache.directory.server.core.annotations.CreateDS; +import org.apache.directory.server.core.annotations.CreatePartition; +import org.apache.directory.server.core.integ.AbstractLdapTestUnit; +import org.apache.directory.server.core.integ.FrameworkRunner; +import org.apache.nifi.attribute.expression.language.StandardPropertyValue; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.Group; +import org.apache.nifi.authorization.UserAndGroups; +import org.apache.nifi.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.ldap.LdapAuthenticationStrategy; +import org.apache.nifi.ldap.ReferralStrategy; +import org.apache.nifi.util.NiFiProperties; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.Properties; +import java.util.Set; + +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_AUTHENTICATION_STRATEGY; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_CONNECT_TIMEOUT; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_GROUP_MEMBER_ATTRIBUTE; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_GROUP_NAME_ATTRIBUTE; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_GROUP_OBJECT_CLASS; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_GROUP_SEARCH_BASE; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_GROUP_SEARCH_FILTER; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_GROUP_SEARCH_SCOPE; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_MANAGER_DN; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_MANAGER_PASSWORD; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_PAGE_SIZE; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_READ_TIMEOUT; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_REFERRAL_STRATEGY; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_SYNC_INTERVAL; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_URL; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_USER_GROUP_ATTRIBUTE; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_USER_IDENTITY_ATTRIBUTE; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_USER_OBJECT_CLASS; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_USER_SEARCH_BASE; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_USER_SEARCH_FILTER; +import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_USER_SEARCH_SCOPE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(FrameworkRunner.class) +@CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP")}) +@CreateDS(name = "nifi-example", partitions = {@CreatePartition(name = "example", suffix = "o=nifi")}) +@ApplyLdifFiles("nifi-example.ldif") +public class LdapUserGroupProviderTest extends AbstractLdapTestUnit { + + private static final String USER_SEARCH_BASE = "ou=users,o=nifi"; + private static final String GROUP_SEARCH_BASE = "ou=groups,o=nifi"; + + private LdapUserGroupProvider ldapUserGroupProvider; + + @Before + public void setup() { + final UserGroupProviderInitializationContext initializationContext = mock(UserGroupProviderInitializationContext.class); + when(initializationContext.getIdentifier()).thenReturn("identifier"); + + ldapUserGroupProvider = new LdapUserGroupProvider(); + ldapUserGroupProvider.setNiFiProperties(getNiFiProperties(new Properties())); + ldapUserGroupProvider.initialize(initializationContext); + } + + @Test(expected = AuthorizerCreationException.class) + public void testNoSearchBasesSpecified() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, null); + ldapUserGroupProvider.onConfigured(configurationContext); + } + + @Test(expected = AuthorizerCreationException.class) + public void testUserSearchBaseSpecifiedButNoUserObjectClass() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_OBJECT_CLASS)).thenReturn(new StandardPropertyValue(null, null)); + ldapUserGroupProvider.onConfigured(configurationContext); + } + + @Test(expected = AuthorizerCreationException.class) + public void testUserSearchBaseSpecifiedButNoUserSearchScope() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue(null, null)); + ldapUserGroupProvider.onConfigured(configurationContext); + } + + @Test(expected = AuthorizerCreationException.class) + public void testInvalidUserSearchScope() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue("not-valid", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + } + + @Test + public void testSearchUsersWithNoIdentityAttribute() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + assertNotNull(ldapUserGroupProvider.getUserByIdentity("cn=User 1,ou=users,o=nifi")); + assertTrue(ldapUserGroupProvider.getGroups().isEmpty()); + } + + @Test + public void testSearchUsersWithUidIdentityAttribute() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + assertNotNull(ldapUserGroupProvider.getUserByIdentity("user1")); + assertTrue(ldapUserGroupProvider.getGroups().isEmpty()); + } + + @Test + public void testSearchUsersWithCnIdentityAttribute() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + assertNotNull(ldapUserGroupProvider.getUserByIdentity("User 1")); + assertTrue(ldapUserGroupProvider.getGroups().isEmpty()); + } + + @Test + public void testSearchUsersObjectSearchScope() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue(SearchScope.OBJECT.name(), null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertTrue(ldapUserGroupProvider.getUsers().isEmpty()); + assertTrue(ldapUserGroupProvider.getGroups().isEmpty()); + } + + @Test + public void testSearchUsersSubtreeSearchScope() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration("o=nifi", null); + when(configurationContext.getProperty(PROP_USER_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue(SearchScope.SUBTREE.name(), null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + assertTrue(ldapUserGroupProvider.getGroups().isEmpty()); + } + + @Test + public void testSearchUsersWithFilter() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null)); + when(configurationContext.getProperty(PROP_USER_SEARCH_FILTER)).thenReturn(new StandardPropertyValue("(uid=user1)", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(1, ldapUserGroupProvider.getUsers().size()); + assertNotNull(ldapUserGroupProvider.getUserByIdentity("user1")); + assertTrue(ldapUserGroupProvider.getGroups().isEmpty()); + } + + @Test + public void testSearchUsersWithPaging() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_PAGE_SIZE)).thenReturn(new StandardPropertyValue("1", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + assertTrue(ldapUserGroupProvider.getGroups().isEmpty()); + } + + @Test + public void testSearchUsersWithGroupingNoGroupName() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null)); + when(configurationContext.getProperty(PROP_USER_GROUP_ATTRIBUTE)).thenReturn(new StandardPropertyValue("description", null)); // using description in lieu of memberof + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + assertEquals(2, ldapUserGroupProvider.getGroups().size()); + + final UserAndGroups userAndGroups = ldapUserGroupProvider.getUserAndGroups("user4"); + assertNotNull(userAndGroups.getUser()); + assertEquals(1, userAndGroups.getGroups().size()); + assertEquals("cn=team1,ou=groups,o=nifi", userAndGroups.getGroups().iterator().next().getName()); + } + + @Test + public void testSearchUsersWithGroupingAndGroupName() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null)); + when(configurationContext.getProperty(PROP_USER_GROUP_ATTRIBUTE)).thenReturn(new StandardPropertyValue("description", null)); // using description in lieu of memberof + when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + assertEquals(2, ldapUserGroupProvider.getGroups().size()); + + final UserAndGroups userAndGroups = ldapUserGroupProvider.getUserAndGroups("user4"); + assertNotNull(userAndGroups.getUser()); + assertEquals(1, userAndGroups.getGroups().size()); + assertEquals("team1", userAndGroups.getGroups().iterator().next().getName()); + } + + @Test(expected = AuthorizerCreationException.class) + public void testSearchGroupsWithoutMemberAttribute() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + ldapUserGroupProvider.onConfigured(configurationContext); + } + + @Test(expected = AuthorizerCreationException.class) + public void testGroupSearchBaseSpecifiedButNoGroupObjectClass() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_OBJECT_CLASS)).thenReturn(new StandardPropertyValue(null, null)); + ldapUserGroupProvider.onConfigured(configurationContext); + } + + @Test(expected = AuthorizerCreationException.class) + public void testUserSearchBaseSpecifiedButNoGroupSearchScope() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue(null, null)); + ldapUserGroupProvider.onConfigured(configurationContext); + } + + @Test(expected = AuthorizerCreationException.class) + public void testInvalidGroupSearchScope() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue("not-valid", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + } + + @Test + public void testSearchGroupsWithNoNameAttribute() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + final Set<Group> groups = ldapUserGroupProvider.getGroups(); + assertEquals(4, groups.size()); + assertEquals(1, groups.stream().filter(group -> "cn=admins,ou=groups,o=nifi".equals(group.getName())).count()); + } + + @Test + public void testSearchGroupsWithPaging() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_PAGE_SIZE)).thenReturn(new StandardPropertyValue("1", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(4, ldapUserGroupProvider.getGroups().size()); + } + + @Test + public void testSearchGroupsObjectSearchScope() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue(SearchScope.OBJECT.name(), null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertTrue(ldapUserGroupProvider.getUsers().isEmpty()); + assertTrue(ldapUserGroupProvider.getGroups().isEmpty()); + } + + @Test + public void testSearchGroupsSubtreeSearchScope() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, "o=nifi"); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue(SearchScope.SUBTREE.name(), null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(4, ldapUserGroupProvider.getGroups().size()); + } + + @Test + public void testSearchGroupsWithNameAttribute() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + final Set<Group> groups = ldapUserGroupProvider.getGroups(); + assertEquals(4, groups.size()); + + final Group admins = groups.stream().filter(group -> "admins".equals(group.getName())).findFirst().orElse(null); + assertNotNull(admins); + assertFalse(admins.getUsers().isEmpty()); + assertEquals(1, admins.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "cn=User 1,ou=users,o=nifi".equals(user.getIdentity())).count()); + } + + @Test + public void testSearchGroupsWithNoNameAndUserIdentityUidAttribute() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + final Set<Group> groups = ldapUserGroupProvider.getGroups(); + assertEquals(4, groups.size()); + + final Group admins = groups.stream().filter(group -> "cn=admins,ou=groups,o=nifi".equals(group.getName())).findFirst().orElse(null); + assertNotNull(admins); + assertFalse(admins.getUsers().isEmpty()); + assertEquals(1, admins.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user1".equals(user.getIdentity())).count()); + } + + @Test + public void testSearchGroupsWithNameAndUserIdentityCnAttribute() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null)); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + final Set<Group> groups = ldapUserGroupProvider.getGroups(); + assertEquals(4, groups.size()); + + final Group admins = groups.stream().filter(group -> "admins".equals(group.getName())).findFirst().orElse(null); + assertNotNull(admins); + assertFalse(admins.getUsers().isEmpty()); + assertEquals(1, admins.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "User 1".equals(user.getIdentity())).count()); + } + + @Test + public void testSearchGroupsWithFilter() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_SEARCH_FILTER)).thenReturn(new StandardPropertyValue("(cn=admins)", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + final Set<Group> groups = ldapUserGroupProvider.getGroups(); + assertEquals(1, groups.size()); + assertEquals(1, groups.stream().filter(group -> "cn=admins,ou=groups,o=nifi".equals(group.getName())).count()); + } + + @Test + public void testSearchUsersAndGroupsNoMembership() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, GROUP_SEARCH_BASE); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + + final Set<Group> groups = ldapUserGroupProvider.getGroups(); + assertEquals(4, groups.size()); + groups.forEach(group -> assertTrue(group.getUsers().isEmpty())); + } + + @Test + public void testSearchUsersAndGroupsMembershipThroughUsers() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null)); + when(configurationContext.getProperty(PROP_USER_GROUP_ATTRIBUTE)).thenReturn(new StandardPropertyValue("description", null)); // using description in lieu of memberof + when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + + final Set<Group> groups = ldapUserGroupProvider.getGroups(); + assertEquals(4, groups.size()); + + final Group team1 = groups.stream().filter(group -> "team1".equals(group.getName())).findFirst().orElse(null); + assertNotNull(team1); + assertEquals(2, team1.getUsers().size()); + assertEquals(2, team1.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user4".equals(user.getIdentity()) || "user5".equals(user.getIdentity())).count()); + + final Group team2 = groups.stream().filter(group -> "team2".equals(group.getName())).findFirst().orElse(null); + assertNotNull(team2); + assertEquals(2, team2.getUsers().size()); + assertEquals(2, team2.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user6".equals(user.getIdentity()) || "user7".equals(user.getIdentity())).count()); + } + + @Test + public void testSearchUsersAndGroupsMembershipThroughGroups() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null)); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + + final Set<Group> groups = ldapUserGroupProvider.getGroups(); + assertEquals(4, groups.size()); + + final Group admins = groups.stream().filter(group -> "admins".equals(group.getName())).findFirst().orElse(null); + assertNotNull(admins); + assertEquals(2, admins.getUsers().size()); + assertEquals(2, admins.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user1".equals(user.getIdentity()) || "user3".equals(user.getIdentity())).count()); + + final Group readOnly = groups.stream().filter(group -> "read-only".equals(group.getName())).findFirst().orElse(null); + assertNotNull(readOnly); + assertEquals(1, readOnly.getUsers().size()); + assertEquals(1, readOnly.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user2".equals(user.getIdentity())).count()); + + final Group team1 = groups.stream().filter(group -> "team1".equals(group.getName())).findFirst().orElse(null); + assertNotNull(team1); + assertEquals(1, team1.getUsers().size()); + assertEquals(1, team1.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user1".equals(user.getIdentity())).count()); + + final Group team2 = groups.stream().filter(group -> "team2".equals(group.getName())).findFirst().orElse(null); + assertNotNull(team2); + assertEquals(1, team2.getUsers().size()); + assertEquals(1, team2.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user1".equals(user.getIdentity())).count()); + } + + @Test + public void testSearchUsersAndGroupsMembershipThroughUsersAndGroups() throws Exception { + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, GROUP_SEARCH_BASE); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null)); + when(configurationContext.getProperty(PROP_USER_GROUP_ATTRIBUTE)).thenReturn(new StandardPropertyValue("description", null)); // using description in lieu of memberof + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("member", null)); + when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(8, ldapUserGroupProvider.getUsers().size()); + + final Set<Group> groups = ldapUserGroupProvider.getGroups(); + assertEquals(4, groups.size()); + + final Group admins = groups.stream().filter(group -> "admins".equals(group.getName())).findFirst().orElse(null); + assertNotNull(admins); + assertEquals(2, admins.getUsers().size()); + assertEquals(2, admins.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user1".equals(user.getIdentity()) || "user3".equals(user.getIdentity())).count()); + + final Group readOnly = groups.stream().filter(group -> "read-only".equals(group.getName())).findFirst().orElse(null); + assertNotNull(readOnly); + assertEquals(1, readOnly.getUsers().size()); + assertEquals(1, readOnly.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user2".equals(user.getIdentity())).count()); + + final Group team1 = groups.stream().filter(group -> "team1".equals(group.getName())).findFirst().orElse(null); + assertNotNull(team1); + assertEquals(3, team1.getUsers().size()); + assertEquals(3, team1.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user1".equals(user.getIdentity()) || "user4".equals(user.getIdentity()) || "user5".equals(user.getIdentity())).count()); + + final Group team2 = groups.stream().filter(group -> "team2".equals(group.getName())).findFirst().orElse(null); + assertNotNull(team2); + assertEquals(3, team2.getUsers().size()); + assertEquals(3, team2.getUsers().stream().map( + userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter( + user -> "user1".equals(user.getIdentity()) || "user6".equals(user.getIdentity()) || "user7".equals(user.getIdentity())).count()); + } + + @Test + public void testUserIdentityMapping() throws Exception { + final Properties props = new Properties(); + props.setProperty("nifi.security.identity.mapping.pattern.dn1", "^cn=(.*?),o=(.*?)$"); + props.setProperty("nifi.security.identity.mapping.value.dn1", "$1"); + + final NiFiProperties properties = getNiFiProperties(props); + ldapUserGroupProvider.setNiFiProperties(properties); + + final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(USER_SEARCH_BASE, null); + when(configurationContext.getProperty(PROP_USER_SEARCH_FILTER)).thenReturn(new StandardPropertyValue("(uid=user1)", null)); + ldapUserGroupProvider.onConfigured(configurationContext); + + assertEquals(1, ldapUserGroupProvider.getUsers().size()); + assertNotNull(ldapUserGroupProvider.getUserByIdentity("User 1,ou=users")); + } + + private AuthorizerConfigurationContext getBaseConfiguration(final String userSearchBase, final String groupSearchBase) { + final AuthorizerConfigurationContext configurationContext = mock(AuthorizerConfigurationContext.class); + when(configurationContext.getProperty(PROP_URL)).thenReturn(new StandardPropertyValue("ldap://127.0.0.1:" + getLdapServer().getPort(), null)); + when(configurationContext.getProperty(PROP_CONNECT_TIMEOUT)).thenReturn(new StandardPropertyValue("30 secs", null)); + when(configurationContext.getProperty(PROP_READ_TIMEOUT)).thenReturn(new StandardPropertyValue("30 secs", null)); + when(configurationContext.getProperty(PROP_REFERRAL_STRATEGY)).thenReturn(new StandardPropertyValue(ReferralStrategy.FOLLOW.name(), null)); + when(configurationContext.getProperty(PROP_PAGE_SIZE)).thenReturn(new StandardPropertyValue(null, null)); + when(configurationContext.getProperty(PROP_SYNC_INTERVAL)).thenReturn(new StandardPropertyValue("30 mins", null)); + + when(configurationContext.getProperty(PROP_AUTHENTICATION_STRATEGY)).thenReturn(new StandardPropertyValue(LdapAuthenticationStrategy.SIMPLE.name(), null)); + when(configurationContext.getProperty(PROP_MANAGER_DN)).thenReturn(new StandardPropertyValue("uid=admin,ou=system", null)); + when(configurationContext.getProperty(PROP_MANAGER_PASSWORD)).thenReturn(new StandardPropertyValue("secret", null)); + + when(configurationContext.getProperty(PROP_USER_SEARCH_BASE)).thenReturn(new StandardPropertyValue(userSearchBase, null)); + when(configurationContext.getProperty(PROP_USER_OBJECT_CLASS)).thenReturn(new StandardPropertyValue("person", null)); + when(configurationContext.getProperty(PROP_USER_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue(SearchScope.ONE_LEVEL.name(), null)); + when(configurationContext.getProperty(PROP_USER_SEARCH_FILTER)).thenReturn(new StandardPropertyValue(null, null)); + when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue(null, null)); + when(configurationContext.getProperty(PROP_USER_GROUP_ATTRIBUTE)).thenReturn(new StandardPropertyValue(null, null)); + + when(configurationContext.getProperty(PROP_GROUP_SEARCH_BASE)).thenReturn(new StandardPropertyValue(groupSearchBase, null)); + when(configurationContext.getProperty(PROP_GROUP_OBJECT_CLASS)).thenReturn(new StandardPropertyValue("groupOfNames", null)); + when(configurationContext.getProperty(PROP_GROUP_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue(SearchScope.ONE_LEVEL.name(), null)); + when(configurationContext.getProperty(PROP_GROUP_SEARCH_FILTER)).thenReturn(new StandardPropertyValue(null, null)); + when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue(null, null)); + when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue(null, null)); + + return configurationContext; + } + + private NiFiProperties getNiFiProperties(final Properties properties) { + final NiFiProperties nifiProperties = Mockito.mock(NiFiProperties.class); + when(nifiProperties.getPropertyKeys()).thenReturn(properties.stringPropertyNames()); + + when(nifiProperties.getProperty(anyString())).then(new Answer<String>() { + @Override + public String answer(InvocationOnMock invocationOnMock) throws Throwable { + return properties.getProperty((String)invocationOnMock.getArguments()[0]); + } + }); + return nifiProperties; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/6bc6f955/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/test/resources/nifi-example.ldif ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/test/resources/nifi-example.ldif b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/test/resources/nifi-example.ldif new file mode 100644 index 0000000..2b5bdac --- /dev/null +++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/src/test/resources/nifi-example.ldif @@ -0,0 +1,136 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +version: 1 + +dn: o=nifi +objectclass: extensibleObject +objectclass: top +objectclass: domain +dc: nifi +o: nifi + +dn: ou=users,o=nifi +objectClass: organizationalUnit +objectClass: top +ou: users + +dn: cn=User 1,ou=users,o=nifi +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +cn: User 1 +sn: User1 +uid: user1 + +dn: cn=User 2,ou=users,o=nifi +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +cn: User 2 +sn: User2 +uid: user2 + +dn: cn=User 3,ou=users,o=nifi +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +cn: User 3 +sn: User3 +uid: user3 + +dn: cn=User 4,ou=users,o=nifi +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +cn: User 4 +sn: User4 +description: cn=team1,ou=groups,o=nifi +uid: user4 + +dn: cn=User 5,ou=users,o=nifi +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +cn: User 5 +sn: User5 +description: cn=team1,ou=groups,o=nifi +uid: user5 + +dn: cn=User 6,ou=users,o=nifi +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +cn: User 6 +sn: User6 +description: cn=team2,ou=groups,o=nifi +uid: user6 + +dn: cn=User 7,ou=users,o=nifi +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +cn: User 7 +sn: User7 +description: cn=team2,ou=groups,o=nifi +uid: user7 + +dn: cn=User 8,ou=users,o=nifi +objectClass: organizationalPerson +objectClass: person +objectClass: inetOrgPerson +objectClass: top +cn: User 8 +sn: User8 +uid: user8 + +dn: ou=groups,o=nifi +objectClass: organizationalUnit +objectClass: top +ou: groups + +dn: cn=admins,ou=groups,o=nifi +objectClass: groupOfNames +objectClass: top +cn: admins +member: cn=User 1,ou=users,o=nifi +member: cn=User 3,ou=users,o=nifi + +dn: cn=read-only,ou=groups,o=nifi +objectClass: groupOfNames +objectClass: top +cn: read-only +member: cn=User 2,ou=users,o=nifi + +dn: cn=team1,ou=groups,o=nifi +objectClass: groupOfNames +objectClass: top +cn: team1 +member: cn=User 1,ou=users,o=nifi + +dn: cn=team2,ou=groups,o=nifi +objectClass: groupOfNames +objectClass: top +cn: team2 +member: cn=User 1,ou=users,o=nifi
