Java tutorial
/* * Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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.wso2.carbon.user.core.ldap; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.base.MultitenantConstants; import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.user.api.Properties; import org.wso2.carbon.user.api.Property; import org.wso2.carbon.user.api.RealmConfiguration; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.UserRealm; import org.wso2.carbon.user.core.UserStoreConfigConstants; import org.wso2.carbon.user.core.UserStoreException; import org.wso2.carbon.user.core.claim.ClaimManager; import org.wso2.carbon.user.core.common.RoleContext; import org.wso2.carbon.user.core.hybrid.HybridRoleManager; import org.wso2.carbon.user.core.profile.ProfileConfigurationManager; import org.wso2.carbon.user.core.tenant.Tenant; import org.wso2.carbon.user.core.util.DatabaseUtil; import org.wso2.carbon.user.core.util.JNDIUtil; import org.wso2.carbon.user.core.util.UserCoreUtil; import javax.naming.Name; import javax.naming.NameParser; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import javax.naming.directory.DirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.sql.DataSource; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.StringTokenizer; /** * This class is capable of get connected to an external or internal LDAP based user store in * read/write mode. Create, Update, Delete users and groups are supported. */ @SuppressWarnings({}) public class ReadWriteLDAPUserStoreManager extends ReadOnlyLDAPUserStoreManager { public static final String PASSWORD_HASH_METHOD = UserStoreConfigConstants.passwordHashMethod; public static final String PASSWORD_HASH_METHOD_SHA = "SHA"; public static final String PASSWORD_HASH_METHOD_MD5 = "MD5"; public static final String ATTR_NAME_CN = "cn"; public static final String ATTR_NAME_SN = "sn"; protected static final String KRB5_PRINCIPAL_NAME_ATTRIBUTE = "krb5PrincipalName"; protected static final String KRB5_KEY_VERSION_NUMBER_ATTRIBUTE = "krb5KeyVersionNumber"; protected static final String EMPTY_ATTRIBUTE_STRING = ""; private static final String MULTI_ATTRIBUTE_SEPARATOR = "MultiAttributeSeparator"; /* To track whether this is the first time startup of the server. */ protected static boolean isFirstStartup = true; private static Log logger = LogFactory.getLog(ReadWriteLDAPUserStoreManager.class); private static Log log = LogFactory.getLog(ReadWriteLDAPUserStoreManager.class); protected Random random = new Random(); protected boolean kdcEnabled = false; public ReadWriteLDAPUserStoreManager() { } public ReadWriteLDAPUserStoreManager(RealmConfiguration realmConfig, Map<String, Object> properties, ClaimManager claimManager, ProfileConfigurationManager profileManager, UserRealm realm, Integer tenantId) throws UserStoreException { super(realmConfig, properties, claimManager, profileManager, realm, tenantId, true); if (log.isDebugEnabled()) { log.debug("Read-Write UserStoreManager initialization started " + System.currentTimeMillis()); } this.realmConfig = realmConfig; this.claimManager = claimManager; this.userRealm = realm; this.tenantId = tenantId; this.kdcEnabled = UserCoreUtil.isKdcEnabled(realmConfig); checkRequiredUserStoreConfigurations(); dataSource = (DataSource) properties.get(UserCoreConstants.DATA_SOURCE); if (dataSource == null) { // avoid returning null dataSource = DatabaseUtil.getRealmDataSource(realmConfig); } if (dataSource == null) { throw new UserStoreException("Data Source is null"); } properties.put(UserCoreConstants.DATA_SOURCE, dataSource); ReadWriteLDAPUserStoreManager.isFirstStartup = (Boolean) properties .get(UserCoreConstants.FIRST_STARTUP_CHECK); // hybrid role manager used if only users needs to be read-written. hybridRoleManager = new HybridRoleManager(dataSource, tenantId, realmConfig, userRealm); // obtain the ldap connection source that was created in // DefaultRealmService. this.connectionSource = (LDAPConnectionContext) properties.get(UserCoreConstants.LDAP_CONNECTION_SOURCE); if (connectionSource == null) { connectionSource = new LDAPConnectionContext(realmConfig); } DirContext dirContext = null; try { dirContext = connectionSource.getContext(); log.info("LDAP connection created successfully in read-write mode"); } catch (Exception e) { throw new UserStoreException( "Cannot create connection to LDAP server. Error message " + e.getMessage()); } finally { JNDIUtil.closeContext(dirContext); } this.userRealm = realm; //persist domain this.persistDomain(); doInitialSetup(); if (realmConfig.isPrimary()) { addInitialAdminData(Boolean.parseBoolean(realmConfig.getAddAdmin()), !isInitSetupDone()); } /* * Initialize user roles cache as implemented in AbstractUserStoreManager */ initUserRolesCache(); if (log.isDebugEnabled()) { log.debug("Read-Write UserStoreManager initialization ended " + System.currentTimeMillis()); } } /** * This constructor is not used. So not applying the changes done to above constructor. * * @param realmConfig * @param claimManager * @param profileManager * @throws UserStoreException */ public ReadWriteLDAPUserStoreManager(RealmConfiguration realmConfig, ClaimManager claimManager, ProfileConfigurationManager profileManager) throws UserStoreException { super(realmConfig, claimManager, profileManager); // checkRequiredUserStoreConfiguration(); } @Override public boolean isReadOnly() { return false; } /** * @return */ protected String getRealmName() { // First check whether realm name is defined in the configuration String defaultRealmName = this.realmConfig .getUserStoreProperty(UserCoreConstants.RealmConfig.DEFAULT_REALM_NAME); if (defaultRealmName != null) { return defaultRealmName; } // If not build the realm name from the search base. // Here the realm name will be a concatenation of dc components in the // search base. String searchBase = this.realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); String[] domainComponents = searchBase.split("dc="); StringBuilder builder = new StringBuilder(); for (String dc : domainComponents) { if (!dc.contains("=")) { String trimmedDc = dc.trim(); if (trimmedDc.endsWith(",")) { builder.append(trimmedDc.replace(',', '.')); } else { builder.append(trimmedDc); } } } return builder.toString().toUpperCase(Locale.ENGLISH); } @Override public void doAddUser(String userName, Object credential, String[] roleList, Map<String, String> claims, String profileName) throws UserStoreException { this.doAddUser(userName, credential, roleList, claims, profileName, false); } @Override public void doAddUser(String userName, Object credential, String[] roleList, Map<String, String> claims, String profileName, boolean requirePasswordChange) throws UserStoreException { /* getting search base directory context */ DirContext dirContext = getSearchBaseDirectoryContext(); /* getting add user basic attributes */ BasicAttributes basicAttributes = getAddUserBasicAttributes(escapeSpecialCharactersForDN(userName)); BasicAttribute userPassword = new BasicAttribute("userPassword"); userPassword.add(UserCoreUtil.getPasswordToStore((String) credential, this.realmConfig.getUserStoreProperty(PASSWORD_HASH_METHOD), kdcEnabled)); basicAttributes.put(userPassword); /* setting claims */ setUserClaims(claims, basicAttributes, userName); try { NameParser ldapParser = dirContext.getNameParser(""); Name compoundName = ldapParser.parse(realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_ATTRIBUTE) + "=" + escapeSpecialCharactersForDN(userName)); if (log.isDebugEnabled()) { log.debug("Binding user: " + compoundName); } dirContext.bind(compoundName, null, basicAttributes); } catch (NamingException e) { String errorMessage = "Cannot access the directory context or " + "user already exists in the system for user :" + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeContext(dirContext); } try { /* update the user roles */ doUpdateRoleListOfUser(userName, null, roleList); if (log.isDebugEnabled()) { log.debug("Roles are added for user : " + userName + " successfully."); } } catch (UserStoreException e) { String errorMessage = "User is added. But error while updating role list of user : " + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } } /** * Returns the directory context for the user search base * * @return * @throws NamingException * @throws UserStoreException */ protected DirContext getSearchBaseDirectoryContext() throws UserStoreException { DirContext mainDirContext = this.connectionSource.getContext(); String searchBase = realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); try { return (DirContext) mainDirContext.lookup(searchBase); } catch (NamingException e) { String errorMessage = "Can not access the directory context or" + "user already exists in the system"; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeContext(mainDirContext); } } /** * Returns a BasicAttributes object with basic required attributes * * @param userName * @return */ protected BasicAttributes getAddUserBasicAttributes(String userName) { BasicAttributes basicAttributes = new BasicAttributes(true); String userEntryObjectClassProperty = realmConfig .getUserStoreProperty(LDAPConstants.USER_ENTRY_OBJECT_CLASS); BasicAttribute objectClass = new BasicAttribute(LDAPConstants.OBJECT_CLASS_NAME); String[] objectClassHierarchy = userEntryObjectClassProperty.split("/"); for (String userObjectClass : objectClassHierarchy) { if (userObjectClass != null && !userObjectClass.trim().equals("")) { objectClass.add(userObjectClass.trim()); } } // If KDC is enabled we have to set KDC specific object classes also if (kdcEnabled) { // Add Kerberos specific object classes objectClass.add("krb5principal"); objectClass.add("krb5kdcentry"); objectClass.add("subschema"); } basicAttributes.put(objectClass); BasicAttribute userNameAttribute = new BasicAttribute( realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_ATTRIBUTE)); userNameAttribute.add(userName); basicAttributes.put(userNameAttribute); if (kdcEnabled) { CarbonContext cc = CarbonContext.getThreadLocalCarbonContext(); if (cc != null) { String tenantDomainName = cc.getTenantDomain(); if (!MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomainName)) { userName = userName + UserCoreConstants.PRINCIPAL_USERNAME_SEPARATOR + tenantDomainName; } else { userName = userName + UserCoreConstants.PRINCIPAL_USERNAME_SEPARATOR + MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; } } String principal = userName + "@" + this.getRealmName(); BasicAttribute principalAttribute = new BasicAttribute(KRB5_PRINCIPAL_NAME_ATTRIBUTE); principalAttribute.add(principal); basicAttributes.put(principalAttribute); BasicAttribute versionNumberAttribute = new BasicAttribute(KRB5_KEY_VERSION_NUMBER_ATTRIBUTE); versionNumberAttribute.add("0"); basicAttributes.put(versionNumberAttribute); } return basicAttributes; } /** * Sets the set of claims provided at adding users * * @param claims * @param basicAttributes * @throws UserStoreException */ protected void setUserClaims(Map<String, String> claims, BasicAttributes basicAttributes, String userName) throws UserStoreException { BasicAttribute claim; boolean debug = log.isDebugEnabled(); log.debug("Processing user claims"); /* * we keep boolean values to know whether compulsory attributes 'sn' and 'cn' are set during * setting claims. */ boolean isSNExists = false; boolean isCNExists = false; if (claims != null) { for (Map.Entry<String, String> entry : claims.entrySet()) { /* * LDAP does not allow for empty values. If an attribute has a value its stored * with the entry, otherwise it is not. Hence needs to check for empty values before * storing the attribute. */ if (EMPTY_ATTRIBUTE_STRING.equals(entry.getValue())) { continue; } // needs to get attribute name from claim mapping String claimURI = entry.getKey(); if (debug) { log.debug("Claim URI: " + claimURI); } String attributeName = null; try { attributeName = getClaimAtrribute(claimURI, userName, null); } catch (org.wso2.carbon.user.api.UserStoreException e) { String errorMessage = "Error in obtaining claim mapping."; throw new UserStoreException(errorMessage, e); } if (ATTR_NAME_CN.equals(attributeName)) { isCNExists = true; } else if (ATTR_NAME_SN.equals(attributeName)) { isSNExists = true; } if (debug) { log.debug("Mapped attribute: " + attributeName); log.debug("Attribute value: " + claims.get(entry.getKey())); } claim = new BasicAttribute(attributeName); claim.add(claims.get(entry.getKey())); basicAttributes.put(claim); } } // If required attributes cn, sn are not set during claim mapping, // set them as user names if (!isCNExists) { BasicAttribute cn = new BasicAttribute("cn"); cn.add(escapeSpecialCharactersForDNWithStar(userName)); basicAttributes.put(cn); } if (!isSNExists) { BasicAttribute sn = new BasicAttribute("sn"); sn.add(escapeSpecialCharactersForDNWithStar(userName)); basicAttributes.put(sn); } } @SuppressWarnings("deprecation") @Override public void doDeleteUser(String userName) throws UserStoreException { boolean debug = log.isDebugEnabled(); if (debug) { log.debug("Deleting user: " + userName); } // delete user from LDAP group if read-write enabled. String userNameAttribute = realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_ATTRIBUTE); String searchFilter = realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_SEARCH_FILTER); searchFilter = searchFilter.replace("?", escapeSpecialCharactersForFilter(userName)); String[] returningUserAttributes = new String[] { userNameAttribute }; DirContext mainDirContext = this.connectionSource.getContext(); NamingEnumeration<SearchResult> userResults = searchInUserBase(searchFilter, returningUserAttributes, SearchControls.SUBTREE_SCOPE, mainDirContext); NamingEnumeration<SearchResult> groupResults = null; DirContext subDirContext = null; try { SearchResult userResult = null; String userDN = null; // here we assume only one user // TODO: what to do if there are more than one user while (userResults.hasMore()) { userResult = userResults.next(); userDN = userResult.getName(); log.debug("User DN: " + userDN); } // LDAP roles of user to delete the mapping List<String> roles = new ArrayList<String>(); String[] externalRoles = doGetExternalRoleListOfUser(userName, "*"); roles.addAll(Arrays.asList(externalRoles)); if (isSharedGroupEnabled()) { String[] sharedRoles = doGetSharedRoleListOfUser(null, userName, "*"); if (sharedRoles != null) { roles.addAll(Arrays.asList(sharedRoles)); } } String[] rolesOfUser = roles.toArray(new String[roles.size()]); if (rolesOfUser.length != 0) { String[] returningGroupAttributes = new String[] { realmConfig.getUserStoreProperty(LDAPConstants.MEMBERSHIP_ATTRIBUTE) }; for (String role : rolesOfUser) { RoleContext context = createRoleContext(role); String searchBase = ((LDAPRoleContext) context).getSearchBase(); searchFilter = ((LDAPRoleContext) context).getSearchFilter(); role = context.getRoleName(); if (role.indexOf("/") > -1) { role = (role.split("/"))[1]; } String grpSearchFilter = searchFilter.replace("?", escapeSpecialCharactersForFilter(role)); groupResults = searchInGroupBase(grpSearchFilter, returningGroupAttributes, SearchControls.SUBTREE_SCOPE, mainDirContext, searchBase); SearchResult groupResult = null; while (groupResults.hasMore()) { groupResult = groupResults.next(); } if (isOnlyUserInRole(userDN, groupResult) && !emptyRolesAllowed) { String errorMessage = "User: " + userName + " is the only user " + "in " + role + "." + "There should be at " + "least one user" + " in the role. Hence can" + " not delete the user."; throw new UserStoreException(errorMessage); } } // delete role list doUpdateRoleListOfUser(userName, rolesOfUser, new String[] {}); } // delete user entry if it exist if (userResult != null && userResult.getAttributes().get(userNameAttribute).get().toString() .toLowerCase().equals(userName.toLowerCase())) { if (log.isDebugEnabled()) { log.debug("Deleting " + userDN + " with search base " + userSearchBase); } subDirContext = (DirContext) mainDirContext.lookup(userSearchBase); subDirContext.destroySubcontext(userDN); } userCache.remove(userName); } catch (NamingException e) { String errorMessage = "Error occurred while deleting the user : " + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(groupResults); JNDIUtil.closeNamingEnumeration(userResults); JNDIUtil.closeContext(subDirContext); JNDIUtil.closeContext(mainDirContext); } } @SuppressWarnings("rawtypes") @Override public void doUpdateCredential(String userName, Object newCredential, Object oldCredential) throws UserStoreException { DirContext dirContext = this.connectionSource.getContext(); DirContext subDirContext = null; // first search the existing user entry. String searchBase = realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); String searchFilter = realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_SEARCH_FILTER); searchFilter = searchFilter.replace("?", escapeSpecialCharactersForFilter(userName)); SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setReturningAttributes(new String[] { "userPassword" }); NamingEnumeration<SearchResult> namingEnumeration = null; NamingEnumeration passwords = null; try { namingEnumeration = dirContext.search(escapeDNForSearch(searchBase), searchFilter, searchControls); // here we assume only one user // TODO: what to do if there are more than one user SearchResult searchResult = null; String passwordHashMethod = realmConfig.getUserStoreProperty(PASSWORD_HASH_METHOD); while (namingEnumeration.hasMore()) { searchResult = namingEnumeration.next(); String dnName = searchResult.getName(); subDirContext = (DirContext) dirContext.lookup(searchBase); Attribute passwordAttribute = new BasicAttribute("userPassword"); passwordAttribute.add( UserCoreUtil.getPasswordToStore((String) newCredential, passwordHashMethod, kdcEnabled)); BasicAttributes basicAttributes = new BasicAttributes(true); basicAttributes.put(passwordAttribute); subDirContext.modifyAttributes(dnName, DirContext.REPLACE_ATTRIBUTE, basicAttributes); } // we check whether both carbon admin entry and ldap connection // entry are the same if (searchResult.getNameInNamespace() .equals(realmConfig.getUserStoreProperty(LDAPConstants.CONNECTION_NAME))) { this.connectionSource.updateCredential((String) newCredential); } } catch (NamingException e) { String errorMessage = "Can not access the directory service for user : " + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(passwords); JNDIUtil.closeNamingEnumeration(namingEnumeration); JNDIUtil.closeContext(subDirContext); JNDIUtil.closeContext(dirContext); } } @Override public void doUpdateCredentialByAdmin(String userName, Object newCredential) throws UserStoreException { DirContext dirContext = this.connectionSource.getContext(); DirContext subDirContext = null; // first search the existing user entry. String searchBase = realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); String searchFilter = realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_SEARCH_FILTER); searchFilter = searchFilter.replace("?", escapeSpecialCharactersForFilter(userName)); SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setReturningAttributes(new String[] { "userPassword" }); NamingEnumeration<SearchResult> namingEnumeration = null; NamingEnumeration passwords = null; try { namingEnumeration = dirContext.search(escapeDNForSearch(searchBase), searchFilter, searchControls); // here we assume only one user // TODO: what to do if there are more than one user // there can be only only on user SearchResult searchResult = null; while (namingEnumeration.hasMore()) { searchResult = namingEnumeration.next(); String passwordHashMethod = realmConfig.getUserStoreProperty(PASSWORD_HASH_METHOD); if (!UserCoreConstants.RealmConfig.PASSWORD_HASH_METHOD_PLAIN_TEXT .equalsIgnoreCase(passwordHashMethod)) { Attributes attributes = searchResult.getAttributes(); Attribute userPassword = attributes.get("userPassword"); // When admin changes other user passwords he do not have to // provide the old password. Here it is only possible to have one password, if there // are more every one should match with the given old password passwords = userPassword.getAll(); if (passwords.hasMore()) { byte[] byteArray = (byte[]) passwords.next(); String password = new String(byteArray); if (password.startsWith("{")) { passwordHashMethod = password.substring(password.indexOf('{') + 1, password.indexOf('}')); } } } String dnName = searchResult.getName(); subDirContext = (DirContext) dirContext.lookup(searchBase); Attribute passwordAttribute = new BasicAttribute("userPassword"); passwordAttribute.add( UserCoreUtil.getPasswordToStore((String) newCredential, passwordHashMethod, kdcEnabled)); BasicAttributes basicAttributes = new BasicAttributes(true); basicAttributes.put(passwordAttribute); subDirContext.modifyAttributes(dnName, DirContext.REPLACE_ATTRIBUTE, basicAttributes); } // we check whether both carbon admin entry and ldap connection // entry are the same if (searchResult.getNameInNamespace() .equals(realmConfig.getUserStoreProperty(LDAPConstants.CONNECTION_NAME))) { this.connectionSource.updateCredential((String) newCredential); } } catch (NamingException e) { String errorMessage = "Can not access the directory service for user : " + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(passwords); JNDIUtil.closeNamingEnumeration(namingEnumeration); JNDIUtil.closeContext(subDirContext); JNDIUtil.closeContext(dirContext); } } @Override public Map<String, String> getProperties(Tenant tenant) throws UserStoreException { Map<String, String> existingProperties = this.realmConfig.getUserStoreProperties(); String tenantSufix = getTenantSuffix(tenant.getDomain()); String propertyName = null; Map<String, String> newProperties = new HashMap<String, String>(); for (Map.Entry<String, String> iter : existingProperties.entrySet()) { propertyName = iter.getKey(); if (propertyName.equals(LDAPConstants.USER_SEARCH_BASE)) { newProperties.put(propertyName, tenantSufix); } else { newProperties.put(propertyName, iter.getValue()); } } return newProperties; } /** * @param domain * @return */ private String getTenantSuffix(String domain) { // here we use a simple algorithum by splitting the domain with . String[] domainParts = domain.split("\\."); StringBuffer suffixName = new StringBuffer(); for (String domainPart : domainParts) { suffixName.append(",dc=").append(domainPart); } return suffixName.toString().replaceFirst(",", ""); } /** * This method overwrites the method in LDAPUserStoreManager. This implements the functionality * of updating user's profile information in LDAP user store. * * @param userName * @param claims * @param profileName * @throws UserStoreException */ @Override public void doSetUserClaimValues(String userName, Map<String, String> claims, String profileName) throws UserStoreException { // get the LDAP Directory context DirContext dirContext = this.connectionSource.getContext(); DirContext subDirContext = null; // search the relevant user entry by user name String userSearchBase = realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); String userSearchFilter = realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_SEARCH_FILTER); // if user name contains domain name, remove domain name String[] userNames = userName.split(CarbonConstants.DOMAIN_SEPARATOR); if (userNames.length > 1) { userName = userNames[1]; } userSearchFilter = userSearchFilter.replace("?", escapeSpecialCharactersForFilter(userName)); SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setReturningAttributes(null); NamingEnumeration<SearchResult> returnedResultList = null; String returnedUserEntry = null; try { returnedResultList = dirContext.search(escapeDNForSearch(userSearchBase), userSearchFilter, searchControls); // assume only one user is returned from the search // TODO:what if more than one user is returned if (returnedResultList.hasMore()) { returnedUserEntry = returnedResultList.next().getName(); } } catch (NamingException e) { String errorMessage = "Results could not be retrieved from the directory context for user : " + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(returnedResultList); } if (profileName == null) { profileName = UserCoreConstants.DEFAULT_PROFILE; } if (claims.get(UserCoreConstants.PROFILE_CONFIGURATION) == null) { claims.put(UserCoreConstants.PROFILE_CONFIGURATION, UserCoreConstants.DEFAULT_PROFILE_CONFIGURATION); } try { Attributes updatedAttributes = new BasicAttributes(true); for (Map.Entry<String, String> claimEntry : claims.entrySet()) { String claimURI = claimEntry.getKey(); // if there is no attribute for profile configuration in LDAP, // skip updating it. if (claimURI.equals(UserCoreConstants.PROFILE_CONFIGURATION)) { continue; } // get the claimMapping related to this claimURI String attributeName = getClaimAtrribute(claimURI, userName, null); //remove user DN from cache if changing username attribute if (realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_ATTRIBUTE).equals(attributeName)) { userCache.remove(userName); } // if uid attribute value contains domain name, remove domain // name if (attributeName.equals("uid")) { // if user name contains domain name, remove domain name String uidName = claimEntry.getValue(); String[] uidNames = uidName.split(CarbonConstants.DOMAIN_SEPARATOR); if (uidNames.length > 1) { uidName = uidNames[1]; claimEntry.setValue(uidName); } // claimEntry.setValue(escapeISSpecialCharacters(uidName)); } Attribute currentUpdatedAttribute = new BasicAttribute(attributeName); /* if updated attribute value is null, remove its values. */ if (EMPTY_ATTRIBUTE_STRING.equals(claimEntry.getValue())) { currentUpdatedAttribute.clear(); } else { String userAttributeSeparator = ","; if (claimEntry.getValue() != null && !attributeName.equals("uid") && !attributeName.equals("sn")) { String claimSeparator = realmConfig.getUserStoreProperty(MULTI_ATTRIBUTE_SEPARATOR); if (claimSeparator != null && !claimSeparator.trim().isEmpty()) { userAttributeSeparator = claimSeparator; } if (claimEntry.getValue().contains(userAttributeSeparator)) { StringTokenizer st = new StringTokenizer(claimEntry.getValue(), userAttributeSeparator); while (st.hasMoreElements()) { String newVal = st.nextElement().toString(); if (newVal != null && newVal.trim().length() > 0) { currentUpdatedAttribute.add(newVal.trim()); } } } else { currentUpdatedAttribute.add(claimEntry.getValue()); } } else { currentUpdatedAttribute.add(claimEntry.getValue()); } } updatedAttributes.put(currentUpdatedAttribute); } // update the attributes in the relevant entry of the directory // store subDirContext = (DirContext) dirContext.lookup(userSearchBase); subDirContext.modifyAttributes(returnedUserEntry, DirContext.REPLACE_ATTRIBUTE, updatedAttributes); } catch (Exception e) { handleException(e, userName); } finally { JNDIUtil.closeContext(subDirContext); JNDIUtil.closeContext(dirContext); } } @Override public void doSetUserClaimValue(String userName, String claimURI, String value, String profileName) throws UserStoreException { // get the LDAP Directory context DirContext dirContext = this.connectionSource.getContext(); DirContext subDirContext = null; // search the relevant user entry by user name String userSearchBase = realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); String userSearchFilter = realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_SEARCH_FILTER); // if user name contains domain name, remove domain name String[] userNames = userName.split(CarbonConstants.DOMAIN_SEPARATOR); if (userNames.length > 1) { userName = userNames[1]; } userSearchFilter = userSearchFilter.replace("?", escapeSpecialCharactersForFilter(userName)); SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setReturningAttributes(null); NamingEnumeration<SearchResult> returnedResultList = null; String returnedUserEntry = null; try { returnedResultList = dirContext.search(escapeDNForSearch(userSearchBase), userSearchFilter, searchControls); // assume only one user is returned from the search // TODO:what if more than one user is returned if (returnedResultList.hasMore()) { returnedUserEntry = returnedResultList.next().getName(); } } catch (NamingException e) { String errorMessage = "Results could not be retrieved from the directory context for user : " + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(returnedResultList); } try { Attributes updatedAttributes = new BasicAttributes(true); // if there is no attribute for profile configuration in LDAP, skip // updating it. // get the claimMapping related to this claimURI String attributeName = null; attributeName = getClaimAtrribute(claimURI, userName, null); Attribute currentUpdatedAttribute = new BasicAttribute(attributeName); /* if updated attribute value is null, remove its values. */ if (EMPTY_ATTRIBUTE_STRING.equals(value)) { currentUpdatedAttribute.clear(); } else { if (attributeName.equals("uid") || attributeName.equals("sn")) { currentUpdatedAttribute.add(value); } else { String userAttributeSeparator = ","; String claimSeparator = realmConfig.getUserStoreProperty(MULTI_ATTRIBUTE_SEPARATOR); if (claimSeparator != null && !claimSeparator.trim().isEmpty()) { userAttributeSeparator = claimSeparator; } if (value.contains(userAttributeSeparator)) { StringTokenizer st = new StringTokenizer(value, userAttributeSeparator); while (st.hasMoreElements()) { String newVal = st.nextElement().toString(); if (newVal != null && newVal.trim().length() > 0) { currentUpdatedAttribute.add(newVal.trim()); } } } else { currentUpdatedAttribute.add(value); } } } updatedAttributes.put(currentUpdatedAttribute); // update the attributes in the relevant entry of the directory // store subDirContext = (DirContext) dirContext.lookup(userSearchBase); subDirContext.modifyAttributes(returnedUserEntry, DirContext.REPLACE_ATTRIBUTE, updatedAttributes); } catch (Exception e) { handleException(e, userName); } finally { JNDIUtil.closeContext(subDirContext); JNDIUtil.closeContext(dirContext); } } @Override public void doDeleteUserClaimValue(String userName, String claimURI, String profileName) throws UserStoreException { // get the LDAP Directory context DirContext dirContext = this.connectionSource.getContext(); DirContext subDirContext = null; // search the relevant user entry by user name String userSearchBase = realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); String userSearchFilter = realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_SEARCH_FILTER); userSearchFilter = userSearchFilter.replace("?", escapeSpecialCharactersForFilter(userName)); SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setReturningAttributes(null); NamingEnumeration<SearchResult> returnedResultList = null; String returnedUserEntry = null; try { returnedResultList = dirContext.search(escapeDNForSearch(userSearchBase), userSearchFilter, searchControls); // assume only one user is returned from the search // TODO:what if more than one user is returned if (returnedResultList.hasMore()) { returnedUserEntry = returnedResultList.next().getName(); } } catch (NamingException e) { String errorMessage = "Results could not be retrieved from the directory context for user : " + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(returnedResultList); } try { Attributes updatedAttributes = new BasicAttributes(true); // if there is no attribute for profile configuration in LDAP, skip // updating it. // get the claimMapping related to this claimURI String attributeName = null; attributeName = getClaimAtrribute(claimURI, userName, null); Attribute currentUpdatedAttribute = new BasicAttribute(attributeName); updatedAttributes.put(currentUpdatedAttribute); subDirContext = (DirContext) dirContext.lookup(userSearchBase); subDirContext.modifyAttributes(returnedUserEntry, DirContext.REMOVE_ATTRIBUTE, updatedAttributes); } catch (Exception e) { handleException(e, userName); } finally { JNDIUtil.closeContext(subDirContext); JNDIUtil.closeContext(dirContext); } } @Override public void doDeleteUserClaimValues(String userName, String[] claims, String profileName) throws UserStoreException { // get the LDAP Directory context DirContext dirContext = this.connectionSource.getContext(); DirContext subDirContext = null; // search the relevant user entry by user name String userSearchBase = realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); String userSearchFilter = realmConfig.getUserStoreProperty(LDAPConstants.USER_NAME_SEARCH_FILTER); userSearchFilter = userSearchFilter.replace("?", escapeSpecialCharactersForFilter(userName)); SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); searchControls.setReturningAttributes(null); NamingEnumeration<SearchResult> returnedResultList = null; String returnedUserEntry = null; try { returnedResultList = dirContext.search(escapeDNForSearch(userSearchBase), userSearchFilter, searchControls); // assume only one user is returned from the search // TODO:what if more than one user is returned if (returnedResultList.hasMore()) { returnedUserEntry = returnedResultList.next().getName(); } } catch (NamingException e) { String errorMessage = "Results could not be retrieved from the directory context for user : " + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(returnedResultList); } try { Attributes updatedAttributes = new BasicAttributes(true); // if there is no attribute for profile configuration in LDAP, skip // updating it. // get the claimMapping related to this claimURI for (String claimURI : claims) { String attributeName = getClaimAtrribute(claimURI, userName, null); Attribute currentUpdatedAttribute = new BasicAttribute(attributeName); updatedAttributes.put(currentUpdatedAttribute); } subDirContext = (DirContext) dirContext.lookup(userSearchBase); subDirContext.modifyAttributes(returnedUserEntry, DirContext.REMOVE_ATTRIBUTE, updatedAttributes); } catch (Exception e) { handleException(e, userName); } finally { JNDIUtil.closeContext(subDirContext); JNDIUtil.closeContext(dirContext); } } /** * Add roles by writing groups to LDAP. * * @param roleName * @param userList * @throws UserStoreException */ @Override public void doAddRole(String roleName, String[] userList, boolean shared) throws UserStoreException { RoleContext roleContext; roleContext = createRoleContext(roleName); roleContext.setMembers(userList); addLDAPRole(roleContext); if (shared && isSharedGroupEnabled()) { String tenantDomain = CarbonContext.getThreadLocalCarbonContext().getTenantDomain(); roleName = roleName + UserCoreConstants.TENANT_DOMAIN_COMBINER + tenantDomain; roleContext = createRoleContext(roleName); addLDAPRole(roleContext); } } protected void addLDAPRole(RoleContext context) throws UserStoreException { String roleName = context.getRoleName(); String[] userList = context.getMembers(); String groupEntryObjectClass = ((LDAPRoleContext) context).getGroupEntryObjectClass(); String groupNameAttribute = ((LDAPRoleContext) context).getRoleNameProperty(); String searchBase = ((LDAPRoleContext) context).getSearchBase(); if ((userList == null || userList.length == 0) && !emptyRolesAllowed) { String errorMessage = "Can not create empty role. There should be at least " + "one user for the role."; throw new UserStoreException(errorMessage); } else if (userList == null && emptyRolesAllowed || userList != null && userList.length > 0 && !emptyRolesAllowed || emptyRolesAllowed) { // if (userList.length > 0) { DirContext mainDirContext = this.connectionSource.getContext(); DirContext groupContext = null; NamingEnumeration<SearchResult> results = null; try { // create the attribute set for group entry Attributes groupAttributes = new BasicAttributes(true); // create group entry's object class attribute Attribute objectClassAttribute = new BasicAttribute(LDAPConstants.OBJECT_CLASS_NAME); objectClassAttribute.add(groupEntryObjectClass); groupAttributes.put(objectClassAttribute); // create cn attribute Attribute cnAttribute = new BasicAttribute(groupNameAttribute); cnAttribute.add(roleName); groupAttributes.put(cnAttribute); // following check is for if emptyRolesAllowed made this // code executed. if (userList != null && userList.length > 0) { String memberAttributeName = realmConfig .getUserStoreProperty(LDAPConstants.MEMBERSHIP_ATTRIBUTE); Attribute memberAttribute = new BasicAttribute(memberAttributeName); for (String userName : userList) { if (userName == null || userName.trim().length() == 0) { continue; } // search the user in user search base String searchFilter = realmConfig .getUserStoreProperty(LDAPConstants.USER_NAME_SEARCH_FILTER); searchFilter = searchFilter.replace("?", escapeSpecialCharactersForFilter(userName)); results = searchInUserBase(searchFilter, new String[] {}, SearchControls.SUBTREE_SCOPE, mainDirContext); // we assume only one user with the given user // name under user search base. SearchResult userResult = null; if (results.hasMore()) { userResult = results.next(); } else { String errorMsg = "There is no user with the user name: " + userName + " to be added to this role."; logger.error(errorMsg); throw new UserStoreException(errorMsg); } // get his DN String userEntryDN = userResult.getNameInNamespace(); // put it as member-attribute value memberAttribute.add(userEntryDN); } groupAttributes.put(memberAttribute); } groupContext = (DirContext) mainDirContext.lookup(searchBase); NameParser ldapParser = groupContext.getNameParser(""); /* * Name compoundGroupName = ldapParser.parse(groupNameAttributeName + "=" + * roleName); */ Name compoundGroupName = ldapParser.parse("cn=" + roleName); groupContext.bind(compoundGroupName, null, groupAttributes); } catch (NamingException e) { String errorMsg = "Role: " + roleName + " could not be added."; if (log.isDebugEnabled()) { log.debug(errorMsg, e); } throw new UserStoreException(errorMsg, e); } catch (Exception e) { String errorMsg = "Role: " + roleName + " could not be added."; if (log.isDebugEnabled()) { log.debug(errorMsg, e); } throw new UserStoreException(errorMsg, e); } finally { JNDIUtil.closeNamingEnumeration(results); JNDIUtil.closeContext(groupContext); JNDIUtil.closeContext(mainDirContext); } } } /** * Update role list of user by writing to LDAP. * * @param userName * @param deletedRoles * @param newRoles * @throws UserStoreException */ @SuppressWarnings("deprecation") @Override public void doUpdateRoleListOfUser(String userName, String[] deletedRoles, String[] newRoles) throws UserStoreException { // get the DN of the user entry String userNameDN = this.getNameInSpaceForUserName(userName); String membershipAttribute = realmConfig.getUserStoreProperty(LDAPConstants.MEMBERSHIP_ATTRIBUTE); /* * check deleted roles and delete member entries from relevant groups. */ String errorMessage = null; String roleSearchFilter = null; DirContext mainDirContext = this.connectionSource.getContext(); try { if (deletedRoles != null && deletedRoles.length != 0) { // perform validation for empty role occurrences before // updating in LDAP // check whether this is shared roles and where shared roles are // enable for (String deletedRole : deletedRoles) { LDAPRoleContext context = (LDAPRoleContext) createRoleContext(deletedRole); deletedRole = context.getRoleName(); String searchFilter = context.getSearchFilter(); roleSearchFilter = searchFilter.replace("?", escapeSpecialCharactersForFilter(deletedRole)); String[] returningAttributes = new String[] { membershipAttribute }; String searchBase = context.getSearchBase(); NamingEnumeration<SearchResult> groupResults = searchInGroupBase(roleSearchFilter, returningAttributes, SearchControls.SUBTREE_SCOPE, mainDirContext, searchBase); SearchResult resultedGroup = null; if (groupResults.hasMore()) { resultedGroup = groupResults.next(); } if (resultedGroup != null && isOnlyUserInRole(userNameDN, resultedGroup) && !emptyRolesAllowed) { errorMessage = userName + " is the only user in the role: " + deletedRole + ". Hence can not delete user from role."; throw new UserStoreException(errorMessage); } JNDIUtil.closeNamingEnumeration(groupResults); } // if empty role violation does not happen, continue // updating the LDAP. for (String deletedRole : deletedRoles) { LDAPRoleContext context = (LDAPRoleContext) createRoleContext(deletedRole); deletedRole = context.getRoleName(); String searchFilter = context.getSearchFilter(); if (isExistingRole(deletedRole)) { roleSearchFilter = searchFilter.replace("?", escapeSpecialCharactersForFilter(deletedRole)); String[] returningAttributes = new String[] { membershipAttribute }; String searchBase = context.getSearchBase(); NamingEnumeration<SearchResult> groupResults = searchInGroupBase(roleSearchFilter, returningAttributes, SearchControls.SUBTREE_SCOPE, mainDirContext, searchBase); SearchResult resultedGroup = null; String groupDN = null; if (groupResults.hasMore()) { resultedGroup = groupResults.next(); groupDN = resultedGroup.getName(); } this.modifyUserInRole(userNameDN, groupDN, DirContext.REMOVE_ATTRIBUTE, searchBase); JNDIUtil.closeNamingEnumeration(groupResults); // need to update authz cache of user since roles // are deleted userRealm.getAuthorizationManager().clearUserAuthorization(userName); } else { errorMessage = "The role: " + deletedRole + " does not exist."; throw new UserStoreException(errorMessage); } } } if (newRoles != null && newRoles.length != 0) { for (String newRole : newRoles) { LDAPRoleContext context = (LDAPRoleContext) createRoleContext(newRole); newRole = context.getRoleName(); String searchFilter = context.getSearchFilter(); if (isExistingRole(newRole)) { roleSearchFilter = searchFilter.replace("?", escapeSpecialCharactersForFilter(newRole)); String[] returningAttributes = new String[] { membershipAttribute }; String searchBase = context.getSearchBase(); NamingEnumeration<SearchResult> groupResults = searchInGroupBase(roleSearchFilter, returningAttributes, SearchControls.SUBTREE_SCOPE, mainDirContext, searchBase); SearchResult resultedGroup = null; // assume only one group with given group name String groupDN = null; if (groupResults.hasMore()) { resultedGroup = groupResults.next(); groupDN = resultedGroup.getName(); } if (resultedGroup != null && !isUserInRole(userNameDN, resultedGroup)) { modifyUserInRole(userNameDN, groupDN, DirContext.ADD_ATTRIBUTE, searchBase); } else { errorMessage = "User: " + userName + " already belongs to role: " + groupDN; throw new UserStoreException(errorMessage); } JNDIUtil.closeNamingEnumeration(groupResults); } else { errorMessage = "The role: " + newRole + " does not exist."; throw new UserStoreException(errorMessage); } } } } catch (NamingException e) { errorMessage = "Error occurred while modifying the role list of user: " + userName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeContext(mainDirContext); } } /** * Update the set of users belong to a LDAP role. * * @param roleName * @param deletedUsers * @param newUsers */ @SuppressWarnings("deprecation") @Override public void doUpdateUserListOfRole(String roleName, String[] deletedUsers, String[] newUsers) throws UserStoreException { String errorMessage = null; NamingEnumeration<SearchResult> groupSearchResults = null; LDAPRoleContext ctx = (LDAPRoleContext) createRoleContext(roleName); roleName = ctx.getRoleName(); String searchFilter = ctx.getSearchFilter(); if (isExistingLDAPRole(ctx)) { DirContext mainDirContext = this.connectionSource.getContext(); try { searchFilter = searchFilter.replace("?", escapeSpecialCharactersForFilter(roleName)); String membershipAttributeName = realmConfig .getUserStoreProperty(LDAPConstants.MEMBERSHIP_ATTRIBUTE); String[] returningAttributes = new String[] { membershipAttributeName }; String searchBase = ctx.getSearchBase(); groupSearchResults = searchInGroupBase(searchFilter, returningAttributes, SearchControls.SUBTREE_SCOPE, mainDirContext, searchBase); SearchResult resultedGroup = null; String groupName = null; while (groupSearchResults.hasMoreElements()) { resultedGroup = groupSearchResults.next(); groupName = resultedGroup.getName(); } // check whether update operations are going to violate non // empty role // restriction specified in user-mgt.xml by // checking whether all users are trying to be deleted // before updating LDAP. Attribute returnedMemberAttribute = resultedGroup.getAttributes().get(membershipAttributeName); if (!emptyRolesAllowed && newUsers.length - deletedUsers.length + returnedMemberAttribute.size() == 0) { errorMessage = "There should be at least one member in the role. " + "Hence can not delete all the members."; throw new UserStoreException(errorMessage); } else { List<String> newUserList = new ArrayList<String>(); List<String> deleteUserList = new ArrayList<String>(); if (newUsers != null && newUsers.length != 0) { String invalidUserList = ""; String existingUserList = ""; for (String newUser : newUsers) { if (StringUtils.isEmpty(newUser)) { continue; } String userNameDN = getNameInSpaceForUserName(newUser); if (userNameDN == null) { invalidUserList += newUser + " "; } else if (isUserInRole(userNameDN, resultedGroup)) { existingUserList += userNameDN + ","; } else { newUserList.add(userNameDN); } } if (!StringUtils.isEmpty(invalidUserList) || !StringUtils.isEmpty(existingUserList)) { errorMessage = (StringUtils.isEmpty(invalidUserList) ? "" : "'" + invalidUserList + "' not in the user store. ") + (StringUtils.isEmpty(existingUserList) ? "" : "'" + existingUserList + "' already belong to the role : " + roleName); throw new UserStoreException(errorMessage); } } if (deletedUsers != null && deletedUsers.length != 0) { String invalidUserList = ""; for (String deletedUser : deletedUsers) { if (StringUtils.isEmpty(deletedUser)) { continue; } String userNameDN = getNameInSpaceForUserName(deletedUser); if (userNameDN == null) { invalidUserList += deletedUser + ","; } else { deleteUserList.add(userNameDN); } } if (!StringUtils.isEmpty(invalidUserList)) { errorMessage = "'" + invalidUserList + "' not in the user store."; throw new UserStoreException(errorMessage); } } for (String userNameDN : newUserList) { modifyUserInRole(userNameDN, groupName, DirContext.ADD_ATTRIBUTE, searchBase); } for (String userNameDN : deleteUserList) { modifyUserInRole(userNameDN, groupName, DirContext.REMOVE_ATTRIBUTE, searchBase); // needs to clear authz cache for deleted users userRealm.getAuthorizationManager().clearUserAuthorization(userNameDN); } } } catch (NamingException e) { errorMessage = "Error occurred while modifying the user list of role: " + roleName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(groupSearchResults); JNDIUtil.closeContext(mainDirContext); } } else { errorMessage = "The role: " + roleName + " does not exist."; if (log.isDebugEnabled()) { log.debug(errorMessage); } throw new UserStoreException(errorMessage); } } /** * Either delete or add user from/to group. * * @param userNameDN : distinguish name of user entry. * @param groupRDN : relative distinguish name of group entry * @param modifyType : modify attribute type in DirCOntext. * @throws UserStoreException */ protected void modifyUserInRole(String userNameDN, String groupRDN, int modifyType, String searchBase) throws UserStoreException { if (log.isDebugEnabled()) { logger.debug("Modifying role: " + groupRDN + " with type: " + modifyType + " user: " + userNameDN + " in search base: " + searchBase); } DirContext mainDirContext = null; DirContext groupContext = null; try { mainDirContext = this.connectionSource.getContext(); groupContext = (DirContext) mainDirContext.lookup(searchBase); String memberAttributeName = realmConfig.getUserStoreProperty(LDAPConstants.MEMBERSHIP_ATTRIBUTE); Attributes modifyingAttributes = new BasicAttributes(true); Attribute memberAttribute = new BasicAttribute(memberAttributeName); memberAttribute.add(userNameDN); modifyingAttributes.put(memberAttribute); groupContext.modifyAttributes(groupRDN, modifyType, modifyingAttributes); if (log.isDebugEnabled()) { logger.debug("User: " + userNameDN + " was successfully " + "modified in LDAP group: " + groupRDN); } } catch (NamingException e) { String errorMessage = "Error occurred while modifying user entry: " + userNameDN + " in LDAP role: " + groupRDN; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage); } finally { JNDIUtil.closeContext(groupContext); JNDIUtil.closeContext(mainDirContext); } } /** * Check whether user is in the group by searching through its member attributes. * * @param userDN * @param groupEntry * @return * @throws UserStoreException */ protected boolean isUserInRole(String userDN, SearchResult groupEntry) throws UserStoreException { boolean isUserInRole = false; try { Attributes groupAttributes = groupEntry.getAttributes(); if (groupAttributes != null) { // get group's returned attributes NamingEnumeration attributes = groupAttributes.getAll(); // loop through attributes while (attributes.hasMoreElements()) { Attribute memberAttribute = (Attribute) attributes.next(); String memberAttributeName = realmConfig .getUserStoreProperty(LDAPConstants.MEMBERSHIP_ATTRIBUTE); if (memberAttributeName.equalsIgnoreCase(memberAttribute.getID())) { // loop through attribute values for (int i = 0; i < memberAttribute.size(); i++) { if (userDN.equalsIgnoreCase((String) memberAttribute.get(i))) { return true; } } } } attributes.close(); } } catch (NamingException e) { String errorMessage = "Error occurred while looping through attributes set of group: " + groupEntry.getNameInNamespace(); if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } return isUserInRole; } /** * Check whether this is the last/only user in this group. * * @param userDN * @param groupEntry * @return groupContext */ @SuppressWarnings("rawtypes") protected boolean isOnlyUserInRole(String userDN, SearchResult groupEntry) throws UserStoreException { boolean isOnlyUserInRole = false; try { Attributes groupAttributes = groupEntry.getAttributes(); if (groupAttributes != null) { NamingEnumeration attributes = groupAttributes.getAll(); while (attributes.hasMoreElements()) { Attribute memberAttribute = (Attribute) attributes.next(); String memberAttributeName = realmConfig .getUserStoreProperty(LDAPConstants.MEMBERSHIP_ATTRIBUTE); String attributeID = memberAttribute.getID(); if (memberAttributeName.equals(attributeID)) { if (memberAttribute.size() == 1 && userDN.equals(memberAttribute.get())) { return true; } } } attributes.close(); } } catch (NamingException e) { String errorMessage = "Error occurred while looping through attributes set of group: " + groupEntry.getNameInNamespace(); if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } return isOnlyUserInRole; } protected void updateLDAPRoleName(RoleContext context, String newRoleName) throws UserStoreException { String roleName = context.getRoleName(); String groupSearchFilter = ((LDAPRoleContext) context).getSearchFilter(); String roleNameAttributeName = ((LDAPRoleContext) context).getRoleNameProperty(); String searchBase = ((LDAPRoleContext) context).getSearchBase(); DirContext mainContext = this.connectionSource.getContext(); DirContext groupContext = null; NamingEnumeration<SearchResult> groupSearchResults = null; try { groupSearchFilter = groupSearchFilter.replace("?", escapeSpecialCharactersForFilter(roleName)); String[] returningAttributes = { roleNameAttributeName }; groupSearchResults = searchInGroupBase(groupSearchFilter, returningAttributes, SearchControls.SUBTREE_SCOPE, mainContext, searchBase); SearchResult resultedGroup = null; while (groupSearchResults.hasMoreElements()) { resultedGroup = groupSearchResults.next(); } if (resultedGroup == null) { throw new UserStoreException("Could not find user role " + roleName + " in LDAP server."); } String groupNameRDN = resultedGroup.getName(); String newGroupNameRDN = roleNameAttributeName + "=" + newRoleName; groupContext = (DirContext) mainContext.lookup(groupSearchBase); groupContext.rename(groupNameRDN, newGroupNameRDN); String roleNameWithDomain = UserCoreUtil.addDomainToName(roleName, getMyDomainName()); String newRoleNameWithDomain = UserCoreUtil.addDomainToName(newRoleName, getMyDomainName()); this.userRealm.getAuthorizationManager().resetPermissionOnUpdateRole(roleNameWithDomain, newRoleNameWithDomain); } catch (NamingException e) { String errorMessage = "Error occurred while modifying the name of role: " + roleName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(groupSearchResults); JNDIUtil.closeContext(groupContext); JNDIUtil.closeContext(mainContext); } } @Override public void doUpdateRoleName(String roleName, String newRoleName) throws UserStoreException { RoleContext roleContext = createRoleContext(roleName); updateLDAPRoleName(roleContext, newRoleName); if (roleContext.isShared()) { roleName = roleName + UserCoreConstants.TENANT_DOMAIN_COMBINER + CarbonContext.getThreadLocalCarbonContext().getTenantDomain(); roleContext = createRoleContext(roleName); updateLDAPRoleName(roleContext, newRoleName); } } protected void deleteLDAPRole(RoleContext context) throws UserStoreException { String roleName = context.getRoleName(); String groupSearchFilter = ((LDAPRoleContext) context).getSearchFilter(); groupSearchFilter = groupSearchFilter.replace("?", escapeSpecialCharactersForFilter(context.getRoleName())); String[] returningAttributes = { ((LDAPRoleContext) context).getRoleNameProperty() }; String searchBase = ((LDAPRoleContext) context).getSearchBase(); DirContext mainDirContext = null; DirContext groupContext = null; NamingEnumeration<SearchResult> groupSearchResults = null; try { mainDirContext = this.connectionSource.getContext(); groupSearchResults = searchInGroupBase(groupSearchFilter, returningAttributes, SearchControls.SUBTREE_SCOPE, mainDirContext, searchBase); SearchResult resultedGroup = null; while (groupSearchResults.hasMoreElements()) { resultedGroup = groupSearchResults.next(); } if (resultedGroup == null) { throw new UserStoreException("Could not find specified group/role - " + roleName); } String groupName = resultedGroup.getName(); groupContext = (DirContext) mainDirContext.lookup(groupSearchBase); String groupNameAttributeValue = (String) resultedGroup.getAttributes() .get(realmConfig.getUserStoreProperty(LDAPConstants.GROUP_NAME_ATTRIBUTE)).get(); if (groupNameAttributeValue.equals(roleName)) { groupContext.destroySubcontext(groupName); } } catch (NamingException e) { String errorMessage = "Error occurred while deleting the role: " + roleName; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } finally { JNDIUtil.closeNamingEnumeration(groupSearchResults); JNDIUtil.closeContext(groupContext); JNDIUtil.closeContext(mainDirContext); } } /** * Delete LDAP group corresponding to the role. * * @param roleName The role to delete. * @throws UserStoreException In case if an error occurred while deleting a role. */ @Override public void doDeleteRole(String roleName) throws UserStoreException { RoleContext roleContext = createRoleContext(roleName); deleteLDAPRole(roleContext); if (roleContext.isShared()) { roleName = roleName + UserCoreConstants.TENANT_DOMAIN_COMBINER + CarbonContext.getThreadLocalCarbonContext().getTenantDomain(); roleContext = createRoleContext(roleName); deleteLDAPRole(roleContext); } } /** * Reused methods to search users with various filters * * @param searchFilter * @param returningAttributes * @param searchScope * @return */ private NamingEnumeration<SearchResult> searchInUserBase(String searchFilter, String[] returningAttributes, int searchScope, DirContext rootContext) throws UserStoreException { if (log.isDebugEnabled()) { log.debug("Searching user with " + searchFilter); } String userBase = realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); SearchControls userSearchControl = new SearchControls(); userSearchControl.setReturningAttributes(returningAttributes); userSearchControl.setSearchScope(searchScope); NamingEnumeration<SearchResult> userSearchResults = null; try { userSearchResults = rootContext.search(escapeDNForSearch(userBase), searchFilter, userSearchControl); } catch (NamingException e) { String errorMessage = "Error occurred while searching in user base."; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } return userSearchResults; } /** * Reused method to search groups with various filters. * * @param searchFilter * @param returningAttributes * @param searchScope * @return */ protected NamingEnumeration<SearchResult> searchInGroupBase(String searchFilter, String[] returningAttributes, int searchScope, DirContext rootContext, String searchBase) throws UserStoreException { SearchControls userSearchControl = new SearchControls(); userSearchControl.setReturningAttributes(returningAttributes); userSearchControl.setSearchScope(searchScope); NamingEnumeration<SearchResult> groupSearchResults = null; try { groupSearchResults = rootContext.search(escapeDNForSearch(searchBase), searchFilter, userSearchControl); } catch (NamingException e) { String errorMessage = "Error occurred while searching in group base."; if (log.isDebugEnabled()) { log.debug(errorMessage, e); } throw new UserStoreException(errorMessage, e); } return groupSearchResults; } /** * This is to read and validate the required user store configuration for this user store * manager to take decisions. * * @throws UserStoreException */ @Override protected void checkRequiredUserStoreConfigurations() throws UserStoreException { super.checkRequiredUserStoreConfigurations(); String userObjectClass = realmConfig.getUserStoreProperty(LDAPConstants.USER_ENTRY_OBJECT_CLASS); if (userObjectClass == null || userObjectClass.equals("")) { throw new UserStoreException( "Required UserEntryObjectClass property is not set at the LDAP configurations"); } if (realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.WRITE_GROUPS_ENABLED) != null) { writeGroupsEnabled = Boolean.parseBoolean( realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.WRITE_GROUPS_ENABLED)); } if (log.isDebugEnabled()) { if (writeGroupsEnabled) { log.debug("WriteGroups is enabled for " + getMyDomainName()); } else { log.debug("WriteGroups is disabled for " + getMyDomainName()); } } if (!writeGroupsEnabled) { if (realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.READ_GROUPS_ENABLED) != null) { readGroupsEnabled = Boolean.parseBoolean( realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.READ_GROUPS_ENABLED)); log.debug("Read LDAP groups enabled: " + readGroupsEnabled); } } else { // Write overwrites Read readGroupsEnabled = true; log.debug("Read LDAP groups enabled: true"); } emptyRolesAllowed = Boolean .parseBoolean(realmConfig.getUserStoreProperty(LDAPConstants.EMPTY_ROLES_ALLOWED)); String groupEntryObjectClass = realmConfig.getUserStoreProperty(LDAPConstants.GROUP_ENTRY_OBJECT_CLASS); if (groupEntryObjectClass == null || groupEntryObjectClass.equals("")) { throw new UserStoreException( "Required GroupEntryObjectClass property is not set at the LDAP configurations"); } userSearchBase = realmConfig.getUserStoreProperty(LDAPConstants.USER_SEARCH_BASE); groupSearchBase = realmConfig.getUserStoreProperty(LDAPConstants.GROUP_SEARCH_BASE); } // // /** // * Check and add the initial data to the user store for user manager to start properly. // */ // private void addInitialAdminData() throws UserStoreException { // // if (realmConfig.getAdminUserName() == null || realmConfig.getAdminRoleName() == null) { // throw new UserStoreException( // "Admin user name or role name is not valid. Please provide valid values."); // } // // String adminUserName = UserCoreUtil.removeDomainFromName(realmConfig.getAdminUserName()); // String adminRoleName = UserCoreUtil.removeDomainFromName(realmConfig.getAdminRoleName()); // // // add admin user if not already added - if it is the first start up // // only. this will not affect MT environment. since TenantManager // // creates the tenant admin, before initializing UserStoreManager // // for the tenant. // if (!doCheckExistingUser(adminUserName)) { // if (log.isDebugEnabled()) { // log.debug("Admin user does not exist. Hence creating the user."); // } // this.doAddUser(adminUserName, realmConfig.getAdminPassword(), null, null, null); // } // // // add admin role, if not already added. // if (!isExistingRole(realmConfig.getAdminRoleName())) { // if (log.isDebugEnabled()) { // log.debug("Admin role does not exist. Hence creating the role."); // } // // try { // this.addRole(realmConfig.getAdminRoleName(), // new String[] { realmConfig.getAdminUserName() }, null, false); // } catch (org.wso2.carbon.user.api.UserStoreException e) { // throw new UserStoreException(e); // } // } // /* since this is at startup, admin user name is sent without domain */ // if (!super.isUserInRole(adminUserName, adminRoleName)) { // // this is when the admin role is changed in the user-mgt.xml // if (log.isDebugEnabled()) { // log.debug("Admin user is not in the Admin role. Adding the Admin user" // + " to the Admin role"); // } // String[] roles = { realmConfig.getAdminRoleName() }; // this.updateRoleListOfUser(realmConfig.getAdminUserName(), null, roles); // } // } @Override public Properties getDefaultUserStoreProperties() { Properties properties = new Properties(); properties.setMandatoryProperties(ReadWriteLDAPUserStoreConstants.RWLDAP_USERSTORE_PROPERTIES .toArray(new Property[ReadWriteLDAPUserStoreConstants.RWLDAP_USERSTORE_PROPERTIES.size()])); properties.setOptionalProperties(ReadWriteLDAPUserStoreConstants.OPTINAL_RWLDAP_USERSTORE_PROPERTIES .toArray(new Property[ReadWriteLDAPUserStoreConstants.OPTINAL_RWLDAP_USERSTORE_PROPERTIES.size()])); return properties; } /** * Escaping ldap DN special characters in a String value * @param text * @return */ private String escapeSpecialCharactersForDN(String text) { boolean replaceEscapeCharacters = true; String replaceEscapeCharactersAtUserLoginString = realmConfig.getUserStoreProperty( UserCoreConstants.RealmConfig.PROPERTY_REPLACE_ESCAPE_CHARACTERS_AT_USER_LOGIN); if (replaceEscapeCharactersAtUserLoginString != null) { replaceEscapeCharacters = Boolean.parseBoolean(replaceEscapeCharactersAtUserLoginString); if (log.isDebugEnabled()) { log.debug("Replace escape characters configured to: " + replaceEscapeCharactersAtUserLoginString); } } if (replaceEscapeCharacters) { StringBuilder sb = new StringBuilder(); if ((text.length() > 0) && ((text.charAt(0) == ' ') || (text.charAt(0) == '#'))) { sb.append('\\'); // add the leading backslash if needed } for (int i = 0; i < text.length(); i++) { char currentChar = text.charAt(i); switch (currentChar) { case '\\': if (text.charAt(i + 1) == '*') { sb.append("*"); i++; break; } sb.append("\\\\"); break; case ',': sb.append("\\,"); break; case '+': sb.append("\\+"); break; case '"': sb.append("\\\""); break; case '<': sb.append("\\<"); break; case '>': sb.append("\\>"); break; case ';': sb.append("\\;"); break; default: sb.append(currentChar); } } if ((text.length() > 1) && (text.charAt(text.length() - 1) == ' ')) { sb.insert(sb.length() - 1, '\\'); // add the trailing backslash if needed } if (log.isDebugEnabled()) { log.debug("value after escaping special characters in " + text + " : " + sb.toString()); } return sb.toString(); } else { return text; } } /** * Escaping ldap search filter special characters in a string * @param dnPartial * @return */ private String escapeSpecialCharactersForFilter(String dnPartial) { boolean replaceEscapeCharacters = true; dnPartial.replace("\\*", "*"); String replaceEscapeCharactersAtUserLoginString = realmConfig.getUserStoreProperty( UserCoreConstants.RealmConfig.PROPERTY_REPLACE_ESCAPE_CHARACTERS_AT_USER_LOGIN); if (replaceEscapeCharactersAtUserLoginString != null) { replaceEscapeCharacters = Boolean.parseBoolean(replaceEscapeCharactersAtUserLoginString); if (log.isDebugEnabled()) { log.debug("Replace escape characters configured to: " + replaceEscapeCharactersAtUserLoginString); } } //TODO: implement character escaping for * if (replaceEscapeCharacters) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < dnPartial.length(); i++) { char currentChar = dnPartial.charAt(i); switch (currentChar) { case '\\': sb.append("\\5c"); break; case '*': sb.append("\\2a"); break; case '(': sb.append("\\28"); break; case ')': sb.append("\\29"); break; case '\u0000': sb.append("\\00"); break; default: sb.append(currentChar); } } return sb.toString(); } else { return dnPartial; } } /** * Escaping ldap DN special characters in a String value * @param text * @return */ private String escapeSpecialCharactersForDNWithStar(String text) { boolean replaceEscapeCharacters = true; text.replace("\\*", "*"); String replaceEscapeCharactersAtUserLoginString = realmConfig.getUserStoreProperty( UserCoreConstants.RealmConfig.PROPERTY_REPLACE_ESCAPE_CHARACTERS_AT_USER_LOGIN); if (replaceEscapeCharactersAtUserLoginString != null) { replaceEscapeCharacters = Boolean.parseBoolean(replaceEscapeCharactersAtUserLoginString); if (log.isDebugEnabled()) { log.debug("Replace escape characters configured to: " + replaceEscapeCharactersAtUserLoginString); } } if (replaceEscapeCharacters) { StringBuilder sb = new StringBuilder(); if ((text.length() > 0) && ((text.charAt(0) == ' ') || (text.charAt(0) == '#'))) { sb.append('\\'); // add the leading backslash if needed } for (int i = 0; i < text.length(); i++) { char currentChar = text.charAt(i); switch (currentChar) { case '\\': sb.append("\\\\"); break; case ',': sb.append("\\,"); break; case '+': sb.append("\\+"); break; case '"': sb.append("\\\""); break; case '<': sb.append("\\<"); break; case '>': sb.append("\\>"); break; case ';': sb.append("\\;"); break; case '*': sb.append("\\2a"); break; default: sb.append(currentChar); } } if ((text.length() > 1) && (text.charAt(text.length() - 1) == ' ')) { sb.insert(sb.length() - 1, '\\'); // add the trailing backslash if needed } if (log.isDebugEnabled()) { log.debug("value after escaping special characters in " + text + " : " + sb.toString()); } return sb.toString(); } else { return text; } } /** * This method performs the additional level escaping for ldap search. In ldap search / and " characters * have to be escaped again * @param dn * @return */ private String escapeDNForSearch(String dn) { boolean replaceEscapeCharacters = true; String replaceEscapeCharactersAtUserLoginString = realmConfig.getUserStoreProperty( UserCoreConstants.RealmConfig.PROPERTY_REPLACE_ESCAPE_CHARACTERS_AT_USER_LOGIN); if (replaceEscapeCharactersAtUserLoginString != null) { replaceEscapeCharacters = Boolean.parseBoolean(replaceEscapeCharactersAtUserLoginString); if (log.isDebugEnabled()) { log.debug("Replace escape characters configured to: " + replaceEscapeCharactersAtUserLoginString); } } if (replaceEscapeCharacters) { return dn.replace("\\\\", "\\\\\\").replace("\\\"", "\\\\\""); } else { return dn; } } }