NIFI-4567:

- Adding new properties to allow the referenced attribute of a user/group to be configurable when detecting group membership.
- Expanding on documentation regarding the new properties.

This closes #2274.

Signed-off-by: Bryan Bende <bbende@apache.org>
This commit is contained in:
Matt Gilman 2017-11-16 09:21:02 -05:00 committed by Bryan Bende
parent 6b75921725
commit 439e13a8d5
No known key found for this signature in database
GPG Key ID: A0DDA9ED50711C39
5 changed files with 353 additions and 41 deletions

View File

@ -504,13 +504,15 @@ Another option for the UserGroupProvider is the LdapUserGroupProvider. By defaul
* User Search Scope - Search scope for searching users (ONE_LEVEL, OBJECT, or SUBTREE). Required if searching users.
* User Search Filter - Filter for searching for users against the 'User Search Base' (i.e. (memberof=cn=team1,ou=groups,o=nifi) ). Optional.
* User Identity Attribute - Attribute to use to extract user identity (i.e. cn). Optional. If not set, the entire DN is used.
* User Group Name Attribute - Attribute to use to define group membership (i.e. memberof). Optional. If not set group membership will not be calculated through the users. Will rely on group membership being defined through 'Group Member Attribute' if set.
* User Group Name Attribute - Attribute to use to define group membership (i.e. memberof). Optional. If not set group membership will not be calculated through the users. Will rely on group membership being defined through 'Group Member Attribute' if set. The value of this property is the name of the attribute in the user ldap entry that associates them with a group. The value of that user attribute could be a dn or group name for instance. What value is expected is configured in the 'User Group Name Attribute - Referenced Group Attribute'.
* User Group Name Attribute - Referenced Group Attribute - If blank, the value of the attribute defined in 'User Group Name Attribute' is expected to be the full dn of the group. If not blank, this property will define the attribute of the group ldap entry that the value of the attribute defined in 'User Group Name Attribute' is referencing (i.e. name). Use of this property requires that 'Group Search Base' is also configured.
* Group Search Base - Base DN for searching for groups (i.e. ou=groups,o=nifi). Required to search groups.
* Group Object Class - Object class for identifying groups (i.e. groupOfNames). Required if searching groups.
* Group Search Scope - Search scope for searching groups (ONE_LEVEL, OBJECT, or SUBTREE). Required if searching groups.
* Group Search Filter - Filter for searching for groups against the 'Group Search Base'. Optional.
* Group Name Attribute - Attribute to use to extract group name (i.e. cn). Optional. If not set, the entire DN is used.
* Group Member Attribute - Attribute to use to define group membership (i.e. member). Optional. If not set group membership will not be calculated through the groups. Will rely on group member being defined through 'User Group Name Attribute' if set.
* Group Member Attribute - Attribute to use to define group membership (i.e. member). Optional. If not set group membership will not be calculated through the groups. Will rely on group membership being defined through 'User Group Name Attribute' if set. The value of this property is the name of the attribute in the group ldap entry that associates them with a user. The value of that group attribute could be a dn or memberUid for instance. What value is expected is configured in the 'Group Member Attribute - Referenced User Attribute'. (i.e. member: cn=User 1,ou=users,o=nifi vs. memberUid: user1)
* Group Member Attribute - Referenced User Attribute - If blank, the value of the attribute defined in 'Group Member Attribute' is expected to be the full dn of the user. If not blank, this property will define the attribute of the user ldap entry that the value of the attribute defined in 'Group Member Attribute' is referencing (i.e. uid). Use of this property requires that 'User Search Base' is also configured. (i.e. member: cn=User 1,ou=users,o=nifi vs. memberUid: user1)
Another option for the UserGroupProvider are composite implementations. This means that multiple sources/implementations can be configured and composed. For instance, an admin can configure users/groups to be loaded from a file and a directory server. There are two composite implementations, one that supports multiple UserGroupProviders and one that supports multiple UserGroupProviders and a single configurable UserGroupProvider.
@ -616,9 +618,34 @@ After you have edited and saved the 'authorizers.xml' file, restart NiFi. The
NOTE: For a brand new secure flow, providing the "Initial Admin Identity" gives that user access to get into the UI and to manage users, groups and policies. But if that user wants to start modifying the flow, they need to grant themselves policies for the root process group. The system is unable to do this automatically because in a new flow the UUID of the root process group is not permanent until the flow.xml.gz is generated. If the NiFi instance is an upgrade from an existing flow.xml.gz or a 1.x instance going from unsecure to secure, then the "Initial Admin Identity" user is automatically given the privileges to modify the flow.
Here is an example loading users and groups from LDAP but still using file based access policies:
Here is an example loading users and groups from LDAP. Group membership will be driven through the member attribute of each group. Authorization will still use file based access policies:
----
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=admins,ou=groups,o=nifi
objectClass: groupOfNames
objectClass: top
cn: admins
member: cn=User 1,ou=users,o=nifi
member: cn=User 2,ou=users,o=nifi
<authorizers>
<userGroupProvider>
<identifier>ldap-user-group-provider</identifier>
@ -652,6 +679,7 @@ Here is an example loading users and groups from LDAP but still using file based
<property name="User Search Filter"></property>
<property name="User Identity Attribute">cn</property>
<property name="User Group Name Attribute"></property>
<property name="User Group Name Attribute - Referenced Group Attribute"></property>
<property name="Group Search Base">ou=groups,o=nifi</property>
<property name="Group Object Class">groupOfNames</property>
@ -659,6 +687,7 @@ Here is an example loading users and groups from LDAP but still using file based
<property name="Group Search Filter"></property>
<property name="Group Name Attribute">cn</property>
<property name="Group Member Attribute">member</property>
<property name="Group Member Attribute - Referenced User Attribute"></property>
</userGroupProvider>
<accessPolicyProvider>
<identifier>file-access-policy-provider</identifier>
@ -680,7 +709,91 @@ Here is an example loading users and groups from LDAP but still using file based
The 'Initial Admin Identity' value would have loaded from the cn from John Smith's entry based on the 'User Identity Attribute' value.
Here is an example composite implementation loading users from LDAP and a local file. The users from LDAP will be read only while the users loaded from the file will be configurable in UI.
Here is an example loading users and groups from LDAP. Group membership will be driven through the member attribute of each group. Authorization will still use file based access policies:
----
dn: uid=User 1,ou=Users,dc=local
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: user1
cn: User 1
dn: uid=User 2,ou=Users,dc=local
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: user2
cn: User 2
dn: cn=Managers,ou=Groups,dc=local
objectClass: posixGroup
cn: Managers
memberUid: user1
memberUid: user2
<authorizers>
<userGroupProvider>
<identifier>ldap-user-group-provider</identifier>
<class>org.apache.nifi.ldap.tenants.LdapUserGroupProvider</class>
<property name="Authentication Strategy">ANONYMOUS</property>
<property name="Manager DN"></property>
<property name="Manager Password"></property>
<property name="TLS - Keystore"></property>
<property name="TLS - Keystore Password"></property>
<property name="TLS - Keystore Type"></property>
<property name="TLS - Truststore"></property>
<property name="TLS - Truststore Password"></property>
<property name="TLS - Truststore Type"></property>
<property name="TLS - Client Auth"></property>
<property name="TLS - Protocol"></property>
<property name="TLS - Shutdown Gracefully"></property>
<property name="Referral Strategy">FOLLOW</property>
<property name="Connect Timeout">10 secs</property>
<property name="Read Timeout">10 secs</property>
<property name="Url">ldap://localhost:10389</property>
<property name="Page Size"></property>
<property name="Sync Interval">30 mins</property>
<property name="User Search Base">ou=Groups,dc=local</property>
<property name="User Object Class">posixAccount</property>
<property name="User Search Scope">ONE_LEVEL</property>
<property name="User Search Filter"></property>
<property name="User Identity Attribute">cn</property>
<property name="User Group Name Attribute"></property>
<property name="User Group Name Attribute - Referenced Group Attribute"></property>
<property name="Group Search Base">ou=Groups,dc=local</property>
<property name="Group Object Class">posixGroup</property>
<property name="Group Search Scope">ONE_LEVEL</property>
<property name="Group Search Filter"></property>
<property name="Group Name Attribute">cn</property>
<property name="Group Member Attribute">memberUid</property>
<property name="Group Member Attribute - Referenced User Attribute">uid</property>
</userGroupProvider>
<accessPolicyProvider>
<identifier>file-access-policy-provider</identifier>
<class>org.apache.nifi.authorization.FileAccessPolicyProvider</class>
<property name="User Group Provider">ldap-user-group-provider</property>
<property name="Authorizations File">./conf/authorizations.xml</property>
<property name="Initial Admin Identity">John Smith</property>
<property name="Legacy Authorized Users File"></property>
<property name="Node Identity 1"></property>
</accessPolicyProvider>
<authorizer>
<identifier>managed-authorizer</identifier>
<class>org.apache.nifi.authorization.StandardManagedAuthorizer</class>
<property name="Access Policy Provider">file-access-policy-provider</property>
</authorizer>
</authorizers>
----
Here is an example composite implementation loading users and groups from LDAP and a local file. Group membership will be driven through the member attribute of each group. The users from LDAP will be read only while the users loaded from the file will be configurable in UI.
----
<authorizers>
@ -725,6 +838,7 @@ Here is an example composite implementation loading users from LDAP and a local
<property name="User Search Filter"></property>
<property name="User Identity Attribute">cn</property>
<property name="User Group Name Attribute"></property>
<property name="User Group Name Attribute - Referenced Group Attribute"></property>
<property name="Group Search Base">ou=groups,o=nifi</property>
<property name="Group Object Class">groupOfNames</property>
@ -732,6 +846,7 @@ Here is an example composite implementation loading users from LDAP and a local
<property name="Group Search Filter"></property>
<property name="Group Name Attribute">cn</property>
<property name="Group Member Attribute">member</property>
<property name="Group Member Attribute - Referenced User Attribute"></property>
</userGroupProvider>
<userGroupProvider>
<identifier>composite-user-group-provider</identifier>

