All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.zeppelin.realm.LdapRealm Maven / Gradle / Ivy

There is a newer version: 0.11.2
Show newest version
/*
 *  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.zeppelin.realm;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.PartialResultException;
import javax.naming.SizeLimitExceededException;
import javax.naming.directory.Attribute;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.PagedResultsControl;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.alias.CredentialProvider;
import org.apache.hadoop.security.alias.CredentialProviderFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.DefaultHashService;
import org.apache.shiro.crypto.hash.Hash;
import org.apache.shiro.crypto.hash.HashRequest;
import org.apache.shiro.crypto.hash.HashService;
import org.apache.shiro.realm.ldap.JndiLdapContextFactory;
import org.apache.shiro.realm.ldap.JndiLdapRealm;
import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.realm.ldap.LdapUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.MutablePrincipalCollection;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementation of {@link org.apache.shiro.realm.ldap.JndiLdapRealm} that also returns each user's
 * groups. This implementation is heavily based on org.apache.isis.security.shiro.IsisLdapRealm.
 *
 * 

This implementation saves looked up ldap groups in Shiro Session to make them * easy to be looked up outside of this object * *

Sample config for shiro.ini: * *

* [main] * ldapRealm = org.apache.zeppelin.realm.LdapRealm * ldapRealm.contextFactory.url = ldap://localhost:33389 * ldapRealm.contextFactory.authenticationMechanism = simple * ldapRealm.contextFactory.systemUsername = uid=guest,ou=people,dc=hadoop,dc= apache,dc=org * ldapRealm.contextFactory.systemPassword = S{ALIAS=ldcSystemPassword} * ldapRealm.hadoopSecurityCredentialPath = jceks://file/user/zeppelin/zeppelin.jceks * ldapRealm.userDnTemplate = uid={0},ou=people,dc=hadoop,dc=apache,dc=org * # Ability to set ldap paging Size if needed default is 100 * ldapRealm.pagingSize = 200 * ldapRealm.authorizationEnabled = true * ldapRealm.searchBase = dc=hadoop,dc=apache,dc=org * ldapRealm.userSearchBase = dc=hadoop,dc=apache,dc=org * ldapRealm.groupSearchBase = ou=groups,dc=hadoop,dc=apache,dc=org * ldapRealm.userObjectClass = person * ldapRealm.groupObjectClass = groupofnames * # Allow userSearchAttribute to be customized * ldapRealm.userSearchAttributeName = sAMAccountName * ldapRealm.memberAttribute = member * # force usernames returned from ldap to lowercase useful for AD * ldapRealm.userLowerCase = true * # ability set searchScopes subtree (default), one, base * ldapRealm.userSearchScope = subtree; * ldapRealm.groupSearchScope = subtree; * ldapRealm.userSearchFilter = (&(objectclass=person)(sAMAccountName={0})) * ldapRealm.groupSearchFilter = (&(objectclass=groupofnames)(member={0})) * ldapRealm.memberAttributeValueTemplate=cn={0},ou=people,dc=hadoop,dc=apache,dc=org * # enable support for nested groups using the LDAP_MATCHING_RULE_IN_CHAIN operator * ldapRealm.groupSearchEnableMatchingRuleInChain = true *

* # optional mapping from physical groups to logical application roles * ldapRealm.rolesByGroup = \ LDN_USERS: user_role,\ NYK_USERS: user_role,\ HKG_USERS: user_role, * \GLOBAL_ADMIN: admin_role,\ DEMOS: self-install_role *

* # optional list of roles that are allowed to authenticate * ldapRealm.allowedRolesForAuthentication = admin_role,user_role *

* ldapRealm.permissionsByRole=\ user_role = *:ToDoItemsJdo:*:*,\*:ToDoItem:*:*; * \ self-install_role = *:ToDoItemsFixturesService:install:* ; \ admin_role = * *

* [urls] * **=authcBasic *

* securityManager.realms = $ldapRealm */ public class LdapRealm extends JndiLdapRealm { private static final SearchControls SUBTREE_SCOPE = new SearchControls(); private static final SearchControls ONELEVEL_SCOPE = new SearchControls(); private static final SearchControls OBJECT_SCOPE = new SearchControls(); private static final String SUBJECT_USER_ROLES = "subject.userRoles"; private static final String SUBJECT_USER_GROUPS = "subject.userGroups"; private static final String MEMBER_URL = "memberUrl"; private static final String POSIX_GROUP = "posixGroup"; // LDAP Operator '1.2.840.113556.1.4.1941' // walks the chain of ancestry in objects all the way to the root until it finds a match // see https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx private static final String MATCHING_RULE_IN_CHAIN_FORMAT = "(&(objectClass=%s)(%s:1.2.840.113556.1.4.1941:=%s))"; private static final Pattern TEMPLATE_PATTERN = Pattern.compile("\\{(\\d+?)\\}"); private static final String DEFAULT_PRINCIPAL_REGEX = "(.*)"; private static final String MEMBER_SUBSTITUTION_TOKEN = "{0}"; private static final String HASHING_ALGORITHM = "SHA-1"; private static final Logger log = LoggerFactory.getLogger(LdapRealm.class); static { SUBTREE_SCOPE.setSearchScope(SearchControls.SUBTREE_SCOPE); ONELEVEL_SCOPE.setSearchScope(SearchControls.ONELEVEL_SCOPE); OBJECT_SCOPE.setSearchScope(SearchControls.OBJECT_SCOPE); } private String searchBase; private String userSearchBase; private int pagingSize = 100; private boolean userLowerCase; private String principalRegex = DEFAULT_PRINCIPAL_REGEX; private Pattern principalPattern = Pattern.compile(DEFAULT_PRINCIPAL_REGEX); private String userDnTemplate = "{0}"; private String userSearchFilter = null; private String groupSearchFilter = null; private String userSearchAttributeTemplate = "{0}"; private String userSearchScope = "subtree"; private String groupSearchScope = "subtree"; private boolean groupSearchEnableMatchingRuleInChain; private String groupSearchBase; private String groupObjectClass = "groupOfNames"; // typical value: member, uniqueMember, memberUrl private String memberAttribute = "member"; private String groupIdAttribute = "cn"; private String memberAttributeValuePrefix = "uid="; private String memberAttributeValueSuffix = ""; private final Map rolesByGroup = new LinkedHashMap<>(); private final List allowedRolesForAuthentication = new ArrayList<>(); private final Map> permissionsByRole = new LinkedHashMap<>(); private String hadoopSecurityCredentialPath; final String keystorePass = "ldapRealm.systemPassword"; private boolean authorizationEnabled; private String userSearchAttributeName; private String userObjectClass = "person"; private HashService hashService = new DefaultHashService(); public void setHadoopSecurityCredentialPath(String hadoopSecurityCredentialPath) { this.hadoopSecurityCredentialPath = hadoopSecurityCredentialPath; } public LdapRealm() { HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(HASHING_ALGORITHM); setCredentialsMatcher(credentialsMatcher); } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws org.apache.shiro.authc.AuthenticationException { try { return super.doGetAuthenticationInfo(token); } catch (org.apache.shiro.authc.AuthenticationException ae) { throw ae; } } protected void onInit() { super.onInit(); if (!org.apache.commons.lang3.StringUtils.isEmpty(this.hadoopSecurityCredentialPath) && getContextFactory() != null) { ((JndiLdapContextFactory) getContextFactory()).setSystemPassword( getSystemPassword(this.hadoopSecurityCredentialPath, keystorePass)); } } static String getSystemPassword(String hadoopSecurityCredentialPath, String keystorePass) { String password = ""; try { Configuration configuration = new Configuration(); configuration.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, hadoopSecurityCredentialPath); CredentialProvider provider = CredentialProviderFactory.getProviders(configuration).get(0); CredentialProvider.CredentialEntry credEntry = provider.getCredentialEntry(keystorePass); if (credEntry != null) { password = new String(credEntry.getCredential()); } } catch (IOException e) { throw new ShiroException("Error from getting credential entry from keystore", e); } if (org.apache.commons.lang3.StringUtils.isEmpty(password)) { throw new ShiroException("Error getting SystemPassword from the provided keystore:" + keystorePass + ", in path:" + hadoopSecurityCredentialPath); } return password; } /** * This overrides the implementation of queryForAuthenticationInfo inside JndiLdapRealm. * In addition to calling the super method for authentication it also tries to validate * if this user has atleast one of the allowed roles for authentication. In case the property * allowedRolesForAuthentication is empty this check always returns true. * * @param token the submitted authentication token that triggered the authentication attempt. * @param ldapContextFactory factory used to retrieve LDAP connections. * @return AuthenticationInfo instance representing the authenticated user's information. * @throws NamingException if any LDAP errors occur. */ @Override protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException { AuthenticationInfo info = super.queryForAuthenticationInfo(token, ldapContextFactory); // Credentials were verified. Verify that the principal has all allowedRulesForAuthentication if (!hasAllowedAuthenticationRules(info.getPrincipals(), ldapContextFactory)) { throw new NamingException("Principal does not have any of the allowedRolesForAuthentication"); } return info; } /** * Get groups from LDAP. * * @param principals * the principals of the Subject whose AuthenticationInfo should * be queried from the LDAP server. * @param ldapContextFactory * factory used to retrieve LDAP connections. * @return an {@link AuthorizationInfo} instance containing information * retrieved from the LDAP server. * @throws NamingException * if any LDAP errors occur during the search. */ @Override public AuthorizationInfo queryForAuthorizationInfo(final PrincipalCollection principals, final LdapContextFactory ldapContextFactory) throws NamingException { if (!isAuthorizationEnabled()) { return null; } final Set roleNames = getRoles(principals, ldapContextFactory); if (log.isDebugEnabled()) { log.debug("RolesNames Authorization: " + roleNames); } SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roleNames); Set stringPermissions = permsFor(roleNames); simpleAuthorizationInfo.setStringPermissions(stringPermissions); return simpleAuthorizationInfo; } private boolean hasAllowedAuthenticationRules(PrincipalCollection principals, final LdapContextFactory ldapContextFactory) throws NamingException { boolean allowed = allowedRolesForAuthentication.isEmpty(); if (!allowed) { Set roles = getRoles(principals, ldapContextFactory); for (String allowedRole : allowedRolesForAuthentication) { if (roles.contains(allowedRole)) { log.debug("Allowed role for user [" + allowedRole + "] found."); allowed = true; break; } } } return allowed; } private Set getRoles(PrincipalCollection principals, final LdapContextFactory ldapContextFactory) throws NamingException { final String username = (String) getAvailablePrincipal(principals); LdapContext systemLdapCtx = null; try { systemLdapCtx = ldapContextFactory.getSystemLdapContext(); return rolesFor(principals, username, systemLdapCtx, ldapContextFactory, SecurityUtils.getSubject().getSession()); } catch (Throwable t) { log.warn("Failed to get roles in current context for " + username, t); return Collections.emptySet(); } finally { LdapUtils.closeContext(systemLdapCtx); } } protected Set rolesFor(PrincipalCollection principals, String userNameIn, final LdapContext ldapCtx, final LdapContextFactory ldapContextFactory, Session session) throws NamingException { final Set roleNames = new HashSet<>(); final Set groupNames = new HashSet<>(); final String userName; if (getUserLowerCase()) { log.debug("userLowerCase true"); userName = userNameIn.toLowerCase(); } else { userName = userNameIn; } String userDn = getUserDnForSearch(userName); // Activate paged results int pageSize = getPagingSize(); if (log.isDebugEnabled()) { log.debug("Ldap PagingSize: " + pageSize); } int numResults = 0; byte[] cookie = null; try { ldapCtx.addToEnvironment(Context.REFERRAL, "ignore"); ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize, Control.NONCRITICAL)}); do { // ldapsearch -h localhost -p 33389 -D // uid=guest,ou=people,dc=hadoop,dc=apache,dc=org -w guest-password // -b dc=hadoop,dc=apache,dc=org -s sub '(objectclass=*)' NamingEnumeration searchResultEnum = null; SearchControls searchControls = getGroupSearchControls(); try { if (groupSearchEnableMatchingRuleInChain) { searchResultEnum = ldapCtx.search( getGroupSearchBase(), String.format( MATCHING_RULE_IN_CHAIN_FORMAT, groupObjectClass, memberAttribute, userDn), searchControls); while (searchResultEnum != null && searchResultEnum.hasMore()) { // searchResults contains all the groups in search scope numResults++; final SearchResult group = searchResultEnum.next(); Attribute attribute = group.getAttributes().get(getGroupIdAttribute()); String groupName = attribute.get().toString(); String roleName = roleNameFor(groupName); if (roleName != null) { roleNames.add(roleName); } else { roleNames.add(groupName); } } } else { // Default group search filter String searchFilter = String.format("(objectclass=%1$s)", groupObjectClass); // If group search filter is defined in Shiro config, then use it if (groupSearchFilter != null) { searchFilter = expandTemplate(groupSearchFilter, userName); //searchFilter = String.format("%1$s", groupSearchFilter); } if (log.isDebugEnabled()) { log.debug("Group SearchBase|SearchFilter|GroupSearchScope: " + getGroupSearchBase() + "|" + searchFilter + "|" + groupSearchScope); } searchResultEnum = ldapCtx.search( getGroupSearchBase(), searchFilter, searchControls); while (searchResultEnum != null && searchResultEnum.hasMore()) { // searchResults contains all the groups in search scope numResults++; final SearchResult group = searchResultEnum.next(); addRoleIfMember(userDn, group, roleNames, groupNames, ldapContextFactory); } } } catch (PartialResultException e) { log.debug("Ignoring PartitalResultException"); } finally { if (searchResultEnum != null) { searchResultEnum.close(); } } // Re-activate paged results ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize, cookie, Control.CRITICAL)}); } while (cookie != null); } catch (SizeLimitExceededException e) { log.info("Only retrieved first " + numResults + " groups due to SizeLimitExceededException."); } catch (IOException e) { log.error("Unabled to setup paged results"); } // save role names and group names in session so that they can be // easily looked up outside of this object session.setAttribute(SUBJECT_USER_ROLES, roleNames); session.setAttribute(SUBJECT_USER_GROUPS, groupNames); if (!groupNames.isEmpty() && (principals instanceof MutablePrincipalCollection)) { ((MutablePrincipalCollection) principals).addAll(groupNames, getName()); } if (log.isDebugEnabled()) { log.debug("User RoleNames: " + userName + "::" + roleNames); } return roleNames; } protected String getUserDnForSearch(String userName) { if (userSearchAttributeName == null || userSearchAttributeName.isEmpty()) { // memberAttributeValuePrefix and memberAttributeValueSuffix // were computed from memberAttributeValueTemplate return memberDn(userName); } else { return getUserDn(userName); } } private void addRoleIfMember(final String userDn, final SearchResult group, final Set roleNames, final Set groupNames, final LdapContextFactory ldapContextFactory) throws NamingException { NamingEnumeration attributeEnum = null; NamingEnumeration ne = null; try { LdapName userLdapDn = new LdapName(userDn); Attribute attribute = group.getAttributes().get(getGroupIdAttribute()); String groupName = attribute.get().toString(); attributeEnum = group.getAttributes().getAll(); while (attributeEnum.hasMore()) { final Attribute attr = attributeEnum.next(); if (!memberAttribute.equalsIgnoreCase(attr.getID())) { continue; } ne = attr.getAll(); while (ne.hasMore()) { String attrValue = ne.next().toString(); if (memberAttribute.equalsIgnoreCase(MEMBER_URL)) { boolean dynamicGroupMember = isUserMemberOfDynamicGroup(userLdapDn, attrValue, ldapContextFactory); if (dynamicGroupMember) { groupNames.add(groupName); String roleName = roleNameFor(groupName); if (roleName != null) { roleNames.add(roleName); } else { roleNames.add(groupName); } } } else { // posix groups' members don' include the entire dn if (groupObjectClass.equalsIgnoreCase(POSIX_GROUP)) { attrValue = memberDn(attrValue); } if (userLdapDn.equals(new LdapName(attrValue))) { groupNames.add(groupName); String roleName = roleNameFor(groupName); if (roleName != null) { roleNames.add(roleName); } else { roleNames.add(groupName); } break; } } } } } finally { try { if (attributeEnum != null) { attributeEnum.close(); } } finally { if (ne != null) { ne.close(); } } } } private String memberDn(String attrValue) { return memberAttributeValuePrefix + attrValue + memberAttributeValueSuffix; } public Map getListRoles() { Map groupToRoles = getRolesByGroup(); Map roles = new HashMap<>(); for (Map.Entry entry : groupToRoles.entrySet()) { roles.put(entry.getValue(), entry.getKey()); } return roles; } private String roleNameFor(String groupName) { return !rolesByGroup.isEmpty() ? rolesByGroup.get(groupName) : groupName; } private Set permsFor(Set roleNames) { Set perms = new LinkedHashSet<>(); // preserve order for (String role : roleNames) { List permsForRole = permissionsByRole.get(role); if (log.isDebugEnabled()) { log.debug("PermsForRole: " + role); log.debug("PermByRole: " + permsForRole); } if (permsForRole != null) { perms.addAll(permsForRole); } } return perms; } public String getSearchBase() { return searchBase; } public void setSearchBase(String searchBase) { this.searchBase = searchBase; } public String getUserSearchBase() { return (userSearchBase != null && !userSearchBase.isEmpty()) ? userSearchBase : searchBase; } public void setUserSearchBase(String userSearchBase) { this.userSearchBase = userSearchBase; } public int getPagingSize() { return pagingSize; } public void setPagingSize(int pagingSize) { this.pagingSize = pagingSize; } public String getGroupSearchBase() { return (groupSearchBase != null && !groupSearchBase.isEmpty()) ? groupSearchBase : searchBase; } public void setGroupSearchBase(String groupSearchBase) { this.groupSearchBase = groupSearchBase; } public String getGroupObjectClass() { return groupObjectClass; } public void setGroupObjectClass(String groupObjectClassAttribute) { this.groupObjectClass = groupObjectClassAttribute; } public String getMemberAttribute() { return memberAttribute; } public void setMemberAttribute(String memberAttribute) { this.memberAttribute = memberAttribute; } public String getGroupIdAttribute() { return groupIdAttribute; } public void setGroupIdAttribute(String groupIdAttribute) { this.groupIdAttribute = groupIdAttribute; } /** * Set Member Attribute Template for LDAP. * * @param template * DN template to be used to query ldap. * @throws IllegalArgumentException * if template is empty or null. */ public void setMemberAttributeValueTemplate(String template) { if (!StringUtils.hasText(template)) { String msg = "User DN template cannot be null or empty."; throw new IllegalArgumentException(msg); } int index = template.indexOf(MEMBER_SUBSTITUTION_TOKEN); if (index < 0) { String msg = "Member attribute value template must contain the '" + MEMBER_SUBSTITUTION_TOKEN + "' replacement token to understand how to " + "parse the group members."; throw new IllegalArgumentException(msg); } String prefix = template.substring(0, index); String suffix = template.substring(prefix.length() + MEMBER_SUBSTITUTION_TOKEN.length()); this.memberAttributeValuePrefix = prefix; this.memberAttributeValueSuffix = suffix; } public void setAllowedRolesForAuthentication(List allowedRolesForAuthencation) { this.allowedRolesForAuthentication.addAll(allowedRolesForAuthencation); } public void setRolesByGroup(Map rolesByGroup) { this.rolesByGroup.putAll(rolesByGroup); } public Map getRolesByGroup() { return rolesByGroup; } public void setPermissionsByRole(String permissionsByRoleStr) { permissionsByRole.putAll(parsePermissionByRoleString(permissionsByRoleStr)); } public Map> getPermissionsByRole() { return permissionsByRole; } public boolean isAuthorizationEnabled() { return authorizationEnabled; } public void setAuthorizationEnabled(boolean authorizationEnabled) { this.authorizationEnabled = authorizationEnabled; } public String getUserSearchAttributeName() { return userSearchAttributeName; } /** * Set User Search Attribute Name for LDAP. * * @param userSearchAttributeName * userAttribute to search ldap. */ public void setUserSearchAttributeName(String userSearchAttributeName) { if (userSearchAttributeName != null) { userSearchAttributeName = userSearchAttributeName.trim(); } this.userSearchAttributeName = userSearchAttributeName; } public String getUserObjectClass() { return userObjectClass; } public void setUserObjectClass(String userObjectClass) { this.userObjectClass = userObjectClass; } private Map> parsePermissionByRoleString(String permissionsByRoleStr) { Map> perms = new HashMap>(); // split by semicolon ; then by eq = then by comma , StringTokenizer stSem = new StringTokenizer(permissionsByRoleStr, ";"); while (stSem.hasMoreTokens()) { String roleAndPerm = stSem.nextToken(); StringTokenizer stEq = new StringTokenizer(roleAndPerm, "="); if (stEq.countTokens() != 2) { continue; } String role = stEq.nextToken().trim(); String perm = stEq.nextToken().trim(); StringTokenizer stCom = new StringTokenizer(perm, ","); List permList = new ArrayList(); while (stCom.hasMoreTokens()) { permList.add(stCom.nextToken().trim()); } perms.put(role, permList); } return perms; } boolean isUserMemberOfDynamicGroup(LdapName userLdapDn, String memberUrl, final LdapContextFactory ldapContextFactory) throws NamingException { // ldap://host:port/dn?attributes?scope?filter?extensions if (memberUrl == null) { return false; } String[] tokens = memberUrl.split("\\?"); if (tokens.length < 4) { return false; } String searchBaseString = tokens[0].substring(tokens[0].lastIndexOf("/") + 1); String searchScope = tokens[2]; String searchFilter = tokens[3]; LdapName searchBaseDn = new LdapName(searchBaseString); // do scope test if ("base".equalsIgnoreCase(searchScope)) { log.debug("DynamicGroup SearchScope base"); return false; } if (!userLdapDn.toString().endsWith(searchBaseDn.toString())) { return false; } if ("one".equalsIgnoreCase(searchScope) && (userLdapDn.size() != searchBaseDn.size() - 1)) { log.debug("DynamicGroup SearchScope one"); return false; } // search for the filter, substituting base with userDn // search for base_dn=userDn, scope=base, filter=filter LdapContext systemLdapCtx = null; systemLdapCtx = ldapContextFactory.getSystemLdapContext(); boolean member = false; NamingEnumeration searchResultEnum = null; try { searchResultEnum = systemLdapCtx.search(userLdapDn, searchFilter, "sub".equalsIgnoreCase(searchScope) ? SUBTREE_SCOPE : ONELEVEL_SCOPE); if (searchResultEnum.hasMore()) { return true; } } finally { try { if (searchResultEnum != null) { searchResultEnum.close(); } } finally { LdapUtils.closeContext(systemLdapCtx); } } return member; } public String getPrincipalRegex() { return principalRegex; } /** * Set Regex for Principal LDAP. * * @param regex * regex to use to search for principal in shiro. */ public void setPrincipalRegex(String regex) { if (regex == null || regex.trim().isEmpty()) { principalPattern = Pattern.compile(DEFAULT_PRINCIPAL_REGEX); principalRegex = DEFAULT_PRINCIPAL_REGEX; } else { regex = regex.trim(); Pattern pattern = Pattern.compile(regex); principalPattern = pattern; principalRegex = regex; } } public String getUserSearchAttributeTemplate() { return userSearchAttributeTemplate; } public void setUserSearchAttributeTemplate(final String template) { this.userSearchAttributeTemplate = (template == null ? null : template.trim()); } public String getUserSearchFilter() { return userSearchFilter; } public void setUserSearchFilter(final String filter) { this.userSearchFilter = (filter == null ? null : filter.trim()); } public String getGroupSearchFilter() { return groupSearchFilter; } public void setGroupSearchFilter(final String filter) { this.groupSearchFilter = (filter == null ? null : filter.trim()); } public boolean getUserLowerCase() { return userLowerCase; } public void setUserLowerCase(boolean userLowerCase) { this.userLowerCase = userLowerCase; } public String getUserSearchScope() { return userSearchScope; } public void setUserSearchScope(final String scope) { this.userSearchScope = (scope == null ? null : scope.trim().toLowerCase()); } public String getGroupSearchScope() { return groupSearchScope; } public void setGroupSearchScope(final String scope) { this.groupSearchScope = (scope == null ? null : scope.trim().toLowerCase()); } public boolean isGroupSearchEnableMatchingRuleInChain() { return groupSearchEnableMatchingRuleInChain; } public void setGroupSearchEnableMatchingRuleInChain( boolean groupSearchEnableMatchingRuleInChain) { this.groupSearchEnableMatchingRuleInChain = groupSearchEnableMatchingRuleInChain; } private SearchControls getUserSearchControls() { SearchControls searchControls = SUBTREE_SCOPE; if ("onelevel".equalsIgnoreCase(userSearchScope)) { searchControls = ONELEVEL_SCOPE; } else if ("object".equalsIgnoreCase(userSearchScope)) { searchControls = OBJECT_SCOPE; } return searchControls; } protected SearchControls getGroupSearchControls() { SearchControls searchControls = SUBTREE_SCOPE; if ("onelevel".equalsIgnoreCase(groupSearchScope)) { searchControls = ONELEVEL_SCOPE; } else if ("object".equalsIgnoreCase(groupSearchScope)) { searchControls = OBJECT_SCOPE; } return searchControls; } @Override public void setUserDnTemplate(final String template) throws IllegalArgumentException { userDnTemplate = template; } private String matchPrincipal(final String principal) { Matcher matchedPrincipal = principalPattern.matcher(principal); if (!matchedPrincipal.matches()) { throw new IllegalArgumentException("Principal " + principal + " does not match " + principalRegex); } return matchedPrincipal.group(); } /** * Returns the LDAP User Distinguished Name (DN) to use when acquiring an * {@link javax.naming.ldap.LdapContext LdapContext} from the * {@link LdapContextFactory}. *

* If the the {@link #getUserDnTemplate() userDnTemplate} property has been * set, this implementation will construct the User DN by substituting the * specified {@code principal} into the configured template. If the * {@link #getUserDnTemplate() userDnTemplate} has not been set, the method * argument will be returned directly (indicating that the submitted * authentication token principal is the User DN). * * @param principal * the principal to substitute into the configured * {@link #getUserDnTemplate() userDnTemplate}. * @return the constructed User DN to use at runtime when acquiring an * {@link javax.naming.ldap.LdapContext}. * @throws IllegalArgumentException * if the method argument is null or empty * @throws IllegalStateException * if the {@link #getUserDnTemplate userDnTemplate} has not been * set. * @see LdapContextFactory#getLdapContext(Object, Object) */ @Override protected String getUserDn(final String principal) throws IllegalArgumentException, IllegalStateException { String userDn; String matchedPrincipal = matchPrincipal(principal); String userSearchBase = getUserSearchBase(); String userSearchAttributeName = getUserSearchAttributeName(); // If not searching use the userDnTemplate and return. if ((userSearchBase == null || userSearchBase.isEmpty()) || (userSearchAttributeName == null && userSearchFilter == null && !"object".equalsIgnoreCase(userSearchScope))) { userDn = expandTemplate(userDnTemplate, matchedPrincipal); if (log.isDebugEnabled()) { log.debug("LDAP UserDN and Principal: " + userDn + "," + principal); } return userDn; } // Create the searchBase and searchFilter from config. String searchBase = expandTemplate(getUserSearchBase(), matchedPrincipal); String searchFilter = null; if (userSearchFilter == null) { if (userSearchAttributeName == null) { searchFilter = String.format("(objectclass=%1$s)", getUserObjectClass()); } else { searchFilter = String.format("(&(objectclass=%1$s)(%2$s=%3$s))", getUserObjectClass(), userSearchAttributeName, expandTemplate(getUserSearchAttributeTemplate(), matchedPrincipal)); } } else { searchFilter = expandTemplate(userSearchFilter, matchedPrincipal); } SearchControls searchControls = getUserSearchControls(); // Search for userDn and return. LdapContext systemLdapCtx = null; NamingEnumeration searchResultEnum = null; try { systemLdapCtx = getContextFactory().getSystemLdapContext(); if (log.isDebugEnabled()) { log.debug("SearchBase,SearchFilter,UserSearchScope: " + searchBase + "," + searchFilter + "," + userSearchScope); } searchResultEnum = systemLdapCtx.search(searchBase, searchFilter, searchControls); // SearchResults contains all the entries in search scope if (searchResultEnum.hasMore()) { SearchResult searchResult = searchResultEnum.next(); userDn = searchResult.getNameInNamespace(); if (log.isDebugEnabled()) { log.debug("UserDN Returned,Principal: " + userDn + "," + principal); } return userDn; } else { throw new IllegalArgumentException("Illegal principal name: " + principal); } } catch (AuthenticationException ne) { ne.printStackTrace(); throw new IllegalArgumentException("Illegal principal name: " + principal); } catch (NamingException ne) { throw new IllegalArgumentException("Hit NamingException: " + ne.getMessage()); } finally { try { if (searchResultEnum != null) { searchResultEnum.close(); } } catch (NamingException ne) { // Ignore exception on close. } finally { LdapUtils.closeContext(systemLdapCtx); } } } @Override protected AuthenticationInfo createAuthenticationInfo(AuthenticationToken token, Object ldapPrincipal, Object ldapCredentials, LdapContext ldapContext) throws NamingException { HashRequest.Builder builder = new HashRequest.Builder(); Hash credentialsHash = hashService .computeHash(builder.setSource(token.getCredentials()) .setAlgorithmName(HASHING_ALGORITHM).build()); return new SimpleAuthenticationInfo(token.getPrincipal(), credentialsHash.toHex(), credentialsHash.getSalt(), getName()); } protected static final String expandTemplate(final String template, final String input) { return template.replace(MEMBER_SUBSTITUTION_TOKEN, input); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy