Skip to content

Commit 917da17

Browse files
add memberOf
1 parent d95f43a commit 917da17

File tree

1 file changed

+147
-61
lines changed

1 file changed

+147
-61
lines changed

zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java

Lines changed: 147 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import javax.naming.PartialResultException;
3939
import javax.naming.SizeLimitExceededException;
4040
import javax.naming.directory.Attribute;
41+
import javax.naming.directory.Attributes;
4142
import javax.naming.directory.SearchControls;
4243
import javax.naming.directory.SearchResult;
4344
import javax.naming.ldap.Control;
@@ -165,6 +166,9 @@ public class LdapRealm extends DefaultLdapRealm {
165166
private String userSearchScope = "subtree";
166167
private String groupSearchScope = "subtree";
167168
private boolean groupSearchEnableMatchingRuleInChain;
169+
// Support for FreeIPA nested groups using memberOf attribute
170+
private boolean useMemberOfForNestedGroups;
171+
private String memberOfAttribute = "memberOf";
168172

169173
private String groupSearchBase;
170174

@@ -343,80 +347,146 @@ protected Set<String> rolesFor(PrincipalCollection principals, String userNameIn
343347

344348
String userDn = getUserDnForSearch(userName);
345349

346-
// Activate paged results
347-
int pageSize = getPagingSize();
348-
LOGGER.debug("Ldap PagingSize: {}", pageSize);
349-
int numResults = 0;
350-
try {
351-
ldapCtx.addToEnvironment(Context.REFERRAL, "ignore");
352-
353-
ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize,
354-
Control.NONCRITICAL)});
355-
356-
// ldapsearch -h localhost -p 33389 -D
357-
// uid=guest,ou=people,dc=hadoop,dc=apache,dc=org -w guest-password
358-
// -b dc=hadoop,dc=apache,dc=org -s sub '(objectclass=*)'
350+
// Check if we should use memberOf attribute for nested groups (FreeIPA support)
351+
if (useMemberOfForNestedGroups) {
352+
// Search for the user to get memberOf attribute values
353+
SearchControls searchControls = getUserSearchControls();
354+
searchControls.setReturningAttributes(new String[]{memberOfAttribute});
355+
359356
NamingEnumeration<SearchResult> searchResultEnum = null;
360-
SearchControls searchControls = getGroupSearchControls();
361357
try {
362-
if (groupSearchEnableMatchingRuleInChain) {
363-
searchResultEnum = ldapCtx.search(
364-
getGroupSearchBase(),
365-
String.format(
366-
MATCHING_RULE_IN_CHAIN_FORMAT, groupObjectClass, memberAttribute, userDn),
367-
searchControls);
368-
while (searchResultEnum != null && searchResultEnum.hasMore()) {
369-
// searchResults contains all the groups in search scope
370-
numResults++;
371-
final SearchResult group = searchResultEnum.next();
372-
373-
Attribute attribute = group.getAttributes().get(getGroupIdAttribute());
374-
String groupName = attribute.get().toString();
375-
376-
String roleName = roleNameFor(groupName);
377-
if (roleName != null) {
378-
roleNames.add(roleName);
379-
} else {
380-
roleNames.add(groupName);
381-
}
358+
// Search for the user
359+
String searchFilter;
360+
if (userSearchFilter == null) {
361+
if (userSearchAttributeName == null) {
362+
searchFilter = String.format("(objectclass=%1$s)", getUserObjectClass());
363+
} else {
364+
searchFilter = String.format("(&(objectclass=%1$s)(%2$s=%3$s))", getUserObjectClass(),
365+
userSearchAttributeName, expandTemplate(getUserSearchAttributeTemplate(), userName));
382366
}
383367
} else {
384-
// Default group search filter
385-
String searchFilter = String.format("(objectclass=%1$s)", groupObjectClass);
386-
387-
// If group search filter is defined in Shiro config, then use it
388-
if (groupSearchFilter != null) {
389-
searchFilter = expandTemplate(groupSearchFilter, userName);
390-
//searchFilter = String.format("%1$s", groupSearchFilter);
391-
}
392-
LOGGER.debug("Group SearchBase|SearchFilter|GroupSearchScope: " + "{}|{}|{}",
393-
getGroupSearchBase(), searchFilter, groupSearchScope);
394-
searchResultEnum = ldapCtx.search(
395-
getGroupSearchBase(),
396-
searchFilter,
397-
searchControls);
398-
while (searchResultEnum != null && searchResultEnum.hasMore()) {
399-
// searchResults contains all the groups in search scope
400-
numResults++;
401-
final SearchResult group = searchResultEnum.next();
402-
addRoleIfMember(userDn, group, roleNames, groupNames, ldapContextFactory);
368+
searchFilter = expandTemplate(userSearchFilter, userName);
369+
}
370+
371+
LOGGER.debug("MemberOf Attribute Search - SearchBase|SearchFilter: {}|{}",
372+
getUserSearchBase(), searchFilter);
373+
searchResultEnum = ldapCtx.search(getUserSearchBase(), searchFilter, searchControls);
374+
375+
if (searchResultEnum.hasMore()) {
376+
SearchResult searchResult = searchResultEnum.next();
377+
Attributes attrs = searchResult.getAttributes();
378+
379+
if (attrs != null && attrs.get(memberOfAttribute) != null) {
380+
Attribute memberOfAttr = attrs.get(memberOfAttribute);
381+
NamingEnumeration<?> values = memberOfAttr.getAll();
382+
383+
while (values.hasMore()) {
384+
String groupDn = (String) values.next();
385+
// Extract the group name (cn) from the group DN
386+
LdapName ldapName = new LdapName(groupDn);
387+
for (int i = 0; i < ldapName.size(); i++) {
388+
String rdn = ldapName.get(i);
389+
if (rdn.startsWith(getGroupIdAttribute() + "=")) {
390+
String groupName = rdn.substring(getGroupIdAttribute().length() + 1);
391+
LOGGER.debug("Found group via memberOf: {}", groupName);
392+
groupNames.add(groupName);
393+
394+
String roleName = roleNameFor(groupName);
395+
if (roleName != null) {
396+
roleNames.add(roleName);
397+
} else {
398+
roleNames.add(groupName);
399+
}
400+
break;
401+
}
402+
}
403+
}
403404
}
404405
}
405406
} catch (PartialResultException e) {
406-
LOGGER.debug("Ignoring PartitalResultException");
407+
LOGGER.debug("Ignoring PartialResultException for memberOf search");
407408
} finally {
408409
if (searchResultEnum != null) {
409410
searchResultEnum.close();
410411
}
411412
}
412-
// Re-activate paged results
413-
ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize,
414-
null, Control.CRITICAL)});
415-
} catch (SizeLimitExceededException e) {
416-
LOGGER.info("Only retrieved first {} groups due to SizeLimitExceededException.", numResults);
417-
} catch (IOException e) {
418-
LOGGER.error("Unabled to setup paged results");
413+
} else {
414+
// Activate paged results
415+
int pageSize = getPagingSize();
416+
LOGGER.debug("Ldap PagingSize: {}", pageSize);
417+
int numResults = 0;
418+
try {
419+
ldapCtx.addToEnvironment(Context.REFERRAL, "ignore");
420+
421+
ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize,
422+
Control.NONCRITICAL)});
423+
424+
// ldapsearch -h localhost -p 33389 -D
425+
// uid=guest,ou=people,dc=hadoop,dc=apache,dc=org -w guest-password
426+
// -b dc=hadoop,dc=apache,dc=org -s sub '(objectclass=*)'
427+
NamingEnumeration<SearchResult> searchResultEnum = null;
428+
SearchControls searchControls = getGroupSearchControls();
429+
try {
430+
if (groupSearchEnableMatchingRuleInChain) {
431+
searchResultEnum = ldapCtx.search(
432+
getGroupSearchBase(),
433+
String.format(
434+
MATCHING_RULE_IN_CHAIN_FORMAT, groupObjectClass, memberAttribute, userDn),
435+
searchControls);
436+
while (searchResultEnum != null && searchResultEnum.hasMore()) {
437+
// searchResults contains all the groups in search scope
438+
numResults++;
439+
final SearchResult group = searchResultEnum.next();
440+
441+
Attribute attribute = group.getAttributes().get(getGroupIdAttribute());
442+
String groupName = attribute.get().toString();
443+
444+
String roleName = roleNameFor(groupName);
445+
if (roleName != null) {
446+
roleNames.add(roleName);
447+
} else {
448+
roleNames.add(groupName);
449+
}
450+
}
451+
} else {
452+
// Default group search filter
453+
String searchFilter = String.format("(objectclass=%1$s)", groupObjectClass);
454+
455+
// If group search filter is defined in Shiro config, then use it
456+
if (groupSearchFilter != null) {
457+
searchFilter = expandTemplate(groupSearchFilter, userName);
458+
//searchFilter = String.format("%1$s", groupSearchFilter);
459+
}
460+
LOGGER.debug("Group SearchBase|SearchFilter|GroupSearchScope: " + "{}|{}|{}",
461+
getGroupSearchBase(), searchFilter, groupSearchScope);
462+
searchResultEnum = ldapCtx.search(
463+
getGroupSearchBase(),
464+
searchFilter,
465+
searchControls);
466+
while (searchResultEnum != null && searchResultEnum.hasMore()) {
467+
// searchResults contains all the groups in search scope
468+
numResults++;
469+
final SearchResult group = searchResultEnum.next();
470+
addRoleIfMember(userDn, group, roleNames, groupNames, ldapContextFactory);
471+
}
472+
}
473+
} catch (PartialResultException e) {
474+
LOGGER.debug("Ignoring PartitalResultException");
475+
} finally {
476+
if (searchResultEnum != null) {
477+
searchResultEnum.close();
478+
}
479+
}
480+
// Re-activate paged results
481+
ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize,
482+
null, Control.CRITICAL)});
483+
} catch (SizeLimitExceededException e) {
484+
LOGGER.info("Only retrieved first {} groups due to SizeLimitExceededException.", numResults);
485+
} catch (IOException e) {
486+
LOGGER.error("Unabled to setup paged results");
487+
}
419488
}
489+
420490
// save role names and group names in session so that they can be
421491
// easily looked up outside of this object
422492
session.setAttribute(SUBJECT_USER_ROLES, roleNames);
@@ -817,6 +887,22 @@ public void setGroupSearchEnableMatchingRuleInChain(
817887
this.groupSearchEnableMatchingRuleInChain = groupSearchEnableMatchingRuleInChain;
818888
}
819889

890+
public boolean isUseMemberOfForNestedGroups() {
891+
return useMemberOfForNestedGroups;
892+
}
893+
894+
public void setUseMemberOfForNestedGroups(boolean useMemberOfForNestedGroups) {
895+
this.useMemberOfForNestedGroups = useMemberOfForNestedGroups;
896+
}
897+
898+
public String getMemberOfAttribute() {
899+
return memberOfAttribute;
900+
}
901+
902+
public void setMemberOfAttribute(String memberOfAttribute) {
903+
this.memberOfAttribute = memberOfAttribute;
904+
}
905+
820906
private SearchControls getUserSearchControls() {
821907
SearchControls searchControls = SUBTREE_SCOPE;
822908
if ("onelevel".equalsIgnoreCase(userSearchScope)) {

0 commit comments

Comments
 (0)