View File

@ -95,7 +95,13 @@
'User Identity Attribute' - Attribute to use to extract user identity (i.e. cn). Optional. If not set, the entire DN is used.
'User Group Name Attribute' - Attribute to use to define group membership (i.e. memberof). Optional. If not set
group membership will not be calculated through the users. Will rely on group membership being defined
through 'Group Member Attribute' if set.
through 'Group Member Attribute' if set. The value of this property is the name of the attribute in the user ldap entry that
associates them with a group. The value of that user attribute could be a dn or group name for instance. What value is expected
is configured in the 'User Group Name Attribute - Referenced Group Attribute'.
'User Group Name Attribute - Referenced Group Attribute' - If blank, the value of the attribute defined in 'User Group Name Attribute'
is expected to be the full dn of the group. If not blank, this property will define the attribute of the group ldap entry that
the value of the attribute defined in 'User Group Name Attribute' is referencing (i.e. name). Use of this property requires that
'Group Search Base' is also configured.
'Group Search Base' - Base DN for searching for groups (i.e. ou=groups,o=nifi). Required to search groups.
'Group Object Class' - Object class for identifying groups (i.e. groupOfNames). Required if searching groups.
@ -103,8 +109,14 @@
'Group Search Filter' - Filter for searching for groups against the 'Group Search Base'. Optional.
'Group Name Attribute' - Attribute to use to extract group name (i.e. cn). Optional. If not set, the entire DN is used.
'Group Member Attribute' - Attribute to use to define group membership (i.e. member). Optional. If not set
group membership will not be calculated through the groups. Will rely on group member being defined
through 'User Group Name Attribute' if set.
group membership will not be calculated through the groups. Will rely on group membership being defined
through 'User Group Name Attribute' if set. The value of this property is the name of the attribute in the group ldap entry that
associates them with a user. The value of that group attribute could be a dn or memberUid for instance. What value is expected
is configured in the 'Group Member Attribute - Referenced User Attribute'. (i.e. member: cn=User 1,ou=users,o=nifi vs. memberUid: user1)
'Group Member Attribute - Referenced User Attribute' - If blank, the value of the attribute defined in 'Group Member Attribute'
is expected to be the full dn of the user. If not blank, this property will define the attribute of the user ldap entry that
the value of the attribute defined in 'Group Member Attribute' is referencing (i.e. uid). Use of this property requires that
'User Search Base' is also configured. (i.e. member: cn=User 1,ou=users,o=nifi vs. memberUid: user1)
NOTE: Any identity mapping rules specified in nifi.properties will also be applied to the user identities.
Group names are not mapped.
@ -142,6 +154,7 @@
<property name="User Search Filter"></property>
<property name="User Identity Attribute"></property>
<property name="User Group Name Attribute"></property>
<property name="User Group Name Attribute - Referenced Group Attribute"></property>
<property name="Group Search Base"></property>
<property name="Group Object Class">group</property>
@ -149,6 +162,7 @@
<property name="Group Search Filter"></property>
<property name="Group Name Attribute"></property>
<property name="Group Member Attribute"></property>
<property name="Group Member Attribute - Referenced User Attribute"></property>
</userGroupProvider>
To enable the ldap-user-group-provider remove 2 lines. This is 2 of 2. -->

View File

@ -103,6 +103,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
public static final String PROP_USER_SEARCH_FILTER = "User Search Filter";
public static final String PROP_USER_IDENTITY_ATTRIBUTE = "User Identity Attribute";
public static final String PROP_USER_GROUP_ATTRIBUTE = "User Group Name Attribute";
public static final String PROP_USER_GROUP_REFERENCED_GROUP_ATTRIBUTE = "User Group Name Attribute - Referenced Group Attribute";
public static final String PROP_GROUP_SEARCH_BASE = "Group Search Base";
public static final String PROP_GROUP_OBJECT_CLASS = "Group Object Class";
@ -110,6 +111,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
public static final String PROP_GROUP_SEARCH_FILTER = "Group Search Filter";
public static final String PROP_GROUP_NAME_ATTRIBUTE = "Group Name Attribute";
public static final String PROP_GROUP_MEMBER_ATTRIBUTE = "Group Member Attribute";
public static final String PROP_GROUP_MEMBER_REFERENCED_USER_ATTRIBUTE = "Group Member Attribute - Referenced User Attribute";
public static final String PROP_SYNC_INTERVAL = "Sync Interval";
@ -125,6 +127,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
private String userIdentityAttribute;
private String userObjectClass;
private String userGroupNameAttribute;
private String userGroupReferencedGroupAttribute;
private boolean useDnForUserIdentity;
private boolean performUserSearch;
@ -132,6 +135,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
private SearchScope groupSearchScope;
private String groupSearchFilter;
private String groupMemberAttribute;
private String groupMemberReferencedUserAttribute;
private String groupNameAttribute;
private String groupObjectClass;
private boolean useDnForGroupName;
@ -270,6 +274,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
userSearchFilter = configurationContext.getProperty(PROP_USER_SEARCH_FILTER).getValue();
userIdentityAttribute = configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE).getValue();
userGroupNameAttribute = configurationContext.getProperty(PROP_USER_GROUP_ATTRIBUTE).getValue();
userGroupReferencedGroupAttribute = configurationContext.getProperty(PROP_USER_GROUP_REFERENCED_GROUP_ATTRIBUTE).getValue();
try {
userSearchScope = SearchScope.valueOf(rawUserSearchScope.getValue());
@ -303,6 +308,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
groupSearchFilter = configurationContext.getProperty(PROP_GROUP_SEARCH_FILTER).getValue();
groupNameAttribute = configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE).getValue();
groupMemberAttribute = configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE).getValue();
groupMemberReferencedUserAttribute = configurationContext.getProperty(PROP_GROUP_MEMBER_REFERENCED_USER_ATTRIBUTE).getValue();
try {
groupSearchScope = SearchScope.valueOf(rawGroupSearchScope.getValue());
@ -325,6 +331,16 @@ public class LdapUserGroupProvider implements UserGroupProvider {
throw new AuthorizerCreationException("'Group Member Attribute' is required when searching groups but not users.");
}
// ensure that performUserSearch is set when groupMemberReferencedUserAttribute is specified
if (StringUtils.isNotBlank(groupMemberReferencedUserAttribute) && !performUserSearch) {
throw new AuthorizerCreationException("''User Search Base' must be set when specifying 'Group Member Attribute - Referenced User Attribute'.");
}
// ensure that performGroupSearch is set when userGroupReferencedGroupAttribute is specified
if (StringUtils.isNotBlank(userGroupReferencedGroupAttribute) && !performGroupSearch) {
throw new AuthorizerCreationException("'Group Search Base' must be set when specifying 'User Group Name Attribute - Referenced Group Attribute'.");
}
// get the page size if configured
final PropertyValue rawPageSize = configurationContext.getProperty(PROP_PAGE_SIZE);
if (rawPageSize.isSet() && StringUtils.isNotBlank(rawPageSize.getValue())) {
@ -430,10 +446,10 @@ public class LdapUserGroupProvider implements UserGroupProvider {
final List<Group> groupList = new ArrayList<>();
// group dn -> user identifiers lookup
final Map<String, Set<String>> groupDnToUserIdentifierMappings = new HashMap<>();
final Map<String, Set<String>> groupToUserIdentifierMappings = new HashMap<>();
// user dn -> user lookup
final Map<String, User> userDnLookup = new HashMap<>();
final Map<String, User> userLookup = new HashMap<>();
if (performUserSearch) {
// search controls
@ -461,14 +477,14 @@ public class LdapUserGroupProvider implements UserGroupProvider {
userList.addAll(ldapTemplate.search(userSearchBase, userFilter.encode(), userControls, new AbstractContextMapper<User>() {
@Override
protected User doMapFromContext(DirContextOperations ctx) {
final String dn = ctx.getDn().toString();
// get the user identity
final String identity = getUserIdentity(ctx);
// build the user
final User user = new User.Builder().identifierGenerateFromSeed(identity).identity(identity).build();
userDnLookup.put(dn, user);
// store the user for group member later
userLookup.put(getReferencedUserValue(ctx), user);
if (StringUtils.isNotBlank(userGroupNameAttribute)) {
final Attribute attributeGroups = ctx.getAttributes().get(userGroupNameAttribute);
@ -477,10 +493,10 @@ public class LdapUserGroupProvider implements UserGroupProvider {
logger.warn("User group name attribute [" + userGroupNameAttribute + "] does not exist. Ignoring group membership.");
} else {
try {
final NamingEnumeration<String> groupDns = (NamingEnumeration<String>) attributeGroups.getAll();
while (groupDns.hasMoreElements()) {
// store the group dn -> user identifier mapping
groupDnToUserIdentifierMappings.computeIfAbsent(groupDns.next(), g -> new HashSet<>()).add(user.getIdentifier());
final NamingEnumeration<String> groupValues = (NamingEnumeration<String>) attributeGroups.getAll();
while (groupValues.hasMoreElements()) {
// store the group -> user identifier mapping
groupToUserIdentifierMappings.computeIfAbsent(groupValues.next(), g -> new HashSet<>()).add(user.getIdentifier());
}
} catch (NamingException e) {
throw new AuthorizationAccessException("Error while retrieving user group name attribute [" + userIdentityAttribute + "].");
@ -524,30 +540,36 @@ public class LdapUserGroupProvider implements UserGroupProvider {
// get the group identity
final String name = getGroupName(ctx);
// get the value of this group that may associate it to users
final String referencedGroupValue = getReferencedGroupValue(ctx);
if (!StringUtils.isBlank(groupMemberAttribute)) {
Attribute attributeUsers = ctx.getAttributes().get(groupMemberAttribute);
if (attributeUsers == null) {
logger.warn("Group member attribute [" + groupMemberAttribute + "] does not exist. Ignoring group membership.");
} else {
try {
final NamingEnumeration<String> userDns = (NamingEnumeration<String>) attributeUsers.getAll();
while (userDns.hasMoreElements()) {
final String userDn = userDns.next();
final NamingEnumeration<String> userValues = (NamingEnumeration<String>) attributeUsers.getAll();
while (userValues.hasMoreElements()) {
final String userValue = userValues.next();
if (performUserSearch) {
// find the user by dn add the identifier to this group
final User user = userDnLookup.get(userDn);
// find the user by it's referenced attribute and add the identifier to this group
final User user = userLookup.get(userValue);
// ensure the user is known
if (user != null) {
groupDnToUserIdentifierMappings.computeIfAbsent(dn, g -> new HashSet<>()).add(user.getIdentifier());
groupToUserIdentifierMappings.computeIfAbsent(referencedGroupValue, g -> new HashSet<>()).add(user.getIdentifier());
} else {
logger.warn(String.format("%s contains member %s but that user was not found while searching users. Ignoring group membership.", name, userDn));
logger.warn(String.format("%s contains member %s but that user was not found while searching users. Ignoring group membership.", name, userValue));
}
} else {
// since performUserSearch is false, then the referenced group attribute must be blank... the user value must be the dn
final String userDn = userValue;
final String userIdentity;
if (useDnForUserIdentity) {
// use the dn to avoid the unnecessary look up
// use the user value to avoid the unnecessary look up
userIdentity = userDn;
} else {
// lookup the user to extract the user identity
@ -559,7 +581,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
// add this user
userList.add(user);
groupDnToUserIdentifierMappings.computeIfAbsent(dn, g -> new HashSet<>()).add(user.getIdentifier());
groupToUserIdentifierMappings.computeIfAbsent(referencedGroupValue, g -> new HashSet<>()).add(user.getIdentifier());
}
}
} catch (NamingException e) {
@ -571,9 +593,9 @@ public class LdapUserGroupProvider implements UserGroupProvider {
// build this group
final Group.Builder groupBuilder = new Group.Builder().identifierGenerateFromSeed(name).name(name);
// add all users that were associated with this group dn
if (groupDnToUserIdentifierMappings.containsKey(dn)) {
groupDnToUserIdentifierMappings.remove(dn).forEach(userIdentifier -> groupBuilder.addUser(userIdentifier));
// add all users that were associated with this referenced group attribute
if (groupToUserIdentifierMappings.containsKey(referencedGroupValue)) {
groupToUserIdentifierMappings.remove(referencedGroupValue).forEach(userIdentifier -> groupBuilder.addUser(userIdentifier));
}
return groupBuilder.build();
@ -582,13 +604,15 @@ public class LdapUserGroupProvider implements UserGroupProvider {
} while (hasMorePages(groupProcessor));
// any remaining groupDn's were referenced by a user but not found while searching groups
groupDnToUserIdentifierMappings.forEach((groupDn, userIdentifiers) -> {
groupToUserIdentifierMappings.forEach((referencedGroupValue, userIdentifiers) -> {
logger.warn(String.format("[%s] are members of %s but that group was not found while searching users. Ignoring group membership.",
StringUtils.join(userIdentifiers, ", "), groupDn));
StringUtils.join(userIdentifiers, ", "), referencedGroupValue));
});
} else {
// since performGroupSearch is false, then the referenced user attribute must be blank... the group value must be the dn
// groups are not being searched so lookup any groups identified while searching users
groupDnToUserIdentifierMappings.forEach((groupDn, userIdentifiers) -> {
groupToUserIdentifierMappings.forEach((groupDn, userIdentifiers) -> {
final String groupName;
if (useDnForGroupName) {
// use the dn to avoid the unnecessary look up
@ -640,6 +664,27 @@ public class LdapUserGroupProvider implements UserGroupProvider {
return IdentityMappingUtil.mapIdentity(identity, identityMappings);
}
private String getReferencedUserValue(final DirContextOperations ctx) {
final String referencedUserValue;
if (StringUtils.isBlank(groupMemberReferencedUserAttribute)) {
referencedUserValue = ctx.getDn().toString();
} else {
final Attribute attributeName = ctx.getAttributes().get(groupMemberReferencedUserAttribute);
if (attributeName == null) {
throw new AuthorizationAccessException("Referenced user value attribute [" + groupMemberReferencedUserAttribute + "] does not exist.");
}
try {
referencedUserValue = (String) attributeName.get();
} catch (NamingException e) {
throw new AuthorizationAccessException("Error while retrieving reference user value attribute [" + groupMemberReferencedUserAttribute + "].");
}
}
return referencedUserValue;
}
private String getGroupName(final DirContextOperations ctx) {
final String name;
@ -661,6 +706,27 @@ public class LdapUserGroupProvider implements UserGroupProvider {
return name;
}
private String getReferencedGroupValue(final DirContextOperations ctx) {
final String referencedGroupValue;
if (StringUtils.isBlank(userGroupReferencedGroupAttribute)) {
referencedGroupValue = ctx.getDn().toString();
} else {
final Attribute attributeName = ctx.getAttributes().get(userGroupReferencedGroupAttribute);
if (attributeName == null) {
throw new AuthorizationAccessException("Referenced group value attribute [" + userGroupReferencedGroupAttribute + "] does not exist.");
}
try {
referencedGroupValue = (String) attributeName.get();
} catch (NamingException e) {
throw new AuthorizationAccessException("Error while retrieving referenced group value attribute [" + userGroupReferencedGroupAttribute + "].");
}
}
return referencedGroupValue;
}
@AuthorizerContext
public void setNiFiProperties(NiFiProperties properties) {
this.properties = properties;

View File

@ -36,14 +36,13 @@ 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_REFERENCED_USER_ATTRIBUTE;
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;
@ -57,6 +56,7 @@ import static org.apache.nifi.ldap.tenants.LdapUserGroupProvider.PROP_READ_TIMEO
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_REFERENCED_GROUP_ATTRIBUTE;
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;
@ -167,7 +167,7 @@ public class LdapUserGroupProviderTest extends AbstractLdapTestUnit {
when(configurationContext.getProperty(PROP_USER_SEARCH_SCOPE)).thenReturn(new StandardPropertyValue(SearchScope.SUBTREE.name(), null));
ldapUserGroupProvider.onConfigured(configurationContext);
assertEquals(8, ldapUserGroupProvider.getUsers().size());
assertEquals(9, ldapUserGroupProvider.getUsers().size());
assertTrue(ldapUserGroupProvider.getGroups().isEmpty());
}
@ -507,6 +507,97 @@ public class LdapUserGroupProviderTest extends AbstractLdapTestUnit {
assertNotNull(ldapUserGroupProvider.getUserByIdentity("User 1,ou=users"));
}
@Test(expected = AuthorizerCreationException.class)
public void testReferencedGroupAttributeWithoutGroupSearchBase() throws Exception {
final AuthorizerConfigurationContext configurationContext = getBaseConfiguration("ou=users-2,o=nifi", null);
when(configurationContext.getProperty(PROP_USER_GROUP_REFERENCED_GROUP_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null));
ldapUserGroupProvider.onConfigured(configurationContext);
}
@Test
public void testReferencedGroupWithoutDefiningReferencedAttribute() throws Exception {
final AuthorizerConfigurationContext configurationContext = getBaseConfiguration("ou=users-2,o=nifi", "ou=groups-2,o=nifi");
when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null));
when(configurationContext.getProperty(PROP_USER_OBJECT_CLASS)).thenReturn(new StandardPropertyValue("room", null)); // using room due to reqs of groupOfNames
when(configurationContext.getProperty(PROP_USER_GROUP_ATTRIBUTE)).thenReturn(new StandardPropertyValue("description", null)); // using description in lieu of member
when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null));
when(configurationContext.getProperty(PROP_GROUP_OBJECT_CLASS)).thenReturn(new StandardPropertyValue("room", null)); // using room due to reqs of groupOfNames
ldapUserGroupProvider.onConfigured(configurationContext);
final Set<Group> groups = ldapUserGroupProvider.getGroups();
assertEquals(1, groups.size());
final Group team3 = groups.stream().filter(group -> "team3".equals(group.getName())).findFirst().orElse(null);
assertNotNull(team3);
assertTrue(team3.getUsers().isEmpty());
}
@Test
public void testReferencedGroupUsingReferencedAttribute() throws Exception {
final AuthorizerConfigurationContext configurationContext = getBaseConfiguration("ou=users-2,o=nifi", "ou=groups-2,o=nifi");
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 member
when(configurationContext.getProperty(PROP_USER_GROUP_REFERENCED_GROUP_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null));
when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null));
when(configurationContext.getProperty(PROP_GROUP_OBJECT_CLASS)).thenReturn(new StandardPropertyValue("room", null)); // using room because groupOfNames requires a member
ldapUserGroupProvider.onConfigured(configurationContext);
final Set<Group> groups = ldapUserGroupProvider.getGroups();
assertEquals(1, groups.size());
final Group team3 = groups.stream().filter(group -> "team3".equals(group.getName())).findFirst().orElse(null);
assertNotNull(team3);
assertEquals(1, team3.getUsers().size());
assertEquals(1, team3.getUsers().stream().map(
userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter(
user -> "user9".equals(user.getIdentity())).count());
}
@Test(expected = AuthorizerCreationException.class)
public void testReferencedUserWithoutUserSearchBase() throws Exception {
final AuthorizerConfigurationContext configurationContext = getBaseConfiguration(null, "ou=groups-2,o=nifi");
when(configurationContext.getProperty(PROP_GROUP_MEMBER_REFERENCED_USER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null));
ldapUserGroupProvider.onConfigured(configurationContext);
}
@Test
public void testReferencedUserWithoutDefiningReferencedAttribute() throws Exception {
final AuthorizerConfigurationContext configurationContext = getBaseConfiguration("ou=users-2,o=nifi", "ou=groups-2,o=nifi");
when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null));
when(configurationContext.getProperty(PROP_GROUP_OBJECT_CLASS)).thenReturn(new StandardPropertyValue("room", null)); // using room due to reqs of groupOfNames
when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("description", null)); // using description in lieu of member
when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null));
ldapUserGroupProvider.onConfigured(configurationContext);
final Set<Group> groups = ldapUserGroupProvider.getGroups();
assertEquals(1, groups.size());
final Group team3 = groups.stream().filter(group -> "team3".equals(group.getName())).findFirst().orElse(null);
assertNotNull(team3);
assertTrue(team3.getUsers().isEmpty());
}
@Test
public void testReferencedUserUsingReferencedAttribute() throws Exception {
final AuthorizerConfigurationContext configurationContext = getBaseConfiguration("ou=users-2,o=nifi", "ou=groups-2,o=nifi");
when(configurationContext.getProperty(PROP_USER_IDENTITY_ATTRIBUTE)).thenReturn(new StandardPropertyValue("sn", null));
when(configurationContext.getProperty(PROP_GROUP_OBJECT_CLASS)).thenReturn(new StandardPropertyValue("room", null)); // using room due to reqs of groupOfNames
when(configurationContext.getProperty(PROP_GROUP_MEMBER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("description", null)); // using description in lieu of member
when(configurationContext.getProperty(PROP_GROUP_NAME_ATTRIBUTE)).thenReturn(new StandardPropertyValue("cn", null));
when(configurationContext.getProperty(PROP_GROUP_MEMBER_REFERENCED_USER_ATTRIBUTE)).thenReturn(new StandardPropertyValue("uid", null)); // does not need to be the same as user id attr
ldapUserGroupProvider.onConfigured(configurationContext);
final Set<Group> groups = ldapUserGroupProvider.getGroups();
assertEquals(1, groups.size());
final Group team3 = groups.stream().filter(group -> "team3".equals(group.getName())).findFirst().orElse(null);
assertNotNull(team3);
assertEquals(1, team3.getUsers().size());
assertEquals(1, team3.getUsers().stream().map(
userIdentifier -> ldapUserGroupProvider.getUser(userIdentifier)).filter(
user -> "User9".equals(user.getIdentity())).count());
}
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));
@ -526,6 +617,7 @@ public class LdapUserGroupProviderTest extends AbstractLdapTestUnit {
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_USER_GROUP_REFERENCED_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));
@ -533,6 +625,7 @@ public class LdapUserGroupProviderTest extends AbstractLdapTestUnit {
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));
when(configurationContext.getProperty(PROP_GROUP_MEMBER_REFERENCED_USER_ATTRIBUTE)).thenReturn(new StandardPropertyValue(null, null));
return configurationContext;
}
@ -540,13 +633,7 @@ public class LdapUserGroupProviderTest extends AbstractLdapTestUnit {
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]);
}
});
when(nifiProperties.getProperty(anyString())).then(invocationOnMock -> properties.getProperty((String) invocationOnMock.getArguments()[0]));
return nifiProperties;
}
}

View File

@ -29,6 +29,11 @@ objectClass: organizationalUnit
objectClass: top
ou: users
dn: ou=users-2,o=nifi
objectClass: organizationalUnit
objectClass: top
ou: users-2
dn: cn=User 1,ou=users,o=nifi
objectClass: organizationalPerson
objectClass: person
@ -56,6 +61,8 @@ cn: User 3
sn: User3
uid: user3
## since the embedded ldap does not support memberof, we are using description to simulate
dn: cn=User 4,ou=users,o=nifi
objectClass: organizationalPerson
objectClass: person
@ -105,11 +112,26 @@ cn: User 8
sn: User8
uid: user8
dn: cn=User 9,ou=users-2,o=nifi
objectClass: organizationalPerson
objectClass: person
objectClass: inetOrgPerson
objectClass: top
cn: User 9
sn: User9
description: team3
uid: user9
dn: ou=groups,o=nifi
objectClass: organizationalUnit
objectClass: top
ou: groups
dn: ou=groups-2,o=nifi
objectClass: organizationalUnit
objectClass: top
ou: groups
dn: cn=admins,ou=groups,o=nifi
objectClass: groupOfNames
objectClass: top
@ -134,3 +156,11 @@ objectClass: groupOfNames
objectClass: top
cn: team2
member: cn=User 1,ou=users,o=nifi
## since the embedded ldap requires member to be fqdn, we are simulating using room and description
dn: cn=team3,ou=groups-2,o=nifi
objectClass: room
objectClass: top
cn: team3
description: user9