diff --git a/federation/ldap/pom.xml b/federation/ldap/pom.xml index 38fa19d3ddb..bf3feeab028 100755 --- a/federation/ldap/pom.xml +++ b/federation/ldap/pom.xml @@ -26,9 +26,14 @@ 4.0.0 keycloak-ldap-federation - Keycloak LDAP Federation + Keycloak LDAP UserStoreProvider + + 1.8 + 1.8 + + org.keycloak @@ -40,11 +45,6 @@ keycloak-server-spi provided - - org.keycloak - keycloak-server-spi-private - provided - org.keycloak keycloak-kerberos-federation diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java deleted file mode 100644 index 7d32e6a122c..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap; - -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.UserFederationProvider; - -import javax.naming.directory.SearchControls; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -/** - * @author Marek Posolda - * - */ -public class LDAPConfig { - - private final Map config; - - public LDAPConfig(Map config) { - this.config = config; - } - - public String getConnectionUrl() { - return config.get(LDAPConstants.CONNECTION_URL); - } - - public String getFactoryName() { - // hardcoded for now - return "com.sun.jndi.ldap.LdapCtxFactory"; - } - - public String getAuthType() { - String value = config.get(LDAPConstants.AUTH_TYPE); - if (value == null) { - return LDAPConstants.AUTH_TYPE_SIMPLE; - } else { - return value; - } - } - - public String getUseTruststoreSpi() { - return config.get(LDAPConstants.USE_TRUSTSTORE_SPI); - } - - public String getUsersDn() { - String usersDn = config.get(LDAPConstants.USERS_DN); - - if (usersDn == null) { - // Just for the backwards compatibility 1.2 -> 1.3 . Should be removed later. - usersDn = config.get("userDnSuffix"); - } - - return usersDn; - } - - public Collection getUserObjectClasses() { - String objClassesCfg = config.get(LDAPConstants.USER_OBJECT_CLASSES); - String objClassesStr = (objClassesCfg != null && objClassesCfg.length() > 0) ? objClassesCfg.trim() : "inetOrgPerson,organizationalPerson"; - - String[] objectClasses = objClassesStr.split(","); - - // Trim them - Set userObjClasses = new HashSet<>(); - for (int i=0 ; i 1.3 . Should be removed later. - rdn = LDAPConstants.CN; - } - - } - return rdn; - } - - - public String getCustomUserSearchFilter() { - String customFilter = config.get(LDAPConstants.CUSTOM_USER_SEARCH_FILTER); - if (customFilter != null) { - customFilter = customFilter.trim(); - if (customFilter.length() > 0) { - return customFilter; - } - } - return null; - } - - public UserFederationProvider.EditMode getEditMode() { - String editModeString = config.get(LDAPConstants.EDIT_MODE); - if (editModeString == null) { - return UserFederationProvider.EditMode.READ_ONLY; - } else { - return UserFederationProvider.EditMode.valueOf(editModeString); - } - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java deleted file mode 100755 index 67cfb45fe71..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap; - -import org.jboss.logging.Logger; -import org.keycloak.common.constants.KerberosConstants; -import org.keycloak.credential.CredentialInput; -import org.keycloak.credential.CredentialModel; -import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator; -import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder; -import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore; -import org.keycloak.federation.ldap.kerberos.LDAPProviderKerberosConfig; -import org.keycloak.federation.ldap.mappers.LDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.LDAPMappersComparator; -import org.keycloak.federation.ldap.mappers.PasswordUpdated; -import org.keycloak.mappers.UserFederationMapper; -import org.keycloak.models.CredentialValidationOutput; -import org.keycloak.models.GroupModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.ModelException; -import org.keycloak.models.ModelReadOnlyException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserCredentialModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserManager; - -import javax.naming.AuthenticationException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @author Marek Posolda - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class LDAPFederationProvider implements UserFederationProvider { - private static final Logger logger = Logger.getLogger(LDAPFederationProvider.class); - - protected LDAPFederationProviderFactory factory; - protected KeycloakSession session; - protected UserFederationProviderModel model; - protected LDAPIdentityStore ldapIdentityStore; - protected EditMode editMode; - protected LDAPProviderKerberosConfig kerberosConfig; - protected PasswordUpdated updater; - - protected final Set supportedCredentialTypes = new HashSet<>(); - - public LDAPFederationProvider(LDAPFederationProviderFactory factory, KeycloakSession session, UserFederationProviderModel model, LDAPIdentityStore ldapIdentityStore) { - this.factory = factory; - this.session = session; - this.model = model; - this.ldapIdentityStore = ldapIdentityStore; - this.kerberosConfig = new LDAPProviderKerberosConfig(model); - this.editMode = ldapIdentityStore.getConfig().getEditMode(); - - supportedCredentialTypes.add(UserCredentialModel.PASSWORD); - if (kerberosConfig.isAllowKerberosAuthentication()) { - supportedCredentialTypes.add(UserCredentialModel.KERBEROS); - } - } - - public void setUpdater(PasswordUpdated updater) { - this.updater = updater; - } - - public KeycloakSession getSession() { - return session; - } - - public UserFederationProviderModel getModel() { - return model; - } - - public LDAPIdentityStore getLdapIdentityStore() { - return this.ldapIdentityStore; - } - - public EditMode getEditMode() { - return editMode; - } - - @Override - public UserModel validateAndProxy(RealmModel realm, UserModel local) { - LDAPObject ldapObject = loadAndValidateUser(realm, local); - if (ldapObject == null) { - return null; - } - - return proxy(realm, local, ldapObject); - } - - protected UserModel proxy(RealmModel realm, UserModel local, LDAPObject ldapObject) { - UserModel proxied = local; - switch (editMode) { - case READ_ONLY: - proxied = new ReadonlyLDAPUserModelDelegate(local, this); - break; - case WRITABLE: - proxied = new WritableLDAPUserModelDelegate(local, this, ldapObject); - break; - case UNSYNCED: - proxied = new UnsyncedLDAPUserModelDelegate(local, this); - } - - Set federationMappers = realm.getUserFederationMappersByFederationProvider(model.getId()); - List sortedMappers = sortMappersAsc(federationMappers); - for (UserFederationMapperModel mapperModel : sortedMappers) { - LDAPFederationMapper ldapMapper = getMapper(mapperModel); - proxied = ldapMapper.proxy(mapperModel, this, ldapObject, proxied, realm); - } - - return proxied; - } - - @Override - public Set getSupportedCredentialTypes() { - return new HashSet(this.supportedCredentialTypes); - } - - @Override - public boolean synchronizeRegistrations() { - return "true".equalsIgnoreCase(model.getConfig().get(LDAPConstants.SYNC_REGISTRATIONS)) && editMode == EditMode.WRITABLE; - } - - @Override - public UserModel register(RealmModel realm, UserModel user) { - if (editMode == EditMode.READ_ONLY || editMode == EditMode.UNSYNCED) throw new IllegalStateException("Registration is not supported by this ldap server"); - if (!synchronizeRegistrations()) throw new IllegalStateException("Registration is not supported by this ldap server"); - - LDAPObject ldapUser = LDAPUtils.addUserToLDAP(this, realm, user); - LDAPUtils.checkUuid(ldapUser, ldapIdentityStore.getConfig()); - user.setSingleAttribute(LDAPConstants.LDAP_ID, ldapUser.getUuid()); - user.setSingleAttribute(LDAPConstants.LDAP_ENTRY_DN, ldapUser.getDn().toString()); - - return proxy(realm, user, ldapUser); - } - - @Override - public boolean removeUser(RealmModel realm, UserModel user) { - if (editMode == EditMode.READ_ONLY || editMode == EditMode.UNSYNCED) { - logger.warnf("User '%s' can't be deleted in LDAP as editMode is '%s'. Deleting user just from Keycloak DB, but he will be re-imported from LDAP again once searched in Keycloak", user.getUsername(), editMode.toString()); - return true; - } - - LDAPObject ldapObject = loadAndValidateUser(realm, user); - if (ldapObject == null) { - logger.warnf("User '%s' can't be deleted from LDAP as it doesn't exist here", user.getUsername()); - return false; - } - - ldapIdentityStore.remove(ldapObject); - return true; - } - - @Override - public List searchByAttributes(Map attributes, RealmModel realm, int maxResults) { - List searchResults =new LinkedList(); - - List ldapUsers = searchLDAP(realm, attributes, maxResults); - for (LDAPObject ldapUser : ldapUsers) { - String ldapUsername = LDAPUtils.getUsername(ldapUser, this.ldapIdentityStore.getConfig()); - if (session.userStorage().getUserByUsername(ldapUsername, realm) == null) { - UserModel imported = importUserFromLDAP(session, realm, ldapUser); - searchResults.add(imported); - } - } - - return searchResults; - } - - @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { - Set federationMappers = realm.getUserFederationMappersByFederationProvider(model.getId()); - for (UserFederationMapperModel mapperModel : federationMappers) { - LDAPFederationMapper ldapMapper = getMapper(mapperModel); - List users = ldapMapper.getGroupMembers(mapperModel, this, realm, group, firstResult, maxResults); - - // Sufficient for now - if (users.size() > 0) { - return users; - } - } - - return Collections.emptyList(); - } - - public List loadUsersByUsernames(List usernames, RealmModel realm) { - List result = new ArrayList<>(); - for (String username : usernames) { - UserModel kcUser = session.users().getUserByUsername(username, realm); - if (kcUser == null) { - logger.warnf("User '%s' referenced by membership wasn't found in LDAP", username); - } else if (!model.getId().equals(kcUser.getFederationLink())) { - logger.warnf("Incorrect federation provider of user '%s'", kcUser.getUsername()); - } else { - result.add(kcUser); - } - } - return result; - } - - protected List searchLDAP(RealmModel realm, Map attributes, int maxResults) { - - List results = new ArrayList(); - if (attributes.containsKey(USERNAME)) { - LDAPObject user = loadLDAPUserByUsername(realm, attributes.get(USERNAME)); - if (user != null) { - results.add(user); - } - } - - if (attributes.containsKey(EMAIL)) { - LDAPObject user = queryByEmail(realm, attributes.get(EMAIL)); - if (user != null) { - results.add(user); - } - } - - if (attributes.containsKey(FIRST_NAME) || attributes.containsKey(LAST_NAME)) { - LDAPQuery ldapQuery = LDAPUtils.createQueryForUserSearch(this, realm); - LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder(); - - // Mapper should replace parameter with correct LDAP mapped attributes - if (attributes.containsKey(FIRST_NAME)) { - ldapQuery.addWhereCondition(conditionsBuilder.equal(FIRST_NAME, attributes.get(FIRST_NAME))); - } - if (attributes.containsKey(LAST_NAME)) { - ldapQuery.addWhereCondition(conditionsBuilder.equal(LAST_NAME, attributes.get(LAST_NAME))); - } - - List ldapObjects = ldapQuery.getResultList(); - results.addAll(ldapObjects); - } - - return results; - } - - /** - * @param local - * @return ldapUser corresponding to local user or null if user is no longer in LDAP - */ - protected LDAPObject loadAndValidateUser(RealmModel realm, UserModel local) { - LDAPObject ldapUser = loadLDAPUserByUsername(realm, local.getUsername()); - if (ldapUser == null) { - return null; - } - LDAPUtils.checkUuid(ldapUser, ldapIdentityStore.getConfig()); - - if (ldapUser.getUuid().equals(local.getFirstAttribute(LDAPConstants.LDAP_ID))) { - return ldapUser; - } else { - logger.warnf("LDAP User invalid. ID doesn't match. ID from LDAP [%s], LDAP ID from local DB: [%s]", ldapUser.getUuid(), local.getFirstAttribute(LDAPConstants.LDAP_ID)); - return null; - } - } - - @Override - public boolean isValid(RealmModel realm, UserModel local) { - return loadAndValidateUser(realm, local) != null; - } - - @Override - public UserModel getUserByUsername(RealmModel realm, String username) { - LDAPObject ldapUser = loadLDAPUserByUsername(realm, username); - if (ldapUser == null) { - return null; - } - - return importUserFromLDAP(session, realm, ldapUser); - } - - protected UserModel importUserFromLDAP(KeycloakSession session, RealmModel realm, LDAPObject ldapUser) { - String ldapUsername = LDAPUtils.getUsername(ldapUser, ldapIdentityStore.getConfig()); - LDAPUtils.checkUuid(ldapUser, ldapIdentityStore.getConfig()); - - UserModel imported = session.userStorage().addUser(realm, ldapUsername); - imported.setEnabled(true); - - Set federationMappers = realm.getUserFederationMappersByFederationProvider(getModel().getId()); - List sortedMappers = sortMappersDesc(federationMappers); - for (UserFederationMapperModel mapperModel : sortedMappers) { - if (logger.isTraceEnabled()) { - logger.tracef("Using mapper %s during import user from LDAP", mapperModel); - } - LDAPFederationMapper ldapMapper = getMapper(mapperModel); - ldapMapper.onImportUserFromLDAP(mapperModel, this, ldapUser, imported, realm, true); - } - - String userDN = ldapUser.getDn().toString(); - imported.setFederationLink(model.getId()); - imported.setSingleAttribute(LDAPConstants.LDAP_ID, ldapUser.getUuid()); - imported.setSingleAttribute(LDAPConstants.LDAP_ENTRY_DN, userDN); - - logger.debugf("Imported new user from LDAP to Keycloak DB. Username: [%s], Email: [%s], LDAP_ID: [%s], LDAP Entry DN: [%s]", imported.getUsername(), imported.getEmail(), - ldapUser.getUuid(), userDN); - return proxy(realm, imported, ldapUser); - } - - protected LDAPObject queryByEmail(RealmModel realm, String email) { - LDAPQuery ldapQuery = LDAPUtils.createQueryForUserSearch(this, realm); - LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder(); - - // Mapper should replace "email" in parameter name with correct LDAP mapped attribute - Condition emailCondition = conditionsBuilder.equal(UserModel.EMAIL, email); - ldapQuery.addWhereCondition(emailCondition); - - return ldapQuery.getFirstResult(); - } - - - @Override - public UserModel getUserByEmail(RealmModel realm, String email) { - LDAPObject ldapUser = queryByEmail(realm, email); - if (ldapUser == null) { - return null; - } - - // Check here if user already exists - String ldapUsername = LDAPUtils.getUsername(ldapUser, ldapIdentityStore.getConfig()); - if (session.userStorage().getUserByUsername(ldapUsername, realm) != null) { - throw new ModelDuplicateException("User with username '" + ldapUsername + "' already exists in Keycloak. It conflicts with LDAP user with email '" + email + "'"); - } - - return importUserFromLDAP(session, realm, ldapUser); - } - - @Override - public void preRemove(RealmModel realm) { - // complete Don't think we have to do anything - } - - @Override - public void preRemove(RealmModel realm, RoleModel role) { - // TODO: Maybe mappers callback to ensure role deletion propagated to LDAP by RoleLDAPFederationMapper? - } - - @Override - public void preRemove(RealmModel realm, GroupModel group) { - - } - - public boolean validPassword(RealmModel realm, UserModel user, String password) { - if (kerberosConfig.isAllowKerberosAuthentication() && kerberosConfig.isUseKerberosForPasswordAuthentication()) { - // Use Kerberos JAAS (Krb5LoginModule) - KerberosUsernamePasswordAuthenticator authenticator = factory.createKerberosUsernamePasswordAuthenticator(kerberosConfig); - return authenticator.validUser(user.getUsername(), password); - } else { - // Use Naming LDAP API - LDAPObject ldapUser = loadAndValidateUser(realm, user); - - try { - ldapIdentityStore.validatePassword(ldapUser, password); - return true; - } catch (AuthenticationException ae) { - - // Check if any mapper provides callback for handle LDAP AuthenticationException - Set federationMappers = realm.getUserFederationMappersByFederationProvider(getModel().getId()); - boolean processed = false; - for (UserFederationMapperModel mapperModel : federationMappers) { - LDAPFederationMapper ldapMapper = getMapper(mapperModel); - processed = processed || ldapMapper.onAuthenticationFailure(mapperModel, this, ldapUser, user, ae, realm); - } - - return processed; - } - } - } - - - @Override - public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) { - if (!CredentialModel.PASSWORD.equals(input.getType()) || ! (input instanceof UserCredentialModel)) return false; - if (editMode == EditMode.READ_ONLY) { - throw new ModelReadOnlyException("Federated storage is not writable"); - - } else if (editMode == EditMode.WRITABLE) { - LDAPIdentityStore ldapIdentityStore = getLdapIdentityStore(); - UserCredentialModel cred = (UserCredentialModel)input; - String password = cred.getValue(); - LDAPObject ldapUser = loadAndValidateUser(realm, user); - ldapIdentityStore.updatePassword(ldapUser, password); - if (updater != null) updater.passwordUpdated(user, ldapUser, input); - return true; - } else { - return false; - } - } - - @Override - public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) { - - } - - @Override - public Set getDisableableCredentialTypes(RealmModel realm, UserModel user) { - return Collections.EMPTY_SET; - } - - @Override - public boolean supportsCredentialType(String credentialType) { - return getSupportedCredentialTypes().contains(credentialType); - } - - @Override - public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) { - return getSupportedCredentialTypes().contains(credentialType); - } - - @Override - public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) { - if (!(input instanceof UserCredentialModel)) return false; - if (input.getType().equals(UserCredentialModel.PASSWORD) && !session.userCredentialManager().isConfiguredLocally(realm, user, UserCredentialModel.PASSWORD)) { - return validPassword(realm, user, ((UserCredentialModel)input).getValue()); - } else { - return false; // invalid cred type - } - } - - @Override - public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) { - if (credential.getType().equals(UserCredentialModel.KERBEROS)) { - if (kerberosConfig.isAllowKerberosAuthentication()) { - String spnegoToken = credential.getValue(); - SPNEGOAuthenticator spnegoAuthenticator = factory.createSPNEGOAuthenticator(spnegoToken, kerberosConfig); - - spnegoAuthenticator.authenticate(); - - Map state = new HashMap(); - if (spnegoAuthenticator.isAuthenticated()) { - - // TODO: This assumes that LDAP "uid" is equal to kerberos principal name. Like uid "hnelson" and kerberos principal "hnelson@KEYCLOAK.ORG". - // Check if it's correct or if LDAP attribute for mapping kerberos principal should be available (For ApacheDS it seems to be attribute "krb5PrincipalName" but on MSAD it's likely different) - String username = spnegoAuthenticator.getAuthenticatedUsername(); - UserModel user = findOrCreateAuthenticatedUser(realm, username); - - if (user == null) { - logger.warnf("Kerberos/SPNEGO authentication succeeded with username [%s], but couldn't find or create user with federation provider [%s]", username, model.getDisplayName()); - return CredentialValidationOutput.failed(); - } else { - String delegationCredential = spnegoAuthenticator.getSerializedDelegationCredential(); - if (delegationCredential != null) { - state.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, delegationCredential); - } - - return new CredentialValidationOutput(user, CredentialValidationOutput.Status.AUTHENTICATED, state); - } - } else { - state.put(KerberosConstants.RESPONSE_TOKEN, spnegoAuthenticator.getResponseToken()); - return new CredentialValidationOutput(null, CredentialValidationOutput.Status.CONTINUE, state); - } - } - } - - return CredentialValidationOutput.failed(); - } - - @Override - public void close() { - } - - /** - * Called after successful kerberos authentication - * - * @param realm realm - * @param username username without realm prefix - * @return finded or newly created user - */ - protected UserModel findOrCreateAuthenticatedUser(RealmModel realm, String username) { - UserModel user = session.userStorage().getUserByUsername(username, realm); - if (user != null) { - logger.debugf("Kerberos authenticated user [%s] found in Keycloak storage", username); - if (!model.getId().equals(user.getFederationLink())) { - logger.warnf("User with username [%s] already exists, but is not linked to provider [%s]", username, model.getDisplayName()); - return null; - } else { - LDAPObject ldapObject = loadAndValidateUser(realm, user); - if (ldapObject != null) { - return proxy(realm, user, ldapObject); - } else { - logger.warnf("User with username [%s] aready exists and is linked to provider [%s] but is not valid. Stale LDAP_ID on local user is: %s", - username, model.getDisplayName(), user.getFirstAttribute(LDAPConstants.LDAP_ID)); - logger.warn("Will re-create user"); - new UserManager(session).removeUser(realm, user, session.userStorage()); - } - } - } - - // Creating user to local storage - logger.debugf("Kerberos authenticated user [%s] not in Keycloak storage. Creating him", username); - return getUserByUsername(realm, username); - } - - public LDAPObject loadLDAPUserByUsername(RealmModel realm, String username) { - LDAPQuery ldapQuery = LDAPUtils.createQueryForUserSearch(this, realm); - LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder(); - - String usernameMappedAttribute = this.ldapIdentityStore.getConfig().getUsernameLdapAttribute(); - Condition usernameCondition = conditionsBuilder.equal(usernameMappedAttribute, username); - ldapQuery.addWhereCondition(usernameCondition); - - LDAPObject ldapUser = ldapQuery.getFirstResult(); - if (ldapUser == null) { - return null; - } - - return ldapUser; - } - - public LDAPFederationMapper getMapper(UserFederationMapperModel mapperModel) { - LDAPFederationMapper ldapMapper = (LDAPFederationMapper) getSession().getProvider(UserFederationMapper.class, mapperModel.getFederationMapperType()); - if (ldapMapper == null) { - throw new ModelException("Can't find mapper type with ID: " + mapperModel.getFederationMapperType()); - } - - return ldapMapper; - } - - - public List sortMappersAsc(Collection mappers) { - return LDAPMappersComparator.sortAsc(getLdapIdentityStore().getConfig(), mappers); - } - - protected List sortMappersDesc(Collection mappers) { - return LDAPMappersComparator.sortDesc(getLdapIdentityStore().getConfig(), mappers); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java deleted file mode 100755 index 51a3c8cb705..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap; - -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.federation.kerberos.CommonKerberosConfig; -import org.keycloak.federation.kerberos.impl.KerberosServerSubjectAuthenticator; -import org.keycloak.federation.kerberos.impl.KerberosUsernamePasswordAuthenticator; -import org.keycloak.federation.kerberos.impl.SPNEGOAuthenticator; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder; -import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore; -import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory; -import org.keycloak.federation.ldap.mappers.LDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory; -import org.keycloak.federation.ldap.mappers.msad.MSADUserAccountControlMapperFactory; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.mappers.UserFederationMapper; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.KeycloakSessionTask; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationEventAwareProviderFactory; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.models.UserFederationSyncResult; -import org.keycloak.models.UserFederationValidatingProviderFactory; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.KeycloakModelUtils; - -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Set; - -/** - * @author Marek Posolda - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class LDAPFederationProviderFactory extends UserFederationEventAwareProviderFactory implements UserFederationValidatingProviderFactory { - private static final Logger logger = Logger.getLogger(LDAPFederationProviderFactory.class); - public static final String PROVIDER_NAME = LDAPConstants.LDAP_PROVIDER; - - private LDAPIdentityStoreRegistry ldapStoreRegistry; - - @Override - public UserFederationProvider create(KeycloakSession session) { - throw new IllegalAccessError("Illegal to call this method"); - } - - @Override - public LDAPFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) { - LDAPIdentityStore ldapIdentityStore = this.ldapStoreRegistry.getLdapStore(model); - return new LDAPFederationProvider(this, session, model, ldapIdentityStore); - } - - @Override - public void validateConfig(RealmModel realm, UserFederationProviderModel providerModel) throws FederationConfigValidationException { - LDAPConfig cfg = new LDAPConfig(providerModel.getConfig()); - String customFilter = cfg.getCustomUserSearchFilter(); - LDAPUtils.validateCustomLdapFilter(customFilter); - } - - @Override - public void init(Config.Scope config) { - this.ldapStoreRegistry = new LDAPIdentityStoreRegistry(); - } - - @Override - public void close() { - this.ldapStoreRegistry = null; - } - - @Override - public String getId() { - return PROVIDER_NAME; - } - - @Override - public Set getConfigurationOptions() { - return Collections.emptySet(); - } - - - // Best effort to create appropriate mappers according to our LDAP config - @Override - public void onProviderModelCreated(RealmModel realm, UserFederationProviderModel newProviderModel) { - LDAPConfig ldapConfig = new LDAPConfig(newProviderModel.getConfig()); - - boolean activeDirectory = ldapConfig.isActiveDirectory(); - UserFederationProvider.EditMode editMode = ldapConfig.getEditMode(); - String readOnly = String.valueOf(editMode == UserFederationProvider.EditMode.READ_ONLY || editMode == UserFederationProvider.EditMode.UNSYNCED); - String usernameLdapAttribute = ldapConfig.getUsernameLdapAttribute(); - - String alwaysReadValueFromLDAP = String.valueOf(editMode==UserFederationProvider.EditMode.READ_ONLY || editMode== UserFederationProvider.EditMode.WRITABLE); - - UserFederationMapperModel mapperModel; - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("username", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, usernameLdapAttribute, - UserAttributeLDAPFederationMapper.READ_ONLY, readOnly, - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false", - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "true"); - realm.addUserFederationMapper(mapperModel); - - // CN is typically used as RDN for Active Directory deployments - if (ldapConfig.getRdnLdapAttribute().equalsIgnoreCase(LDAPConstants.CN)) { - - if (usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.CN)) { - - // For AD deployments with "cn" as username, we will map "givenName" to first name - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME, - UserAttributeLDAPFederationMapper.READ_ONLY, readOnly, - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP, - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "true"); - realm.addUserFederationMapper(mapperModel); - - } else { - if (editMode == UserFederationProvider.EditMode.WRITABLE) { - - // For AD deployments with "sAMAccountName" as username and writable, we need to map "cn" as username as well (this is needed so we can register new users from KC into LDAP) and we will map "givenName" to first name. - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME, - UserAttributeLDAPFederationMapper.READ_ONLY, readOnly, - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP, - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "true"); - realm.addUserFederationMapper(mapperModel); - - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("username-cn", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN, - UserAttributeLDAPFederationMapper.READ_ONLY, readOnly, - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false", - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "true"); - realm.addUserFederationMapper(mapperModel); - } else { - - // For read-only LDAP, we map "cn" as full name - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID, - FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN, - FullNameLDAPFederationMapper.READ_ONLY, readOnly, - FullNameLDAPFederationMapper.WRITE_ONLY, "false"); - realm.addUserFederationMapper(mapperModel); - } - } - } else { - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN, - UserAttributeLDAPFederationMapper.READ_ONLY, readOnly, - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP, - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "true"); - realm.addUserFederationMapper(mapperModel); - } - - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("last name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.LAST_NAME, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.SN, - UserAttributeLDAPFederationMapper.READ_ONLY, readOnly, - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP, - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "true"); - realm.addUserFederationMapper(mapperModel); - - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("email", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.EMAIL, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.EMAIL, - UserAttributeLDAPFederationMapper.READ_ONLY, readOnly, - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false", - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "false"); - realm.addUserFederationMapper(mapperModel); - - String createTimestampLdapAttrName = activeDirectory ? "whenCreated" : LDAPConstants.CREATE_TIMESTAMP; - String modifyTimestampLdapAttrName = activeDirectory ? "whenChanged" : LDAPConstants.MODIFY_TIMESTAMP; - - // map createTimeStamp as read-only - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("creation date", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.CREATE_TIMESTAMP, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, createTimestampLdapAttrName, - UserAttributeLDAPFederationMapper.READ_ONLY, "true", - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP, - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "false"); - realm.addUserFederationMapper(mapperModel); - - // map modifyTimeStamp as read-only - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("modify date", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.MODIFY_TIMESTAMP, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, modifyTimestampLdapAttrName, - UserAttributeLDAPFederationMapper.READ_ONLY, "true", - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, alwaysReadValueFromLDAP, - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "false"); - realm.addUserFederationMapper(mapperModel); - - // MSAD specific mapper for account state propagation - if (activeDirectory) { - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("MSAD account controls", newProviderModel.getId(), MSADUserAccountControlMapperFactory.PROVIDER_ID); - realm.addUserFederationMapper(mapperModel); - } - } - - - @Override - public UserFederationSyncResult syncAllUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model) { - syncMappers(sessionFactory, realmId, model); - - logger.infof("Sync all users from LDAP to local store: realm: %s, federation provider: %s", realmId, model.getDisplayName()); - - LDAPQuery userQuery = createQuery(sessionFactory, realmId, model); - UserFederationSyncResult syncResult = syncImpl(sessionFactory, userQuery, realmId, model); - - // TODO: Remove all existing keycloak users, which have federation links, but are not in LDAP. Perhaps don't check users, which were just added or updated during this sync? - - logger.infof("Sync all users finished: %s", syncResult.getStatus()); - return syncResult; - } - - @Override - public UserFederationSyncResult syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync) { - syncMappers(sessionFactory, realmId, model); - - logger.infof("Sync changed users from LDAP to local store: realm: %s, federation provider: %s, last sync time: " + lastSync, realmId, model.getDisplayName()); - - // Sync newly created and updated users - LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder(); - Condition createCondition = conditionsBuilder.greaterThanOrEqualTo(LDAPConstants.CREATE_TIMESTAMP, lastSync); - Condition modifyCondition = conditionsBuilder.greaterThanOrEqualTo(LDAPConstants.MODIFY_TIMESTAMP, lastSync); - Condition orCondition = conditionsBuilder.orCondition(createCondition, modifyCondition); - - LDAPQuery userQuery = createQuery(sessionFactory, realmId, model); - userQuery.addWhereCondition(orCondition); - UserFederationSyncResult result = syncImpl(sessionFactory, userQuery, realmId, model); - - logger.infof("Sync changed users finished: %s", result.getStatus()); - return result; - } - - protected void syncMappers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model) { - KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { - - @Override - public void run(KeycloakSession session) { - LDAPFederationProvider ldapProvider = getInstance(session, model); - RealmModel realm = session.realms().getRealm(realmId); - Set mappers = realm.getUserFederationMappersByFederationProvider(model.getId()); - for (UserFederationMapperModel mapperModel : mappers) { - UserFederationMapper ldapMapper = session.getProvider(UserFederationMapper.class, mapperModel.getFederationMapperType()); - UserFederationSyncResult syncResult = ldapMapper.syncDataFromFederationProviderToKeycloak(mapperModel, ldapProvider, session, realm); - if (syncResult.getAdded() > 0 || syncResult.getUpdated() > 0 || syncResult.getRemoved() > 0 || syncResult.getFailed() > 0) { - logger.infof("Sync of federation mapper '%s' finished. Status: %s", mapperModel.getName(), syncResult.toString()); - } - } - } - - }); - } - - protected UserFederationSyncResult syncImpl(KeycloakSessionFactory sessionFactory, LDAPQuery userQuery, final String realmId, final UserFederationProviderModel fedModel) { - - final UserFederationSyncResult syncResult = new UserFederationSyncResult(); - - LDAPConfig ldapConfig = new LDAPConfig(fedModel.getConfig()); - boolean pagination = ldapConfig.isPagination(); - if (pagination) { - int pageSize = ldapConfig.getBatchSizeForSync(); - - boolean nextPage = true; - while (nextPage) { - userQuery.setLimit(pageSize); - final List users = userQuery.getResultList(); - nextPage = userQuery.getPaginationContext() != null; - UserFederationSyncResult currentPageSync = importLdapUsers(sessionFactory, realmId, fedModel, users); - syncResult.add(currentPageSync); - } - } else { - // LDAP pagination not available. Do everything in single transaction - final List users = userQuery.getResultList(); - UserFederationSyncResult currentSync = importLdapUsers(sessionFactory, realmId, fedModel, users); - syncResult.add(currentSync); - } - - return syncResult; - } - - private LDAPQuery createQuery(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model) { - class QueryHolder { - LDAPQuery query; - } - - final QueryHolder queryHolder = new QueryHolder(); - KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { - - @Override - public void run(KeycloakSession session) { - LDAPFederationProvider ldapFedProvider = getInstance(session, model); - RealmModel realm = session.realms().getRealm(realmId); - queryHolder.query = LDAPUtils.createQueryForUserSearch(ldapFedProvider, realm); - } - - }); - return queryHolder.query; - } - - protected UserFederationSyncResult importLdapUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel fedModel, List ldapUsers) { - final UserFederationSyncResult syncResult = new UserFederationSyncResult(); - - class BooleanHolder { - private boolean value = true; - } - final BooleanHolder exists = new BooleanHolder(); - - for (final LDAPObject ldapUser : ldapUsers) { - - try { - - // Process each user in it's own transaction to avoid global fail - KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { - - @Override - public void run(KeycloakSession session) { - LDAPFederationProvider ldapFedProvider = getInstance(session, fedModel); - RealmModel currentRealm = session.realms().getRealm(realmId); - - String username = LDAPUtils.getUsername(ldapUser, ldapFedProvider.getLdapIdentityStore().getConfig()); - exists.value = true; - LDAPUtils.checkUuid(ldapUser, ldapFedProvider.getLdapIdentityStore().getConfig()); - UserModel currentUser = session.userStorage().getUserByUsername(username, currentRealm); - - if (currentUser == null) { - - // Add new user to Keycloak - exists.value = false; - ldapFedProvider.importUserFromLDAP(session, currentRealm, ldapUser); - syncResult.increaseAdded(); - - } else { - if ((fedModel.getId().equals(currentUser.getFederationLink())) && (ldapUser.getUuid().equals(currentUser.getFirstAttribute(LDAPConstants.LDAP_ID)))) { - - // Update keycloak user - Set federationMappers = currentRealm.getUserFederationMappersByFederationProvider(fedModel.getId()); - List sortedMappers = ldapFedProvider.sortMappersDesc(federationMappers); - for (UserFederationMapperModel mapperModel : sortedMappers) { - LDAPFederationMapper ldapMapper = ldapFedProvider.getMapper(mapperModel); - ldapMapper.onImportUserFromLDAP(mapperModel, ldapFedProvider, ldapUser, currentUser, currentRealm, false); - } - - logger.debugf("Updated user from LDAP: %s", currentUser.getUsername()); - syncResult.increaseUpdated(); - } else { - logger.warnf("User '%s' is not updated during sync as he already exists in Keycloak database but is not linked to federation provider '%s'", username, fedModel.getDisplayName()); - syncResult.increaseFailed(); - } - } - } - - }); - } catch (ModelException me) { - logger.error("Failed during import user from LDAP", me); - syncResult.increaseFailed(); - - // Remove user if we already added him during this transaction - if (!exists.value) { - KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { - - @Override - public void run(KeycloakSession session) { - LDAPFederationProvider ldapFedProvider = getInstance(session, fedModel); - RealmModel currentRealm = session.realms().getRealm(realmId); - String username = null; - try { - username = LDAPUtils.getUsername(ldapUser, ldapFedProvider.getLdapIdentityStore().getConfig()); - } catch (ModelException ignore) { - } - - if (username != null) { - UserModel existing = session.userStorage().getUserByUsername(username, currentRealm); - if (existing != null) { - session.userStorage().removeUser(currentRealm, existing); - } - } - } - - }); - } - } - } - - return syncResult; - } - - protected SPNEGOAuthenticator createSPNEGOAuthenticator(String spnegoToken, CommonKerberosConfig kerberosConfig) { - KerberosServerSubjectAuthenticator kerberosAuth = createKerberosSubjectAuthenticator(kerberosConfig); - return new SPNEGOAuthenticator(kerberosConfig, kerberosAuth, spnegoToken); - } - - protected KerberosServerSubjectAuthenticator createKerberosSubjectAuthenticator(CommonKerberosConfig kerberosConfig) { - return new KerberosServerSubjectAuthenticator(kerberosConfig); - } - - protected KerberosUsernamePasswordAuthenticator createKerberosUsernamePasswordAuthenticator(CommonKerberosConfig kerberosConfig) { - return new KerberosUsernamePasswordAuthenticator(kerberosConfig); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java deleted file mode 100644 index 7470d4375fd..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap; - -import org.jboss.logging.Logger; -import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.UserFederationProviderModel; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Marek Posolda - */ -public class LDAPIdentityStoreRegistry { - - private static final Logger logger = Logger.getLogger(LDAPIdentityStoreRegistry.class); - - private Map ldapStores = new ConcurrentHashMap(); - - public LDAPIdentityStore getLdapStore(UserFederationProviderModel model) { - LDAPIdentityStoreContext context = ldapStores.get(model.getId()); - - // Ldap config might have changed for the realm. In this case, we must re-initialize - Map config = model.getConfig(); - if (context == null || !config.equals(context.config)) { - logLDAPConfig(model.getDisplayName(), config); - - LDAPIdentityStore store = createLdapIdentityStore(config); - context = new LDAPIdentityStoreContext(config, store); - ldapStores.put(model.getId(), context); - } - return context.store; - } - - // Don't log LDAP password - private void logLDAPConfig(String fedProviderDisplayName, Map ldapConfig) { - Map copy = new HashMap(ldapConfig); - copy.remove(LDAPConstants.BIND_CREDENTIAL); - logger.infof("Creating new LDAP based partition manager for the Federation provider: " + fedProviderDisplayName + ", LDAP Configuration: " + copy); - } - - /** - * @param ldapConfig from realm - * @return PartitionManager instance based on LDAP store - */ - public static LDAPIdentityStore createLdapIdentityStore(Map ldapConfig) { - LDAPConfig cfg = new LDAPConfig(ldapConfig); - - checkSystemProperty("com.sun.jndi.ldap.connect.pool.authentication", "none simple"); - checkSystemProperty("com.sun.jndi.ldap.connect.pool.initsize", "1"); - checkSystemProperty("com.sun.jndi.ldap.connect.pool.maxsize", "1000"); - checkSystemProperty("com.sun.jndi.ldap.connect.pool.prefsize", "5"); - checkSystemProperty("com.sun.jndi.ldap.connect.pool.timeout", "300000"); - checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain"); - checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off"); - - return new LDAPIdentityStore(cfg); - } - - private static void checkSystemProperty(String name, String defaultValue) { - if (System.getProperty(name) == null) { - System.setProperty(name, defaultValue); - } - } - - - private class LDAPIdentityStoreContext { - - private LDAPIdentityStoreContext(Map config, LDAPIdentityStore store) { - this.config = config; - this.store = store; - } - - private Map config; - private LDAPIdentityStore store; - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java deleted file mode 100755 index 0c203665732..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap; - -import org.keycloak.federation.ldap.idm.model.LDAPDn; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder; -import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore; -import org.keycloak.federation.ldap.mappers.LDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.membership.MembershipType; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserModel; - -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Allow to directly call some operations against LDAPIdentityStore. - * - * @author Marek Posolda - */ -public class LDAPUtils { - - /** - * @param ldapProvider - * @param realm - * @param user - * @return newly created LDAPObject with all the attributes, uuid and DN properly set - */ - public static LDAPObject addUserToLDAP(LDAPFederationProvider ldapProvider, RealmModel realm, UserModel user) { - LDAPObject ldapUser = new LDAPObject(); - - LDAPIdentityStore ldapStore = ldapProvider.getLdapIdentityStore(); - LDAPConfig ldapConfig = ldapStore.getConfig(); - ldapUser.setRdnAttributeName(ldapConfig.getRdnLdapAttribute()); - ldapUser.setObjectClasses(ldapConfig.getUserObjectClasses()); - - Set federationMappers = realm.getUserFederationMappersByFederationProvider(ldapProvider.getModel().getId()); - List sortedMappers = ldapProvider.sortMappersAsc(federationMappers); - for (UserFederationMapperModel mapperModel : sortedMappers) { - LDAPFederationMapper ldapMapper = ldapProvider.getMapper(mapperModel); - ldapMapper.onRegisterUserToLDAP(mapperModel, ldapProvider, ldapUser, user, realm); - } - - LDAPUtils.computeAndSetDn(ldapConfig, ldapUser); - ldapStore.add(ldapUser); - return ldapUser; - } - - public static LDAPQuery createQueryForUserSearch(LDAPFederationProvider ldapProvider, RealmModel realm) { - LDAPQuery ldapQuery = new LDAPQuery(ldapProvider); - LDAPConfig config = ldapProvider.getLdapIdentityStore().getConfig(); - ldapQuery.setSearchScope(config.getSearchScope()); - ldapQuery.setSearchDn(config.getUsersDn()); - ldapQuery.addObjectClasses(config.getUserObjectClasses()); - - String customFilter = config.getCustomUserSearchFilter(); - if (customFilter != null) { - Condition customFilterCondition = new LDAPQueryConditionsBuilder().addCustomLDAPFilter(customFilter); - ldapQuery.addWhereCondition(customFilterCondition); - } - - Set mapperModels = realm.getUserFederationMappersByFederationProvider(ldapProvider.getModel().getId()); - ldapQuery.addMappers(mapperModels); - - return ldapQuery; - } - - // ldapUser has filled attributes, but doesn't have filled dn. - private static void computeAndSetDn(LDAPConfig config, LDAPObject ldapUser) { - String rdnLdapAttrName = config.getRdnLdapAttribute(); - String rdnLdapAttrValue = ldapUser.getAttributeAsString(rdnLdapAttrName); - if (rdnLdapAttrValue == null) { - throw new ModelException("RDN Attribute [" + rdnLdapAttrName + "] is not filled. Filled attributes: " + ldapUser.getAttributes()); - } - - LDAPDn dn = LDAPDn.fromString(config.getUsersDn()); - dn.addFirst(rdnLdapAttrName, rdnLdapAttrValue); - ldapUser.setDn(dn); - } - - public static String getUsername(LDAPObject ldapUser, LDAPConfig config) { - String usernameAttr = config.getUsernameLdapAttribute(); - String ldapUsername = ldapUser.getAttributeAsString(usernameAttr); - - if (ldapUsername == null) { - throw new ModelException("User returned from LDAP has null username! Check configuration of your LDAP mappings. Mapped username LDAP attribute: " + - config.getUsernameLdapAttribute() + ", user DN: " + ldapUser.getDn() + ", attributes from LDAP: " + ldapUser.getAttributes()); - } - - return ldapUsername; - } - - public static void checkUuid(LDAPObject ldapUser, LDAPConfig config) { - if (ldapUser.getUuid() == null) { - throw new ModelException("User returned from LDAP has null uuid! Check configuration of your LDAP settings. UUID Attribute must be unique among your LDAP records and available on all the LDAP user records. " + - "If your LDAP server really doesn't support the notion of UUID, you can use any other attribute, which is supposed to be unique among LDAP users in tree. For example 'uid' or 'entryDN' . " + - "Mapped UUID LDAP attribute: " + config.getUuidLDAPAttributeName() + ", user DN: " + ldapUser.getDn()); - } - } - - - // roles & groups - - public static LDAPObject createLDAPGroup(LDAPFederationProvider ldapProvider, String groupName, String groupNameAttribute, Collection objectClasses, - String parentDn, Map> additionalAttributes) { - LDAPObject ldapObject = new LDAPObject(); - - ldapObject.setRdnAttributeName(groupNameAttribute); - ldapObject.setObjectClasses(objectClasses); - ldapObject.setSingleAttribute(groupNameAttribute, groupName); - - LDAPDn roleDn = LDAPDn.fromString(parentDn); - roleDn.addFirst(groupNameAttribute, groupName); - ldapObject.setDn(roleDn); - - for (Map.Entry> attrEntry : additionalAttributes.entrySet()) { - ldapObject.setAttribute(attrEntry.getKey(), attrEntry.getValue()); - } - - ldapProvider.getLdapIdentityStore().add(ldapObject); - return ldapObject; - } - - /** - * Add ldapChild as member of ldapParent and save ldapParent to LDAP. - * - * @param ldapProvider - * @param membershipType how is 'member' attribute saved (full DN or just uid) - * @param memberAttrName usually 'member' - * @param ldapParent role or group - * @param ldapChild usually user (or child group or child role) - * @param sendLDAPUpdateRequest if true, the method will send LDAP update request too. Otherwise it will skip it - */ - public static void addMember(LDAPFederationProvider ldapProvider, MembershipType membershipType, String memberAttrName, LDAPObject ldapParent, LDAPObject ldapChild, boolean sendLDAPUpdateRequest) { - - Set memberships = getExistingMemberships(memberAttrName, ldapParent); - - // Remove membership placeholder if present - if (membershipType == MembershipType.DN) { - for (String membership : memberships) { - if (LDAPConstants.EMPTY_MEMBER_ATTRIBUTE_VALUE.equals(membership)) { - memberships.remove(membership); - break; - } - } - } - - String membership = getMemberValueOfChildObject(ldapChild, membershipType); - - memberships.add(membership); - ldapParent.setAttribute(memberAttrName, memberships); - - if (sendLDAPUpdateRequest) { - ldapProvider.getLdapIdentityStore().update(ldapParent); - } - } - - /** - * Remove ldapChild as member of ldapParent and save ldapParent to LDAP. - * - * @param ldapProvider - * @param membershipType how is 'member' attribute saved (full DN or just uid) - * @param memberAttrName usually 'member' - * @param ldapParent role or group - * @param ldapChild usually user (or child group or child role) - * @param sendLDAPUpdateRequest if true, the method will send LDAP update request too. Otherwise it will skip it - */ - public static void deleteMember(LDAPFederationProvider ldapProvider, MembershipType membershipType, String memberAttrName, LDAPObject ldapParent, LDAPObject ldapChild, boolean sendLDAPUpdateRequest) { - Set memberships = getExistingMemberships(memberAttrName, ldapParent); - - String userMembership = getMemberValueOfChildObject(ldapChild, membershipType); - - memberships.remove(userMembership); - - // Some membership placeholder needs to be always here as "member" is mandatory attribute on some LDAP servers. But not on active directory! (Placeholder, which not matches any real object is not allowed here) - if (memberships.size() == 0 && membershipType== MembershipType.DN && !ldapProvider.getLdapIdentityStore().getConfig().isActiveDirectory()) { - memberships.add(LDAPConstants.EMPTY_MEMBER_ATTRIBUTE_VALUE); - } - - ldapParent.setAttribute(memberAttrName, memberships); - ldapProvider.getLdapIdentityStore().update(ldapParent); - } - - /** - * Return all existing memberships (values of attribute 'member' ) from the given ldapRole or ldapGroup - * - * @param memberAttrName usually 'member' - * @param ldapRole - * @return - */ - public static Set getExistingMemberships(String memberAttrName, LDAPObject ldapRole) { - Set memberships = ldapRole.getAttributeAsSet(memberAttrName); - if (memberships == null) { - memberships = new HashSet<>(); - } - return memberships; - } - - /** - * Get value to be used as attribute 'member' in some parent ldapObject - */ - public static String getMemberValueOfChildObject(LDAPObject ldapUser, MembershipType membershipType) { - return membershipType == MembershipType.DN ? ldapUser.getDn().toString() : ldapUser.getAttributeAsString(ldapUser.getRdnAttributeName()); - } - - - /** - * Load all LDAP objects corresponding to given query. We will load them paginated, so we allow to bypass the limitation of 1000 - * maximum loaded objects in single query in MSAD - * - * @param ldapQuery - * @param ldapProvider - * @return - */ - public static List loadAllLDAPObjects(LDAPQuery ldapQuery, LDAPFederationProvider ldapProvider) { - LDAPConfig ldapConfig = ldapProvider.getLdapIdentityStore().getConfig(); - boolean pagination = ldapConfig.isPagination(); - if (pagination) { - // For now reuse globally configured batch size in LDAP provider page - int pageSize = ldapConfig.getBatchSizeForSync(); - - List result = new LinkedList<>(); - boolean nextPage = true; - - while (nextPage) { - ldapQuery.setLimit(pageSize); - final List currentPageGroups = ldapQuery.getResultList(); - result.addAll(currentPageGroups); - nextPage = ldapQuery.getPaginationContext() != null; - } - - return result; - } else { - // LDAP pagination not available. Do everything in single transaction - return ldapQuery.getResultList(); - } - } - - - /** - * Validate configured customFilter matches the requested format - * - * @param customFilter - * @throws FederationConfigValidationException - */ - public static void validateCustomLdapFilter(String customFilter) throws FederationConfigValidationException { - if (customFilter != null) { - - customFilter = customFilter.trim(); - if (customFilter.isEmpty()) { - return; - } - - if (!customFilter.startsWith("(") || !customFilter.endsWith(")")) { - throw new FederationConfigValidationException("ldapErrorInvalidCustomFilter"); - } - } - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/ReadonlyLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/ReadonlyLDAPUserModelDelegate.java deleted file mode 100755 index 6a839b6698d..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/ReadonlyLDAPUserModelDelegate.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap; - -import org.keycloak.models.ModelReadOnlyException; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.UserModelDelegate; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements UserModel { - - protected LDAPFederationProvider provider; - - public ReadonlyLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) { - super(delegate); - this.provider = provider; - } - - @Override - public void setUsername(String username) { - throw new ModelReadOnlyException("Federated storage is not writable"); - } - - @Override - public void setLastName(String lastName) { - throw new ModelReadOnlyException("Federated storage is not writable"); - } - - @Override - public void setFirstName(String first) { - throw new ModelReadOnlyException("Federated storage is not writable"); - } - - @Override - public void setEmail(String email) { - throw new ModelReadOnlyException("Federated storage is not writable"); - } - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/UnsyncedLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/UnsyncedLDAPUserModelDelegate.java deleted file mode 100755 index 4d116e41349..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/UnsyncedLDAPUserModelDelegate.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap; - -import org.jboss.logging.Logger; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.UserModelDelegate; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class UnsyncedLDAPUserModelDelegate extends UserModelDelegate implements UserModel { - private static final Logger logger = Logger.getLogger(UnsyncedLDAPUserModelDelegate.class); - - protected LDAPFederationProvider provider; - - public UnsyncedLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) { - super(delegate); - this.provider = provider; - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/WritableLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/WritableLDAPUserModelDelegate.java deleted file mode 100755 index 7bc78014cfa..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/WritableLDAPUserModelDelegate.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap; - -import org.jboss.logging.Logger; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.UserModelDelegate; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class WritableLDAPUserModelDelegate extends UserModelDelegate implements UserModel { - private static final Logger logger = Logger.getLogger(WritableLDAPUserModelDelegate.class); - - protected LDAPFederationProvider provider; - protected LDAPObject ldapObject; - - public WritableLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider, LDAPObject ldapObject) { - super(delegate); - this.provider = provider; - this.ldapObject = ldapObject; - } - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPDn.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPDn.java deleted file mode 100644 index bfbce5964a1..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPDn.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.model; - -import javax.naming.ldap.Rdn; -import java.util.Collection; -import java.util.Deque; -import java.util.LinkedList; - -/** - * @author Marek Posolda - */ -public class LDAPDn { - - private final Deque entries = new LinkedList<>(); - - public static LDAPDn fromString(String dnString) { - LDAPDn dn = new LDAPDn(); - - // In certain OpenLDAP implementations the uniqueMember attribute is mandatory - // Thus, if a new group is created, it will contain an empty uniqueMember attribute - // Later on, when adding members, this empty attribute will be kept - // Keycloak must be able to process it, properly, w/o throwing an ArrayIndexOutOfBoundsException - if(dnString.trim().isEmpty()) - return dn; - - String[] rdns = dnString.split("(? entries) { - StringBuilder builder = new StringBuilder(); - - boolean first = true; - for (Entry rdn : entries) { - if (first) { - first = false; - } else { - builder.append(","); - } - builder.append(rdn.attrName).append("=").append(rdn.attrValue); - } - - return builder.toString(); - } - - /** - * @return string like "uid=joe" from the DN like "uid=joe,dc=something,dc=org" - */ - public String getFirstRdn() { - Entry firstEntry = entries.getFirst(); - return firstEntry.attrName + "=" + firstEntry.attrValue; - } - - /** - * @return string attribute name like "uid" from the DN like "uid=joe,dc=something,dc=org" - */ - public String getFirstRdnAttrName() { - Entry firstEntry = entries.getFirst(); - return firstEntry.attrName; - } - - /** - * @return string attribute value like "joe" from the DN like "uid=joe,dc=something,dc=org" - */ - public String getFirstRdnAttrValue() { - Entry firstEntry = entries.getFirst(); - return firstEntry.attrValue; - } - - /** - * - * @return string like "dc=something,dc=org" from the DN like "uid=joe,dc=something,dc=org" - */ - public String getParentDn() { - LinkedList parentDnEntries = new LinkedList<>(entries); - parentDnEntries.remove(); - return toString(parentDnEntries); - } - - public boolean isDescendantOf(LDAPDn expectedParentDn) { - int parentEntriesCount = expectedParentDn.entries.size(); - - Deque myEntries = new LinkedList<>(this.entries); - boolean someRemoved = false; - while (myEntries.size() > parentEntriesCount) { - myEntries.removeFirst(); - someRemoved = true; - } - - String myEntriesParentStr = toString(myEntries).toLowerCase(); - String expectedParentDnStr = expectedParentDn.toString().toLowerCase(); - return someRemoved && myEntriesParentStr.equals(expectedParentDnStr); - } - - public void addFirst(String rdnName, String rdnValue) { - rdnValue = Rdn.escapeValue(rdnValue); - entries.addFirst(new Entry(rdnName, rdnValue)); - } - - private void addLast(String rdnName, String rdnValue) { - entries.addLast(new Entry(rdnName, rdnValue)); - } - - private static class Entry { - private final String attrName; - private final String attrValue; - - private Entry(String attrName, String attrValue) { - this.attrName = attrName; - this.attrValue = attrValue; - } - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPObject.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPObject.java deleted file mode 100644 index 0141a451f75..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/model/LDAPObject.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.model; - -import org.jboss.logging.Logger; - -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @author Marek Posolda - */ -public class LDAPObject { - - private static final Logger logger = Logger.getLogger(LDAPObject.class); - - private String uuid; - private LDAPDn dn; - private String rdnAttributeName; - - private final List objectClasses = new LinkedList<>(); - - // NOTE: names of read-only attributes are lower-cased to avoid case sensitivity issues - private final List readOnlyAttributeNames = new LinkedList<>(); - - private final Map> attributes = new HashMap<>(); - - // Copy of "attributes" containing lower-cased keys - private final Map> lowerCasedAttributes = new HashMap<>(); - - - public String getUuid() { - return uuid; - } - - public void setUuid(String uuid) { - this.uuid = uuid; - } - - public LDAPDn getDn() { - return dn; - } - - public void setDn(LDAPDn dn) { - this.dn = dn; - } - - public List getObjectClasses() { - return objectClasses; - } - - public void setObjectClasses(Collection objectClasses) { - this.objectClasses.clear(); - this.objectClasses.addAll(objectClasses); - } - - public List getReadOnlyAttributeNames() { - return readOnlyAttributeNames; - } - - public void addReadOnlyAttributeName(String readOnlyAttribute) { - readOnlyAttributeNames.add(readOnlyAttribute.toLowerCase()); - } - - public void removeReadOnlyAttributeName(String readOnlyAttribute) { - readOnlyAttributeNames.remove(readOnlyAttribute.toLowerCase()); - } - - public String getRdnAttributeName() { - return rdnAttributeName; - } - - public void setRdnAttributeName(String rdnAttributeName) { - this.rdnAttributeName = rdnAttributeName; - } - - public void setSingleAttribute(String attributeName, String attributeValue) { - Set asSet = new LinkedHashSet<>(); - asSet.add(attributeValue); - setAttribute(attributeName, asSet); - } - - public void setAttribute(String attributeName, Set attributeValue) { - attributes.put(attributeName, attributeValue); - lowerCasedAttributes.put(attributeName.toLowerCase(), attributeValue); - } - - // Case-insensitive - public String getAttributeAsString(String name) { - Set attrValue = lowerCasedAttributes.get(name.toLowerCase()); - if (attrValue == null || attrValue.size() == 0) { - return null; - } else if (attrValue.size() > 1) { - logger.warnf("Expected String but attribute '%s' has more values '%s' on object '%s' . Returning just first value", name, attrValue, dn); - } - - return attrValue.iterator().next(); - } - - // Case-insensitive. Return null if there is not value of attribute with given name or set with all values otherwise - public Set getAttributeAsSet(String name) { - Set values = lowerCasedAttributes.get(name.toLowerCase()); - return (values == null) ? null : new LinkedHashSet<>(values); - } - - - public Map> getAttributes() { - return attributes; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - - if (!getClass().isInstance(obj)) { - return false; - } - - LDAPObject other = (LDAPObject) obj; - - return getUuid() != null && other.getUuid() != null && getUuid().equals(other.getUuid()); - } - - @Override - public int hashCode() { - int result = getUuid() != null ? getUuid().hashCode() : 0; - result = 31 * result + (getUuid() != null ? getUuid().hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "LDAP Object [ dn: " + dn + " , uuid: " + uuid + ", attributes: " + attributes + ", readOnly attribute names: " + readOnlyAttributeNames + " ]"; - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/Condition.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/Condition.java deleted file mode 100644 index a8d3f071823..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/Condition.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query; - -/** - *

A {@link Condition} is used to specify how a specific query parameter - * is defined in order to filter query results.

- * - * @author Pedro Igor - */ -public interface Condition { - - String getParameterName(); - void setParameterName(String parameterName); - - /** - * Will change the parameter name if it is "modelParamName" to "ldapParamName" . Implementation can apply this to subconditions as well. - * - * It is used to update LDAP queries, which were created with model parameter name ( for example "firstName" ) and rewrite them to use real - * LDAP mapped attribute (for example "givenName" ) - */ - void updateParameterName(String modelParamName, String ldapParamName); - - - void applyCondition(StringBuilder filter); - -} \ No newline at end of file diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/Sort.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/Sort.java deleted file mode 100644 index da982726c30..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/Sort.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query; - -/** - * @author Pedro Igor - */ -public class Sort { - - private final String paramName; - private final boolean asc; - - public Sort(String paramName, boolean asc) { - this.paramName = paramName; - this.asc = asc; - } - - public String getParameter() { - return this.paramName; - } - - public boolean isAscending() { - return asc; - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/BetweenCondition.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/BetweenCondition.java deleted file mode 100644 index ba926e9310e..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/BetweenCondition.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.federation.ldap.idm.store.ldap.LDAPUtil; - -import java.util.Date; - -/** - * @author Pedro Igor - */ -class BetweenCondition extends NamedParameterCondition { - - private final Comparable x; - private final Comparable y; - - public BetweenCondition(String name, Comparable x, Comparable y) { - super(name); - this.x = x; - this.y = y; - } - - @Override - public void applyCondition(StringBuilder filter) { - Comparable x = this.x; - Comparable y = this.y; - - if (Date.class.isInstance(x)) { - x = LDAPUtil.formatDate((Date) x); - } - - if (Date.class.isInstance(y)) { - y = LDAPUtil.formatDate((Date) y); - } - - filter.append("(").append(x).append("<=").append(getParameterName()).append("<=").append(y).append(")"); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/CustomLDAPFilter.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/CustomLDAPFilter.java deleted file mode 100644 index 45b3b1d6818..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/CustomLDAPFilter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.federation.ldap.idm.query.Condition; - -/** - * @author Marek Posolda - */ -class CustomLDAPFilter implements Condition { - - private final String customFilter; - - public CustomLDAPFilter(String customFilter) { - this.customFilter = customFilter; - } - - @Override - public String getParameterName() { - return null; - } - - @Override - public void setParameterName(String parameterName) { - } - - @Override - public void updateParameterName(String modelParamName, String ldapParamName) { - - } - - @Override - public void applyCondition(StringBuilder filter) { - filter.append(customFilter); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/EqualCondition.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/EqualCondition.java deleted file mode 100644 index 115e11f4da1..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/EqualCondition.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.federation.ldap.idm.store.ldap.LDAPUtil; -import org.keycloak.models.LDAPConstants; - -import java.util.Date; - -/** - * @author Pedro Igor - */ -public class EqualCondition extends NamedParameterCondition { - - private final Object value; - - public EqualCondition(String name, Object value) { - super(name); - this.value = value; - } - - public Object getValue() { - return this.value; - } - - @Override - public void applyCondition(StringBuilder filter) { - Object parameterValue = value; - if (Date.class.isInstance(value)) { - parameterValue = LDAPUtil.formatDate((Date) parameterValue); - } - - filter.append("(").append(getParameterName()).append(LDAPConstants.EQUAL).append(parameterValue).append(")"); - } - - @Override - public String toString() { - return "EqualCondition{" + - "paramName=" + getParameterName() + - ", value=" + value + - '}'; - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/GreaterThanCondition.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/GreaterThanCondition.java deleted file mode 100644 index 3ef65357196..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/GreaterThanCondition.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.federation.ldap.idm.store.ldap.LDAPUtil; - -import java.util.Date; - -/** - * @author Pedro Igor - */ -class GreaterThanCondition extends NamedParameterCondition { - - private final boolean orEqual; - - private final Comparable value; - - public GreaterThanCondition(String name, Comparable value, boolean orEqual) { - super(name); - this.value = value; - this.orEqual = orEqual; - } - - @Override - public void applyCondition(StringBuilder filter) { - Comparable parameterValue = value; - - if (Date.class.isInstance(parameterValue)) { - parameterValue = LDAPUtil.formatDate((Date) parameterValue); - } - - if (orEqual) { - filter.append("(").append(getParameterName()).append(">=").append(parameterValue).append(")"); - } else { - filter.append("(").append(getParameterName()).append(">").append(parameterValue).append(")"); - } - } -} \ No newline at end of file diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/InCondition.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/InCondition.java deleted file mode 100644 index ebecd54cf6a..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/InCondition.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.models.LDAPConstants; - -/** - * @author Pedro Igor - */ -class InCondition extends NamedParameterCondition { - - private final Object[] valuesToCompare; - - public InCondition(String name, Object[] valuesToCompare) { - super(name); - this.valuesToCompare = valuesToCompare; - } - - @Override - public void applyCondition(StringBuilder filter) { - - filter.append("(&("); - - for (int i = 0; i< valuesToCompare.length; i++) { - Object value = valuesToCompare[i]; - - filter.append("(").append(getParameterName()).append(LDAPConstants.EQUAL).append(value).append(")"); - } - - filter.append("))"); - } -} - diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQuery.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQuery.java deleted file mode 100644 index 5a57f84f3df..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQuery.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.Sort; -import org.keycloak.federation.ldap.mappers.LDAPFederationMapper; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.ModelException; -import org.keycloak.models.UserFederationMapperModel; - -import javax.naming.directory.SearchControls; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import static java.util.Collections.unmodifiableSet; - -/** - * Default IdentityQuery implementation. - * - * - * @author Shane Bryzak - */ -public class LDAPQuery { - - private final LDAPFederationProvider ldapFedProvider; - - private int offset; - private int limit; - private byte[] paginationContext; - private String searchDn; - private final Set conditions = new LinkedHashSet(); - private final Set ordering = new LinkedHashSet(); - - private final Set returningLdapAttributes = new LinkedHashSet(); - - // Contains just those returningLdapAttributes, which are read-only. They will be marked as read-only in returned LDAPObject instances as well - // NOTE: names of attributes are lower-cased to avoid case sensitivity issues (LDAP searching is usually case-insensitive, so we want to be as well) - private final Set returningReadOnlyLdapAttributes = new LinkedHashSet(); - private final Set objectClasses = new LinkedHashSet(); - - private final List mappers = new ArrayList(); - - private int searchScope = SearchControls.SUBTREE_SCOPE; - - public LDAPQuery(LDAPFederationProvider ldapProvider) { - this.ldapFedProvider = ldapProvider; - } - - public LDAPQuery addWhereCondition(Condition... condition) { - this.conditions.addAll(Arrays.asList(condition)); - return this; - } - - public LDAPQuery sortBy(Sort... sorts) { - this.ordering.addAll(Arrays.asList(sorts)); - return this; - } - - public LDAPQuery setSearchDn(String searchDn) { - this.searchDn = searchDn; - return this; - } - - public LDAPQuery addObjectClasses(Collection objectClasses) { - this.objectClasses.addAll(objectClasses); - return this; - } - - public LDAPQuery addReturningLdapAttribute(String ldapAttributeName) { - this.returningLdapAttributes.add(ldapAttributeName); - return this; - } - - public LDAPQuery addReturningReadOnlyLdapAttribute(String ldapAttributeName) { - this.returningReadOnlyLdapAttributes.add(ldapAttributeName.toLowerCase()); - return this; - } - - public LDAPQuery addMappers(Collection mappers) { - this.mappers.addAll(mappers); - return this; - } - - public LDAPQuery setSearchScope(int searchScope) { - this.searchScope = searchScope; - return this; - } - - public Set getSorting() { - return unmodifiableSet(this.ordering); - } - - public String getSearchDn() { - return this.searchDn; - } - - public Set getObjectClasses() { - return unmodifiableSet(this.objectClasses); - } - - public Set getReturningLdapAttributes() { - return unmodifiableSet(this.returningLdapAttributes); - } - - public Set getReturningReadOnlyLdapAttributes() { - return unmodifiableSet(this.returningReadOnlyLdapAttributes); - } - - public List getMappers() { - return mappers; - } - - public int getSearchScope() { - return searchScope; - } - - public int getLimit() { - return limit; - } - - public int getOffset() { - return offset; - } - - public byte[] getPaginationContext() { - return paginationContext; - } - - - public List getResultList() { - - // Apply mappers now - List sortedMappers = ldapFedProvider.sortMappersAsc(mappers); - for (UserFederationMapperModel mapperModel : sortedMappers) { - LDAPFederationMapper fedMapper = ldapFedProvider.getMapper(mapperModel); - fedMapper.beforeLDAPQuery(mapperModel, this); - } - - List result = new ArrayList(); - - try { - for (LDAPObject ldapObject : ldapFedProvider.getLdapIdentityStore().fetchQueryResults(this)) { - result.add(ldapObject); - } - } catch (Exception e) { - throw new ModelException("LDAP Query failed", e); - } - - return result; - } - - public LDAPObject getFirstResult() { - List results = getResultList(); - - if (results.isEmpty()) { - return null; - } else if (results.size() == 1) { - return results.get(0); - } else { - throw new ModelDuplicateException("Error - multiple LDAP objects found but expected just one"); - } - } - - public int getResultCount() { - return ldapFedProvider.getLdapIdentityStore().countQueryResults(this); - } - - public LDAPQuery setOffset(int offset) { - this.offset = offset; - return this; - } - - public LDAPQuery setLimit(int limit) { - this.limit = limit; - return this; - } - - public LDAPQuery setPaginationContext(byte[] paginationContext) { - this.paginationContext = paginationContext; - return this; - } - - public Set getConditions() { - return this.conditions; - } - - public LDAPFederationProvider getLdapProvider() { - return ldapFedProvider; - } - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java deleted file mode 100644 index 4be4ba41c57..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.Sort; -import org.keycloak.models.ModelException; - -/** - * @author Pedro Igor - */ -public class LDAPQueryConditionsBuilder { - - public Condition equal(String parameter, Object value) { - return new EqualCondition(parameter, value); - } - - public Condition greaterThan(String paramName, Object x) { - throwExceptionIfNotComparable(x); - return new GreaterThanCondition(paramName, (Comparable) x, false); - } - - public Condition greaterThanOrEqualTo(String paramName, Object x) { - throwExceptionIfNotComparable(x); - return new GreaterThanCondition(paramName, (Comparable) x, true); - } - - public Condition lessThan(String paramName, Comparable x) { - return new LessThanCondition(paramName, x, false); - } - - public Condition lessThanOrEqualTo(String paramName, Comparable x) { - return new LessThanCondition(paramName, x, true); - } - - public Condition between(String paramName, Comparable x, Comparable y) { - return new BetweenCondition(paramName, x, y); - } - - public Condition orCondition(Condition... conditions) { - if (conditions == null || conditions.length == 0) { - throw new ModelException("At least one condition should be provided to OR query"); - } - return new OrCondition(conditions); - } - - public Condition addCustomLDAPFilter(String filter) { - filter = filter.trim(); - return new CustomLDAPFilter(filter); - } - - public Condition in(String paramName, Object... x) { - return new InCondition(paramName, x); - } - - public Sort asc(String paramName) { - return new Sort(paramName, true); - } - - public Sort desc(String paramName) { - return new Sort(paramName, false); - } - - private void throwExceptionIfNotComparable(Object x) { - if (!Comparable.class.isInstance(x)) { - throw new ModelException("Query parameter value [" + x + "] must be " + Comparable.class + "."); - } - } -} \ No newline at end of file diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LessThanCondition.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LessThanCondition.java deleted file mode 100644 index e29b014125b..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LessThanCondition.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.federation.ldap.idm.store.ldap.LDAPUtil; - -import java.util.Date; - -/** - * @author Pedro Igor - */ -class LessThanCondition extends NamedParameterCondition { - - private final boolean orEqual; - - private final Comparable value; - - public LessThanCondition(String name, Comparable value, boolean orEqual) { - super(name); - this.value = value; - this.orEqual = orEqual; - } - - @Override - public void applyCondition(StringBuilder filter) { - Comparable parameterValue = value; - - if (Date.class.isInstance(parameterValue)) { - parameterValue = LDAPUtil.formatDate((Date) parameterValue); - } - - if (orEqual) { - filter.append("(").append(getParameterName()).append("<=").append(parameterValue).append(")"); - } else { - filter.append("(").append(getParameterName()).append("<").append(parameterValue).append(")"); - } - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/NamedParameterCondition.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/NamedParameterCondition.java deleted file mode 100644 index 50cb7723be1..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/NamedParameterCondition.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.federation.ldap.idm.query.Condition; - -/** - * @author Marek Posolda - */ -public abstract class NamedParameterCondition implements Condition { - - private String parameterName; - - public NamedParameterCondition(String parameterName) { - this.parameterName = parameterName; - } - - @Override - public String getParameterName() { - return parameterName; - } - - @Override - public void setParameterName(String parameterName) { - this.parameterName = parameterName; - } - - - @Override - public void updateParameterName(String modelParamName, String ldapParamName) { - if (parameterName.equalsIgnoreCase(modelParamName)) { - this.parameterName = ldapParamName; - } - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/OrCondition.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/OrCondition.java deleted file mode 100644 index 7f0e93f211a..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/OrCondition.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.query.internal; - -import org.keycloak.federation.ldap.idm.query.Condition; - -/** - * @author Marek Posolda - */ -class OrCondition implements Condition { - - private final Condition[] innerConditions; - - public OrCondition(Condition... innerConditions) { - this.innerConditions = innerConditions; - } - - @Override - public String getParameterName() { - return null; - } - - @Override - public void setParameterName(String parameterName) { - } - - @Override - public void updateParameterName(String modelParamName, String ldapParamName) { - for (Condition innerCondition : innerConditions) { - innerCondition.updateParameterName(modelParamName, ldapParamName); - } - } - - @Override - public void applyCondition(StringBuilder filter) { - filter.append("(|"); - - for (Condition innerCondition : innerConditions) { - innerCondition.applyCondition(filter); - } - - filter.append(")"); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/IdentityStore.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/IdentityStore.java deleted file mode 100644 index c14f4f03adf..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/IdentityStore.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.store; - -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; - -import javax.naming.AuthenticationException; -import java.util.List; - -/** - * IdentityStore representation providing minimal SPI - * - * TODO: Rather remove this abstraction - * - * @author Boleslaw Dawidowicz - * @author Shane Bryzak - */ -public interface IdentityStore { - - /** - * Returns the configuration for this IdentityStore instance - * - * @return - */ - LDAPConfig getConfig(); - - // General - - /** - * Persists the specified IdentityType - * - * @param ldapObject - */ - void add(LDAPObject ldapObject); - - /** - * Updates the specified IdentityType - * - * @param ldapObject - */ - void update(LDAPObject ldapObject); - - /** - * Removes the specified IdentityType - * - * @param ldapObject - */ - void remove(LDAPObject ldapObject); - - // Identity query - - List fetchQueryResults(LDAPQuery LDAPQuery); - - int countQueryResults(LDAPQuery LDAPQuery); - -// // Relationship query -// -// List fetchQueryResults(RelationshipQuery query); -// -// int countQueryResults(RelationshipQuery query); - - // Credentials - - /** - * Validates the specified credentials. - * - * @param user Keycloak user - * @param password Ldap password - * @throws AuthenticationException if authentication is not successful - */ - void validatePassword(LDAPObject user, String password) throws AuthenticationException; - - /** - * Updates the specified credential value. - * - * @param user Keycloak user - * @param password Ldap password - */ - void updatePassword(LDAPObject user, String password); - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java deleted file mode 100644 index 367fb04d57b..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.store.ldap; - -import org.jboss.logging.Logger; -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.federation.ldap.idm.model.LDAPDn; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.EqualCondition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.federation.ldap.idm.store.IdentityStore; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelException; - -import javax.naming.AuthenticationException; -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.ModificationItem; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.TreeSet; - -/** - * An IdentityStore implementation backed by an LDAP directory - * - * @author Shane Bryzak - * @author Anil Saldhana - * @author Pedro Silva - */ -public class LDAPIdentityStore implements IdentityStore { - - private static final Logger logger = Logger.getLogger(LDAPIdentityStore.class); - - private final LDAPConfig config; - private final LDAPOperationManager operationManager; - - public LDAPIdentityStore(LDAPConfig config) { - this.config = config; - - try { - this.operationManager = new LDAPOperationManager(config); - } catch (NamingException e) { - throw new ModelException("Couldn't init operation manager", e); - } - } - - @Override - public LDAPConfig getConfig() { - return this.config; - } - - @Override - public void add(LDAPObject ldapObject) { - // id will be assigned by the ldap server - if (ldapObject.getUuid() != null) { - throw new ModelException("Can't add object with already assigned uuid"); - } - - String entryDN = ldapObject.getDn().toString(); - BasicAttributes ldapAttributes = extractAttributes(ldapObject, true); - this.operationManager.createSubContext(entryDN, ldapAttributes); - ldapObject.setUuid(getEntryIdentifier(ldapObject)); - - if (logger.isDebugEnabled()) { - logger.debugf("Type with identifier [%s] and dn [%s] successfully added to LDAP store.", ldapObject.getUuid(), entryDN); - } - } - - @Override - public void update(LDAPObject ldapObject) { - BasicAttributes updatedAttributes = extractAttributes(ldapObject, false); - NamingEnumeration attributes = updatedAttributes.getAll(); - - String entryDn = ldapObject.getDn().toString(); - this.operationManager.modifyAttributes(entryDn, attributes); - - if (logger.isDebugEnabled()) { - logger.debugf("Type with identifier [%s] and DN [%s] successfully updated to LDAP store.", ldapObject.getUuid(), entryDn); - } - } - - @Override - public void remove(LDAPObject ldapObject) { - this.operationManager.removeEntry(ldapObject.getDn().toString()); - - if (logger.isDebugEnabled()) { - logger.debugf("Type with identifier [%s] and DN [%s] successfully removed from LDAP store.", ldapObject.getUuid(), ldapObject.getDn().toString()); - } - } - - - @Override - public List fetchQueryResults(LDAPQuery identityQuery) { - if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) { - throw new ModelException("LDAP Identity Store does not yet support sorted queries."); - } - - List results = new ArrayList<>(); - - try { - String baseDN = identityQuery.getSearchDn(); - - for (Condition condition : identityQuery.getConditions()) { - - // Check if we are searching by ID - String uuidAttrName = getConfig().getUuidLDAPAttributeName(); - if (condition instanceof EqualCondition) { - EqualCondition equalCondition = (EqualCondition) condition; - if (equalCondition.getParameterName().equalsIgnoreCase(uuidAttrName)) { - SearchResult search = this.operationManager - .lookupById(baseDN, equalCondition.getValue().toString(), identityQuery.getReturningLdapAttributes()); - - if (search != null) { - results.add(populateAttributedType(search, identityQuery)); - } - - return results; - } - } - } - - - StringBuilder filter = createIdentityTypeSearchFilter(identityQuery); - - List search; - if (getConfig().isPagination() && identityQuery.getLimit() > 0) { - search = this.operationManager.searchPaginated(baseDN, filter.toString(), identityQuery); - } else { - search = this.operationManager.search(baseDN, filter.toString(), identityQuery.getReturningLdapAttributes(), identityQuery.getSearchScope()); - } - - for (SearchResult result : search) { - if (!result.getNameInNamespace().equalsIgnoreCase(baseDN)) { - results.add(populateAttributedType(result, identityQuery)); - } - } - } catch (Exception e) { - throw new ModelException("Querying of LDAP failed " + identityQuery, e); - } - - return results; - } - - @Override - public int countQueryResults(LDAPQuery identityQuery) { - int limit = identityQuery.getLimit(); - int offset = identityQuery.getOffset(); - - identityQuery.setLimit(0); - identityQuery.setOffset(0); - - int resultCount = identityQuery.getResultList().size(); - - identityQuery.setLimit(limit); - identityQuery.setOffset(offset); - - return resultCount; - } - - // *************** CREDENTIALS AND USER SPECIFIC STUFF - - @Override - public void validatePassword(LDAPObject user, String password) throws AuthenticationException { - String userDN = user.getDn().toString(); - - if (logger.isTraceEnabled()) { - logger.tracef("Using DN [%s] for authentication of user", userDN); - } - - operationManager.authenticate(userDN, password); - } - - @Override - public void updatePassword(LDAPObject user, String password) { - String userDN = user.getDn().toString(); - - if (logger.isDebugEnabled()) { - logger.debugf("Using DN [%s] for updating LDAP password of user", userDN); - } - - if (getConfig().isActiveDirectory()) { - updateADPassword(userDN, password); - } else { - ModificationItem[] mods = new ModificationItem[1]; - - try { - BasicAttribute mod0 = new BasicAttribute(LDAPConstants.USER_PASSWORD_ATTRIBUTE, password); - - mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0); - - operationManager.modifyAttribute(userDN, mod0); - } catch (ModelException me) { - throw me; - } catch (Exception e) { - throw new ModelException("Error updating password.", e); - } - } - } - - - private void updateADPassword(String userDN, String password) { - try { - // Replace the "unicdodePwd" attribute with a new value - // Password must be both Unicode and a quoted string - String newQuotedPassword = "\"" + password + "\""; - byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE"); - - BasicAttribute unicodePwd = new BasicAttribute("unicodePwd", newUnicodePassword); - - List modItems = new ArrayList(); - modItems.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, unicodePwd)); - - operationManager.modifyAttributes(userDN, modItems.toArray(new ModificationItem[] {})); - } catch (ModelException me) { - throw me; - } catch (Exception e) { - throw new ModelException(e); - } - } - - // ************ END CREDENTIALS AND USER SPECIFIC STUFF - - protected StringBuilder createIdentityTypeSearchFilter(final LDAPQuery identityQuery) { - StringBuilder filter = new StringBuilder(); - - for (Condition condition : identityQuery.getConditions()) { - condition.applyCondition(filter); - } - - filter.insert(0, "(&"); - filter.append(getObjectClassesFilter(identityQuery.getObjectClasses())); - filter.append(")"); - - if (logger.isTraceEnabled()) { - logger.tracef("Using filter for LDAP search: %s . Searching in DN: %s", filter, identityQuery.getSearchDn()); - } - return filter; - } - - - private StringBuilder getObjectClassesFilter(Collection objectClasses) { - StringBuilder builder = new StringBuilder(); - - if (!objectClasses.isEmpty()) { - for (String objectClass : objectClasses) { - builder.append("(").append(LDAPConstants.OBJECT_CLASS).append(LDAPConstants.EQUAL).append(objectClass).append(")"); - } - } else { - builder.append("(").append(LDAPConstants.OBJECT_CLASS).append(LDAPConstants.EQUAL).append("*").append(")"); - } - - return builder; - } - - - private LDAPObject populateAttributedType(SearchResult searchResult, LDAPQuery ldapQuery) { - Set readOnlyAttrNames = ldapQuery.getReturningReadOnlyLdapAttributes(); - Set lowerCasedAttrNames = new TreeSet<>(); - for (String attrName : ldapQuery.getReturningLdapAttributes()) { - lowerCasedAttrNames.add(attrName.toLowerCase()); - } - - try { - String entryDN = searchResult.getNameInNamespace(); - Attributes attributes = searchResult.getAttributes(); - - LDAPObject ldapObject = new LDAPObject(); - LDAPDn dn = LDAPDn.fromString(entryDN); - ldapObject.setDn(dn); - ldapObject.setRdnAttributeName(dn.getFirstRdnAttrName()); - - NamingEnumeration ldapAttributes = attributes.getAll(); - - while (ldapAttributes.hasMore()) { - Attribute ldapAttribute = ldapAttributes.next(); - - try { - ldapAttribute.get(); - } catch (NoSuchElementException nsee) { - continue; - } - - String ldapAttributeName = ldapAttribute.getID(); - - if (ldapAttributeName.equalsIgnoreCase(getConfig().getUuidLDAPAttributeName())) { - Object uuidValue = ldapAttribute.get(); - ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue)); - } - - // Note: UUID is normally not populated here. It's populated just in case that it's used for name of other attribute as well - if (!ldapAttributeName.equalsIgnoreCase(getConfig().getUuidLDAPAttributeName()) || (lowerCasedAttrNames.contains(ldapAttributeName.toLowerCase()))) { - Set attrValues = new LinkedHashSet<>(); - NamingEnumeration enumm = ldapAttribute.getAll(); - while (enumm.hasMoreElements()) { - String attrVal = enumm.next().toString().trim(); - attrValues.add(attrVal); - } - - if (ldapAttributeName.equalsIgnoreCase(LDAPConstants.OBJECT_CLASS)) { - ldapObject.setObjectClasses(attrValues); - } else { - ldapObject.setAttribute(ldapAttributeName, attrValues); - - // readOnlyAttrNames are lower-cased - if (readOnlyAttrNames.contains(ldapAttributeName.toLowerCase())) { - ldapObject.addReadOnlyAttributeName(ldapAttributeName); - } - } - } - } - - if (logger.isTraceEnabled()) { - logger.tracef("Found ldap object and populated with the attributes. LDAP Object: %s", ldapObject.toString()); - } - return ldapObject; - - } catch (Exception e) { - throw new ModelException("Could not populate attribute type " + searchResult.getNameInNamespace() + ".", e); - } - } - - - protected BasicAttributes extractAttributes(LDAPObject ldapObject, boolean isCreate) { - BasicAttributes entryAttributes = new BasicAttributes(); - - for (Map.Entry> attrEntry : ldapObject.getAttributes().entrySet()) { - String attrName = attrEntry.getKey(); - Set attrValue = attrEntry.getValue(); - - // ldapObject.getReadOnlyAttributeNames() are lower-cased - if (!ldapObject.getReadOnlyAttributeNames().contains(attrName.toLowerCase()) && (isCreate || !ldapObject.getRdnAttributeName().equalsIgnoreCase(attrName))) { - - if (attrValue == null) { - // Shouldn't happen - logger.warnf("Attribute '%s' is null on LDAP object '%s' . Using empty value to be saved to LDAP", attrName, ldapObject.getDn().toString()); - attrValue = Collections.emptySet(); - } - - // Ignore empty attributes during create - if (isCreate && attrValue.isEmpty()) { - continue; - } - - BasicAttribute attr = new BasicAttribute(attrName); - for (String val : attrValue) { - if (val == null || val.toString().trim().length() == 0) { - val = LDAPConstants.EMPTY_ATTRIBUTE_VALUE; - } - attr.add(val); - } - - entryAttributes.put(attr); - } - } - - // Don't extract object classes for update - if (isCreate) { - BasicAttribute objectClassAttribute = new BasicAttribute(LDAPConstants.OBJECT_CLASS); - - for (String objectClassValue : ldapObject.getObjectClasses()) { - objectClassAttribute.add(objectClassValue); - - if (objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_NAMES) - || objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_ENTRIES) - || objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_UNIQUE_NAMES)) { - entryAttributes.put(LDAPConstants.MEMBER, LDAPConstants.EMPTY_MEMBER_ATTRIBUTE_VALUE); - } - } - - entryAttributes.put(objectClassAttribute); - } - - return entryAttributes; - } - - - protected String getEntryIdentifier(final LDAPObject ldapObject) { - try { - // we need this to retrieve the entry's identifier from the ldap server - String uuidAttrName = getConfig().getUuidLDAPAttributeName(); - List search = this.operationManager.search(ldapObject.getDn().toString(), "(" + ldapObject.getDn().getFirstRdn() + ")", Arrays.asList(uuidAttrName), SearchControls.OBJECT_SCOPE); - Attribute id = search.get(0).getAttributes().get(getConfig().getUuidLDAPAttributeName()); - - if (id == null) { - throw new ModelException("Could not retrieve identifier for entry [" + ldapObject.getDn().toString() + "]."); - } - - return this.operationManager.decodeEntryUUID(id.get()); - } catch (NamingException ne) { - throw new ModelException("Could not retrieve identifier for entry [" + ldapObject.getDn().toString() + "]."); - } - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java deleted file mode 100644 index 02cd122b21c..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.store.ldap; - -import org.jboss.logging.Logger; -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelException; - -import javax.naming.AuthenticationException; -import javax.naming.Binding; -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.ModificationItem; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; -import javax.naming.ldap.Control; -import javax.naming.ldap.InitialLdapContext; -import javax.naming.ldap.LdapContext; -import javax.naming.ldap.PagedResultsControl; -import javax.naming.ldap.PagedResultsResponseControl; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -/** - *

This class provides a set of operations to manage LDAP trees.

- * - * @author Anil Saldhana - * @author Pedro Silva - */ -public class LDAPOperationManager { - - private static final Logger logger = Logger.getLogger(LDAPOperationManager.class); - - private final LDAPConfig config; - private final Map connectionProperties; - - public LDAPOperationManager(LDAPConfig config) throws NamingException { - this.config = config; - this.connectionProperties = Collections.unmodifiableMap(createConnectionProperties()); - } - - /** - *

- * Modifies the given {@link javax.naming.directory.Attribute} instance using the given DN. This method performs a REPLACE_ATTRIBUTE - * operation. - *

- * - * @param dn - * @param attribute - */ - public void modifyAttribute(String dn, Attribute attribute) { - ModificationItem[] mods = new ModificationItem[]{new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attribute)}; - modifyAttributes(dn, mods); - } - - /** - *

- * Modifies the given {@link Attribute} instances using the given DN. This method performs a REPLACE_ATTRIBUTE - * operation. - *

- * - * @param dn - * @param attributes - */ - public void modifyAttributes(String dn, NamingEnumeration attributes) { - try { - List modItems = new ArrayList(); - while (attributes.hasMore()) { - ModificationItem modItem = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attributes.next()); - modItems.add(modItem); - } - - modifyAttributes(dn, modItems.toArray(new ModificationItem[] {})); - } catch (NamingException ne) { - throw new ModelException("Could not modify attributes on entry from DN [" + dn + "]", ne); - } - - } - - /** - *

- * Removes the given {@link Attribute} instance using the given DN. This method performs a REMOVE_ATTRIBUTE - * operation. - *

- * - * @param dn - * @param attribute - */ - public void removeAttribute(String dn, Attribute attribute) { - ModificationItem[] mods = new ModificationItem[]{new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attribute)}; - modifyAttributes(dn, mods); - } - - /** - *

- * Adds the given {@link Attribute} instance using the given DN. This method performs a ADD_ATTRIBUTE operation. - *

- * - * @param dn - * @param attribute - */ - public void addAttribute(String dn, Attribute attribute) { - ModificationItem[] mods = new ModificationItem[]{new ModificationItem(DirContext.ADD_ATTRIBUTE, attribute)}; - modifyAttributes(dn, mods); - } - - /** - *

- * Removes the object from the LDAP tree - *

- */ - public void removeEntry(final String entryDn) { - try { - execute(new LdapOperation() { - @Override - public SearchResult execute(LdapContext context) throws NamingException { - if (logger.isTraceEnabled()) { - logger.tracef("Removing entry with DN [%s]", entryDn); - } - destroySubcontext(context, entryDn); - return null; - } - }); - } catch (NamingException e) { - throw new ModelException("Could not remove entry from DN [" + entryDn + "]", e); - } - } - - public List search(final String baseDN, final String filter, Collection returningAttributes, int searchScope) throws NamingException { - final List result = new ArrayList(); - final SearchControls cons = getSearchControls(returningAttributes, searchScope); - - try { - return execute(new LdapOperation>() { - @Override - public List execute(LdapContext context) throws NamingException { - NamingEnumeration search = context.search(baseDN, filter, cons); - - while (search.hasMoreElements()) { - result.add(search.nextElement()); - } - - search.close(); - - return result; - } - }); - } catch (NamingException e) { - logger.errorf(e, "Could not query server using DN [%s] and filter [%s]", baseDN, filter); - throw e; - } - } - - public List searchPaginated(final String baseDN, final String filter, final LDAPQuery identityQuery) throws NamingException { - final List result = new ArrayList(); - final SearchControls cons = getSearchControls(identityQuery.getReturningLdapAttributes(), identityQuery.getSearchScope()); - - try { - return execute(new LdapOperation>() { - @Override - public List execute(LdapContext context) throws NamingException { - try { - byte[] cookie = identityQuery.getPaginationContext(); - PagedResultsControl pagedControls = new PagedResultsControl(identityQuery.getLimit(), cookie, Control.CRITICAL); - context.setRequestControls(new Control[] { pagedControls }); - - NamingEnumeration search = context.search(baseDN, filter, cons); - - while (search.hasMoreElements()) { - result.add(search.nextElement()); - } - - search.close(); - - Control[] responseControls = context.getResponseControls(); - if (responseControls != null) { - for (Control respControl : responseControls) { - if (respControl instanceof PagedResultsResponseControl) { - PagedResultsResponseControl prrc = (PagedResultsResponseControl)respControl; - cookie = prrc.getCookie(); - identityQuery.setPaginationContext(cookie); - } - } - } - - return result; - } catch (IOException ioe) { - logger.errorf(ioe, "Could not query server with paginated query using DN [%s], filter [%s]", baseDN, filter); - throw new NamingException(ioe.getMessage()); - } - } - }); - } catch (NamingException e) { - logger.errorf(e, "Could not query server using DN [%s] and filter [%s]", baseDN, filter); - throw e; - } - } - - private SearchControls getSearchControls(Collection returningAttributes, int searchScope) { - final SearchControls cons = new SearchControls(); - - cons.setSearchScope(searchScope); - cons.setReturningObjFlag(false); - - returningAttributes = getReturningAttributes(returningAttributes); - - cons.setReturningAttributes(returningAttributes.toArray(new String[returningAttributes.size()])); - return cons; - } - - public String getFilterById(String id) { - String filter = null; - - if (this.config.isActiveDirectory()) { - final String strObjectGUID = ""; - - try { - Attributes attributes = execute(new LdapOperation() { - @Override - public Attributes execute(LdapContext context) throws NamingException { - return context.getAttributes(strObjectGUID); - } - }); - - byte[] objectGUID = (byte[]) attributes.get(LDAPConstants.OBJECT_GUID).get(); - - filter = "(&(objectClass=*)(" + getUuidAttributeName() + LDAPConstants.EQUAL + LDAPUtil.convertObjectGUIToByteString(objectGUID) + "))"; - } catch (NamingException ne) { - filter = null; - } - } - - if (filter == null) { - filter = "(&(objectClass=*)(" + getUuidAttributeName() + LDAPConstants.EQUAL + id + "))"; - } - - return filter; - } - - public SearchResult lookupById(final String baseDN, final String id, final Collection returningAttributes) { - final String filter = getFilterById(id); - - try { - final SearchControls cons = getSearchControls(returningAttributes, this.config.getSearchScope()); - - return execute(new LdapOperation() { - @Override - public SearchResult execute(LdapContext context) throws NamingException { - NamingEnumeration search = context.search(baseDN, filter, cons); - - try { - if (search.hasMoreElements()) { - return search.next(); - } - } finally { - if (search != null) { - search.close(); - } - } - - return null; - } - }); - } catch (NamingException e) { - throw new ModelException("Could not query server using DN [" + baseDN + "] and filter [" + filter + "]", e); - } - } - - /** - *

- * Destroys a subcontext with the given DN from the LDAP tree. - *

- * - * @param dn - */ - private void destroySubcontext(LdapContext context, final String dn) { - try { - NamingEnumeration enumeration = null; - - try { - enumeration = context.listBindings(dn); - - while (enumeration.hasMore()) { - Binding binding = enumeration.next(); - String name = binding.getNameInNamespace(); - - destroySubcontext(context, name); - } - - context.unbind(dn); - } finally { - try { - enumeration.close(); - } catch (Exception e) { - } - } - } catch (Exception e) { - throw new ModelException("Could not unbind DN [" + dn + "]", e); - } - } - - /** - *

- * Performs a simple authentication using the given DN and password to bind to the authentication context. - *

- * - * @param dn - * @param password - * @throws AuthenticationException if authentication is not successful - * - */ - public void authenticate(String dn, String password) throws AuthenticationException { - InitialContext authCtx = null; - - try { - if (password == null || password.isEmpty()) { - throw new AuthenticationException("Empty password used"); - } - - Hashtable env = new Hashtable(this.connectionProperties); - - env.put(Context.SECURITY_AUTHENTICATION, LDAPConstants.AUTH_TYPE_SIMPLE); - env.put(Context.SECURITY_PRINCIPAL, dn); - env.put(Context.SECURITY_CREDENTIALS, password); - - // Never use connection pool to prevent password caching - env.put("com.sun.jndi.ldap.connect.pool", "false"); - - authCtx = new InitialLdapContext(env, null); - - } catch (AuthenticationException ae) { - if (logger.isDebugEnabled()) { - logger.debugf(ae, "Authentication failed for DN [%s]", dn); - } - - throw ae; - } catch (Exception e) { - logger.errorf(e, "Unexpected exception when validating password of DN [%s]", dn); - throw new AuthenticationException("Unexpected exception when validating password of user"); - } finally { - if (authCtx != null) { - try { - authCtx.close(); - } catch (NamingException e) { - - } - } - } - } - - public void modifyAttributes(final String dn, final ModificationItem[] mods) { - try { - if (logger.isTraceEnabled()) { - logger.tracef("Modifying attributes for entry [%s]: [", dn); - - for (ModificationItem item : mods) { - Object values; - - if (item.getAttribute().size() > 0) { - values = item.getAttribute().get(); - } else { - values = "No values"; - } - - logger.tracef(" Op [%s]: %s = %s", item.getModificationOp(), item.getAttribute().getID(), values); - } - - logger.tracef("]"); - } - - execute(new LdapOperation() { - @Override - public Void execute(LdapContext context) throws NamingException { - context.modifyAttributes(dn, mods); - return null; - } - }); - } catch (NamingException e) { - throw new ModelException("Could not modify attribute for DN [" + dn + "]", e); - } - } - - public void createSubContext(final String name, final Attributes attributes) { - try { - if (logger.isTraceEnabled()) { - logger.tracef("Creating entry [%s] with attributes: [", name); - - NamingEnumeration all = attributes.getAll(); - - while (all.hasMore()) { - Attribute attribute = all.next(); - - logger.tracef(" %s = %s", attribute.getID(), attribute.get()); - } - - logger.tracef("]"); - } - - execute(new LdapOperation() { - @Override - public Void execute(LdapContext context) throws NamingException { - DirContext subcontext = context.createSubcontext(name, attributes); - - subcontext.close(); - - return null; - } - }); - } catch (NamingException e) { - throw new ModelException("Error creating subcontext [" + name + "]", e); - } - } - - private String getUuidAttributeName() { - return this.config.getUuidLDAPAttributeName(); - } - - public Attributes getAttributes(final String entryUUID, final String baseDN, Set returningAttributes) { - SearchResult search = lookupById(baseDN, entryUUID, returningAttributes); - - if (search == null) { - throw new ModelException("Couldn't find item with ID [" + entryUUID + " under base DN [" + baseDN + "]"); - } - - return search.getAttributes(); - } - - public String decodeEntryUUID(final Object entryUUID) { - String id; - - if (this.config.isActiveDirectory() && entryUUID instanceof byte[]) { - id = LDAPUtil.decodeObjectGUID((byte[]) entryUUID); - } else { - id = entryUUID.toString(); - } - - return id; - } - - private LdapContext createLdapContext() throws NamingException { - return new InitialLdapContext(new Hashtable(this.connectionProperties), null); - } - - private Map createConnectionProperties() { - HashMap env = new HashMap(); - - String authType = this.config.getAuthType(); - env.put(Context.INITIAL_CONTEXT_FACTORY, this.config.getFactoryName()); - env.put(Context.SECURITY_AUTHENTICATION, authType); - - String bindDN = this.config.getBindDN(); - - char[] bindCredential = null; - - if (this.config.getBindCredential() != null) { - bindCredential = this.config.getBindCredential().toCharArray(); - } - - if (!LDAPConstants.AUTH_TYPE_NONE.equals(authType)) { - env.put(Context.SECURITY_PRINCIPAL, bindDN); - env.put(Context.SECURITY_CREDENTIALS, bindCredential); - } - - String url = this.config.getConnectionUrl(); - - if (url != null) { - env.put(Context.PROVIDER_URL, url); - } else { - logger.warn("LDAP URL is null. LDAPOperationManager won't work correctly"); - } - - String useTruststoreSpi = this.config.getUseTruststoreSpi(); - LDAPConstants.setTruststoreSpiIfNeeded(useTruststoreSpi, url, env); - - String connectionPooling = this.config.getConnectionPooling(); - if (connectionPooling != null) { - env.put("com.sun.jndi.ldap.connect.pool", connectionPooling); - } - - // Just dump the additional properties - Properties additionalProperties = this.config.getAdditionalConnectionProperties(); - if (additionalProperties != null) { - for (Object key : additionalProperties.keySet()) { - env.put(key.toString(), additionalProperties.getProperty(key.toString())); - } - } - - if (config.isActiveDirectory()) { - env.put("java.naming.ldap.attributes.binary", LDAPConstants.OBJECT_GUID); - } - - if (logger.isDebugEnabled()) { - logger.debugf("Creating LdapContext using properties: [%s]", env); - } - - return env; - } - - private R execute(LdapOperation operation) throws NamingException { - LdapContext context = null; - - try { - context = createLdapContext(); - return operation.execute(context); - } catch (NamingException ne) { - throw ne; - } finally { - if (context != null) { - try { - context.close(); - } catch (NamingException ne) { - logger.error("Could not close Ldap context.", ne); - } - } - } - } - - private interface LdapOperation { - R execute(LdapContext context) throws NamingException; - } - - private Set getReturningAttributes(final Collection returningAttributes) { - Set result = new HashSet(); - - result.addAll(returningAttributes); - result.add(getUuidAttributeName()); - result.add(LDAPConstants.OBJECT_CLASS); - - return result; - } -} \ No newline at end of file diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPUtil.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPUtil.java deleted file mode 100644 index a60d4eb9887..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPUtil.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.idm.store.ldap; - -import org.keycloak.models.ModelException; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; - -/** - *

Utility class for working with LDAP.

- * - * @author Pedro Igor - */ -public class LDAPUtil { - - /** - *

Formats the given date.

- * - * @param date The Date to format. - * - * @return A String representing the formatted date. - */ - public static final String formatDate(Date date) { - if (date == null) { - throw new IllegalArgumentException("You must provide a date."); - } - - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss'.0Z'"); - - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - - return dateFormat.format(date); - } - - /** - *

- * Parses dates/time stamps stored in LDAP. Some possible values: - *

- *
    - *
  • 20020228150820
  • - *
  • 20030228150820Z
  • - *
  • 20050228150820.12
  • - *
  • 20060711011740.0Z
  • - *
- * - * @param date The date string to parse from. - * - * @return the Date. - */ - public static final Date parseDate(String date) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); - - try { - if (date.endsWith("Z")) { - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - } else { - dateFormat.setTimeZone(TimeZone.getDefault()); - } - - return dateFormat.parse(date); - } catch (Exception e) { - throw new ModelException("Error converting ldap date.", e); - } - } - - - - /** - *

Creates a byte-based {@link String} representation of a raw byte array representing the value of the - * objectGUID attribute retrieved from Active Directory.

- * - *

The returned string is useful to perform queries on AD based on the objectGUID value. Eg.:

- * - *

- * String filter = "(&(objectClass=*)(objectGUID" + EQUAL + convertObjectGUIToByteString(objectGUID) + "))"; - *

- * - * @param objectGUID A raw byte array representing the value of the objectGUID attribute retrieved from - * Active Directory. - * - * @return A byte-based String representation in the form of \[0]\[1]\[2]\[3]\[4]\[5]\[6]\[7]\[8]\[9]\[10]\[11]\[12]\[13]\[14]\[15] - */ - public static String convertObjectGUIToByteString(byte[] objectGUID) { - StringBuilder result = new StringBuilder(); - - for (int i = 0; i < objectGUID.length; i++) { - String transformed = prefixZeros((int) objectGUID[i] & 0xFF); - result.append("\\"); - result.append(transformed); - } - - return result.toString(); - } - - /** - *

Decode a raw byte array representing the value of the objectGUID attribute retrieved from Active - * Directory.

- * - *

The returned string is useful to directly bind an entry. Eg.:

- * - *

- * String bindingString = decodeObjectGUID(objectGUID); - *
- * Attributes attributes = ctx.getAttributes(bindingString); - *

- * - * @param objectGUID A raw byte array representing the value of the objectGUID attribute retrieved from - * Active Directory. - * - * @return A string representing the decoded value in the form of [3][2][1][0]-[5][4]-[7][6]-[8][9]-[10][11][12][13][14][15]. - */ - public static String decodeObjectGUID(byte[] objectGUID) { - StringBuilder displayStr = new StringBuilder(); - - displayStr.append(convertToDashedString(objectGUID)); - - return displayStr.toString(); - } - - private static String convertToDashedString(byte[] objectGUID) { - StringBuilder displayStr = new StringBuilder(); - - displayStr.append(prefixZeros((int) objectGUID[3] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[2] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[1] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[0] & 0xFF)); - displayStr.append("-"); - displayStr.append(prefixZeros((int) objectGUID[5] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[4] & 0xFF)); - displayStr.append("-"); - displayStr.append(prefixZeros((int) objectGUID[7] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[6] & 0xFF)); - displayStr.append("-"); - displayStr.append(prefixZeros((int) objectGUID[8] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[9] & 0xFF)); - displayStr.append("-"); - displayStr.append(prefixZeros((int) objectGUID[10] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[11] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[12] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[13] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[14] & 0xFF)); - displayStr.append(prefixZeros((int) objectGUID[15] & 0xFF)); - - return displayStr.toString(); - } - - private static String prefixZeros(int value) { - if (value <= 0xF) { - StringBuilder sb = new StringBuilder("0"); - sb.append(Integer.toHexString(value)); - return sb.toString(); - } else { - return Integer.toHexString(value); - } - } - - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java deleted file mode 100644 index 71e219871bf..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/kerberos/LDAPProviderKerberosConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.kerberos; - -import org.keycloak.common.constants.KerberosConstants; -import org.keycloak.federation.kerberos.CommonKerberosConfig; -import org.keycloak.models.UserFederationProviderModel; - -/** - * Configuration specific to {@link org.keycloak.federation.ldap.LDAPFederationProvider} - * - * @author Marek Posolda - */ -public class LDAPProviderKerberosConfig extends CommonKerberosConfig { - - public LDAPProviderKerberosConfig(UserFederationProviderModel userFederationProvider) { - super(userFederationProvider); - } - - public boolean isUseKerberosForPasswordAuthentication() { - return Boolean.valueOf(getConfig().get(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION)); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapper.java deleted file mode 100644 index 8d203d68157..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapper.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.mappers.UserFederationMapper; -import org.keycloak.models.GroupModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserFederationSyncResult; -import org.keycloak.models.UserModel; - -import javax.naming.AuthenticationException; -import java.util.Collections; -import java.util.List; - -/** - * Stateful per-request object - * - * @author Marek Posolda - */ -public abstract class AbstractLDAPFederationMapper { - - protected final UserFederationMapperModel mapperModel; - protected final LDAPFederationProvider ldapProvider; - protected final RealmModel realm; - - public AbstractLDAPFederationMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm) { - this.mapperModel = mapperModel; - this.ldapProvider = ldapProvider; - this.realm = realm; - } - - /** - * @see UserFederationMapper#syncDataFromFederationProviderToKeycloak(UserFederationMapperModel, UserFederationProvider, KeycloakSession, RealmModel) - */ - public UserFederationSyncResult syncDataFromFederationProviderToKeycloak() { - return new UserFederationSyncResult(); - } - - /** - * @see UserFederationMapper#syncDataFromKeycloakToFederationProvider(UserFederationMapperModel, UserFederationProvider, KeycloakSession, RealmModel) - */ - public UserFederationSyncResult syncDataFromKeycloakToFederationProvider() { - return new UserFederationSyncResult(); - } - - /** - * @see LDAPFederationMapper#beforeLDAPQuery(UserFederationMapperModel, LDAPQuery) - */ - public abstract void beforeLDAPQuery(LDAPQuery query); - - /** - * @see LDAPFederationMapper#proxy(UserFederationMapperModel, LDAPFederationProvider, LDAPObject, UserModel, RealmModel) - */ - public abstract UserModel proxy(LDAPObject ldapUser, UserModel delegate); - - /** - * @see LDAPFederationMapper#onRegisterUserToLDAP(UserFederationMapperModel, LDAPFederationProvider, LDAPObject, UserModel, RealmModel) - */ - public abstract void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser); - - /** - * @see LDAPFederationMapper#onImportUserFromLDAP(UserFederationMapperModel, LDAPFederationProvider, LDAPObject, UserModel, RealmModel, boolean) - */ - public abstract void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, boolean isCreate); - - public List getGroupMembers(GroupModel group, int firstResult, int maxResults) { - return Collections.emptyList(); - } - - public boolean onAuthenticationFailure(LDAPObject ldapUser, UserModel user, AuthenticationException ldapException) { - return false; - } - - - public static boolean parseBooleanParameter(UserFederationMapperModel mapperModel, String paramName) { - String paramm = mapperModel.getConfig().get(paramName); - return Boolean.parseBoolean(paramm); - } - - public LDAPFederationProvider getLdapProvider() { - return ldapProvider; - } - - public RealmModel getRealm() { - return realm; - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java deleted file mode 100755 index ccd4aa61dc6..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import java.util.List; - -import org.keycloak.Config; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.LDAPFederationProviderFactory; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.mappers.UserFederationMapper; -import org.keycloak.mappers.UserFederationMapperFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.provider.ProviderConfigProperty; -import org.keycloak.representations.idm.UserFederationMapperSyncConfigRepresentation; - -/** - * @author Marek Posolda - */ -public abstract class AbstractLDAPFederationMapperFactory implements UserFederationMapperFactory { - - // Used to map attributes from LDAP to UserModel attributes - public static final String ATTRIBUTE_MAPPER_CATEGORY = "Attribute Mapper"; - - // Used to map roles from LDAP to UserModel users - public static final String ROLE_MAPPER_CATEGORY = "Role Mapper"; - - - // Used to map group from LDAP to UserModel users - public static final String GROUP_MAPPER_CATEGORY = "Group Mapper"; - - @Override - public void init(Config.Scope config) { - } - - @Override - public UserFederationMapper create(KeycloakSession session) { - return new LDAPFederationMapperBridge(this); - } - - // Used just by LDAPFederationMapperBridge. - protected abstract AbstractLDAPFederationMapper createMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider federationProvider, RealmModel realm); - - @Override - public String getFederationProviderType() { - return LDAPFederationProviderFactory.PROVIDER_NAME; - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public UserFederationMapperSyncConfigRepresentation getSyncConfig() { - return new UserFederationMapperSyncConfigRepresentation(false, null, false, null); - } - - @Override - public void close() { - } - - public static ProviderConfigProperty createConfigProperty(String name, String label, String helpText, String type, List options) { - ProviderConfigProperty configProperty = new ProviderConfigProperty(); - configProperty.setName(name); - configProperty.setLabel(label); - configProperty.setHelpText(helpText); - configProperty.setType(type); - configProperty.setOptions(options); - return configProperty; - } - - protected void checkMandatoryConfigAttribute(String name, String displayName, UserFederationMapperModel mapperModel) throws FederationConfigValidationException { - String attrConfigValue = mapperModel.getConfig().get(name); - if (attrConfigValue == null || attrConfigValue.trim().isEmpty()) { - throw new FederationConfigValidationException("Missing configuration for '" + displayName + "'"); - } - } - - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java deleted file mode 100644 index c2cabe412de..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapper.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.EqualCondition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserModel; - -import java.util.HashSet; -import java.util.Set; - -/** - * Mapper useful for the LDAP deployments when some attribute (usually CN) is mapped to full name of user - * - * @author Marek Posolda - */ -public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper { - - public static final String LDAP_FULL_NAME_ATTRIBUTE = "ldap.full.name.attribute"; - public static final String READ_ONLY = "read.only"; - public static final String WRITE_ONLY = "write.only"; - - - public FullNameLDAPFederationMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm) { - super(mapperModel, ldapProvider, realm); - } - - @Override - public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, boolean isCreate) { - if (isWriteOnly()) { - return; - } - - String ldapFullNameAttrName = getLdapFullNameAttrName(); - String fullName = ldapUser.getAttributeAsString(ldapFullNameAttrName); - if (fullName == null) { - return; - } - - fullName = fullName.trim(); - if (!fullName.isEmpty()) { - int lastSpaceIndex = fullName.lastIndexOf(" "); - if (lastSpaceIndex == -1) { - user.setLastName(fullName); - } else { - user.setFirstName(fullName.substring(0, lastSpaceIndex)); - user.setLastName(fullName.substring(lastSpaceIndex + 1)); - } - } - } - - @Override - public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser) { - String ldapFullNameAttrName = getLdapFullNameAttrName(); - String fullName = getFullName(localUser.getFirstName(), localUser.getLastName()); - ldapUser.setSingleAttribute(ldapFullNameAttrName, fullName); - - if (isReadOnly()) { - ldapUser.addReadOnlyAttributeName(ldapFullNameAttrName); - } - } - - @Override - public UserModel proxy(LDAPObject ldapUser, UserModel delegate) { - if (ldapProvider.getEditMode() == UserFederationProvider.EditMode.WRITABLE && !isReadOnly()) { - - - TxAwareLDAPUserModelDelegate txDelegate = new TxAwareLDAPUserModelDelegate(delegate, ldapProvider, ldapUser) { - - @Override - public void setFirstName(String firstName) { - super.setFirstName(firstName); - setFullNameToLDAPObject(); - } - - @Override - public void setLastName(String lastName) { - super.setLastName(lastName); - setFullNameToLDAPObject(); - } - - private void setFullNameToLDAPObject() { - String fullName = getFullName(getFirstName(), getLastName()); - if (logger.isTraceEnabled()) { - logger.tracef("Pushing full name attribute to LDAP. Full name: %s", fullName); - } - - ensureTransactionStarted(); - - String ldapFullNameAttrName = getLdapFullNameAttrName(); - ldapUser.setSingleAttribute(ldapFullNameAttrName, fullName); - } - - }; - - return txDelegate; - } else { - return delegate; - } - } - - @Override - public void beforeLDAPQuery(LDAPQuery query) { - if (isWriteOnly()) { - return; - } - - String ldapFullNameAttrName = getLdapFullNameAttrName(); - query.addReturningLdapAttribute(ldapFullNameAttrName); - - // Change conditions and compute condition for fullName from the conditions for firstName and lastName. Right now just "equal" condition is supported - EqualCondition firstNameCondition = null; - EqualCondition lastNameCondition = null; - Set conditionsCopy = new HashSet(query.getConditions()); - for (Condition condition : conditionsCopy) { - String paramName = condition.getParameterName(); - if (paramName != null) { - if (paramName.equals(UserModel.FIRST_NAME)) { - firstNameCondition = (EqualCondition) condition; - query.getConditions().remove(condition); - } else if (paramName.equals(UserModel.LAST_NAME)) { - lastNameCondition = (EqualCondition) condition; - query.getConditions().remove(condition); - } else if (paramName.equals(LDAPConstants.GIVENNAME)) { - // Some previous mapper already converted it to LDAP name - firstNameCondition = (EqualCondition) condition; - } else if (paramName.equals(LDAPConstants.SN)) { - // Some previous mapper already converted it to LDAP name - lastNameCondition = (EqualCondition) condition; - } - } - } - - - String fullName = null; - if (firstNameCondition != null && lastNameCondition != null) { - fullName = firstNameCondition.getValue() + " " + lastNameCondition.getValue(); - } else if (firstNameCondition != null) { - fullName = (String) firstNameCondition.getValue(); - } else if (lastNameCondition != null) { - fullName = (String) lastNameCondition.getValue(); - } else { - return; - } - EqualCondition fullNameCondition = new EqualCondition(ldapFullNameAttrName, fullName); - query.addWhereCondition(fullNameCondition); - } - - protected String getLdapFullNameAttrName() { - String ldapFullNameAttrName = mapperModel.getConfig().get(LDAP_FULL_NAME_ATTRIBUTE); - return ldapFullNameAttrName == null ? LDAPConstants.CN : ldapFullNameAttrName; - } - - protected String getFullName(String firstName, String lastName) { - if (firstName != null && lastName != null) { - return firstName + " " + lastName; - } else if (firstName != null) { - return firstName; - } else if (lastName != null) { - return lastName; - } else { - return LDAPConstants.EMPTY_ATTRIBUTE_VALUE; - } - } - - private boolean isReadOnly() { - return parseBooleanParameter(mapperModel, READ_ONLY); - } - - private boolean isWriteOnly() { - return parseBooleanParameter(mapperModel, WRITE_ONLY); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java deleted file mode 100755 index 4c5a534a74e..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.provider.ProviderConfigProperty; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author Marek Posolda - */ -public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory { - - public static final String PROVIDER_ID = "full-name-ldap-mapper"; - - protected static final List configProperties = new ArrayList(); - - static { - ProviderConfigProperty userModelAttribute = createConfigProperty(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute", - "Name of LDAP attribute, which contains fullName of user. Usually it will be 'cn' ", ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(userModelAttribute); - - ProviderConfigProperty readOnly = createConfigProperty(FullNameLDAPFederationMapper.READ_ONLY, "Read Only", - "For Read-only is data imported from LDAP to Keycloak DB, but it's not saved back to LDAP when user is updated in Keycloak.", ProviderConfigProperty.BOOLEAN_TYPE, null); - configProperties.add(readOnly); - - ProviderConfigProperty writeOnly = createConfigProperty(FullNameLDAPFederationMapper.WRITE_ONLY, "Write Only", - "For Write-only is data propagated to LDAP when user is created or updated in Keycloak. But this mapper is not used to propagate data from LDAP back into Keycloak. " + - "This setting is useful if you configured separate firstName and lastName attribute mappers and you want to use those to read attribute from LDAP into Keycloak", ProviderConfigProperty.BOOLEAN_TYPE, null); - configProperties.add(writeOnly); - } - - @Override - public String getHelpText() { - return "Used to map full-name of user from single attribute in LDAP (usually 'cn' attribute) to firstName and lastName attributes of UserModel in Keycloak DB"; - } - - @Override - public String getDisplayCategory() { - return ATTRIBUTE_MAPPER_CATEGORY; - } - - @Override - public String getDisplayType() { - return "Full Name"; - } - - @Override - public List getConfigProperties() { - return configProperties; - } - - @Override - public Map getDefaultConfig(UserFederationProviderModel providerModel) { - Map defaultValues = new HashMap<>(); - LDAPConfig config = new LDAPConfig(providerModel.getConfig()); - - defaultValues.put(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN); - - boolean readOnly = config.getEditMode() != UserFederationProvider.EditMode.WRITABLE; - defaultValues.put(FullNameLDAPFederationMapper.READ_ONLY, String.valueOf(readOnly)); - - String writeOnly = String.valueOf(!readOnly); - defaultValues.put(FullNameLDAPFederationMapper.WRITE_ONLY, writeOnly); - - return defaultValues; - } - - @Override - public String getId() { - return PROVIDER_ID; - } - - @Override - public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException { - checkMandatoryConfigAttribute(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute", mapperModel); - - boolean readOnly = AbstractLDAPFederationMapper.parseBooleanParameter(mapperModel, FullNameLDAPFederationMapper.READ_ONLY); - boolean writeOnly = AbstractLDAPFederationMapper.parseBooleanParameter(mapperModel, FullNameLDAPFederationMapper.WRITE_ONLY); - - LDAPConfig cfg = new LDAPConfig(fedProviderModel.getConfig()); - UserFederationProvider.EditMode editMode = cfg.getEditMode(); - - if (writeOnly && cfg.getEditMode() != UserFederationProvider.EditMode.WRITABLE) { - throw new FederationConfigValidationException("ldapErrorCantWriteOnlyForReadOnlyLdap"); - } - if (writeOnly && readOnly) { - throw new FederationConfigValidationException("ldapErrorCantWriteOnlyAndReadOnly"); - } - } - - @Override - protected AbstractLDAPFederationMapper createMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider federationProvider, RealmModel realm) { - return new FullNameLDAPFederationMapper(mapperModel, federationProvider, realm); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapper.java deleted file mode 100644 index 67e577eebef..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapper.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.jboss.logging.Logger; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.models.ClientModel; -import org.keycloak.models.ModelException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.models.utils.UserModelDelegate; - -import java.util.Set; - -/** - * @author Marek Posolda - */ -public class HardcodedLDAPRoleMapper extends AbstractLDAPFederationMapper { - - private static final Logger logger = Logger.getLogger(HardcodedLDAPRoleMapper.class); - - public static final String ROLE = "role"; - - public HardcodedLDAPRoleMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm) { - super(mapperModel, ldapProvider, realm); - } - - @Override - public void beforeLDAPQuery(LDAPQuery query) { - } - - @Override - public UserModel proxy(LDAPObject ldapUser, UserModel delegate) { - return new UserModelDelegate(delegate) { - - @Override - public Set getRealmRoleMappings() { - Set roles = super.getRealmRoleMappings(); - - RoleModel role = getRole(); - if (role != null && role.getContainer().equals(realm)) { - roles.add(role); - } - - return roles; - } - - @Override - public Set getClientRoleMappings(ClientModel app) { - Set roles = super.getClientRoleMappings(app); - - RoleModel role = getRole(); - if (role != null && role.getContainer().equals(app)) { - roles.add(role); - } - - return roles; - } - - @Override - public boolean hasRole(RoleModel role) { - return super.hasRole(role) || role.equals(getRole()); - } - - @Override - public Set getRoleMappings() { - Set roles = super.getRoleMappings(); - - RoleModel role = getRole(); - if (role != null) { - roles.add(role); - } - - return roles; - } - - @Override - public void deleteRoleMapping(RoleModel role) { - if (role.equals(getRole())) { - throw new ModelException("Not possible to delete role. It's hardcoded by LDAP mapper"); - } else { - super.deleteRoleMapping(role); - } - } - }; - } - - @Override - public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser) { - - } - - @Override - public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, boolean isCreate) { - - } - - private RoleModel getRole() { - String roleName = mapperModel.getConfig().get(HardcodedLDAPRoleMapper.ROLE); - RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName); - if (role == null) { - logger.warnf("Hardcoded role '%s' configured in mapper '%s' is not available anymore"); - } - return role; - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java deleted file mode 100644 index 2f1170d11c1..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.provider.ProviderConfigProperty; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author Marek Posolda - */ -public class HardcodedLDAPRoleMapperFactory extends AbstractLDAPFederationMapperFactory { - - public static final String PROVIDER_ID = "hardcoded-ldap-role-mapper"; - protected static final List configProperties = new ArrayList(); - - static { - ProviderConfigProperty roleAttr = createConfigProperty(HardcodedLDAPRoleMapper.ROLE, "Role", - "Role to grant to user. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole", - ProviderConfigProperty.ROLE_TYPE, null); - configProperties.add(roleAttr); - } - - @Override - public String getHelpText() { - return "When user is imported from LDAP, he will be automatically added into this configured role."; - } - - @Override - public String getDisplayCategory() { - return ROLE_MAPPER_CATEGORY; - } - - @Override - public String getDisplayType() { - return "Hardcoded Role"; - } - - @Override - public List getConfigProperties() { - return configProperties; - } - - @Override - public Map getDefaultConfig(UserFederationProviderModel providerModel) { - return new HashMap<>(); - } - - @Override - public String getId() { - return PROVIDER_ID; - } - - @Override - public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException { - String roleName = mapperModel.getConfig().get(HardcodedLDAPRoleMapper.ROLE); - if (roleName == null) { - throw new FederationConfigValidationException("Role can't be null"); - } - RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName); - if (role == null) { - throw new FederationConfigValidationException("There is no role corresponding to configured value"); - } - } - - @Override - protected AbstractLDAPFederationMapper createMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider federationProvider, RealmModel realm) { - return new HardcodedLDAPRoleMapper(mapperModel, federationProvider, realm); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/LDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/LDAPFederationMapper.java deleted file mode 100644 index 52acbfb528b..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/LDAPFederationMapper.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.mappers.UserFederationMapper; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserModel; - -import javax.naming.AuthenticationException; - -/** - * @author Marek Posolda - */ -public interface LDAPFederationMapper extends UserFederationMapper { - - - /** - * Called when importing user from LDAP to local keycloak DB. - * - * @param mapperModel - * @param ldapProvider - * @param ldapUser - * @param user - * @param realm - * @param isCreate true if we importing new user from LDAP. False if user already exists in Keycloak, but we are upgrading (syncing) it from LDAP - */ - void onImportUserFromLDAP(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel user, RealmModel realm, boolean isCreate); - - - /** - * Called when register new user to LDAP - just after user was created in Keycloak DB - * - * @param mapperModel - * @param ldapProvider - * @param ldapUser - * @param localUser - * @param realm - */ - void onRegisterUserToLDAP(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel localUser, RealmModel realm); - - - /** - * Called when invoke proxy on LDAP federation provider - * - * @param mapperModel - * @param ldapProvider - * @param ldapUser - * @param delegate - * @param realm - * @return - */ - UserModel proxy(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel delegate, RealmModel realm); - - - /** - * Called before LDAP Identity query for retrieve LDAP users was executed. It allows to change query somehow (add returning attributes from LDAP, change conditions etc) - * - * @param mapperModel - * @param query - */ - void beforeLDAPQuery(UserFederationMapperModel mapperModel, LDAPQuery query); - - /** - * Called when LDAP authentication of specified user fails. If any mapper returns true from this method, AuthenticationException won't be rethrown! - * - * @param mapperModel - * @param ldapProvider - * @param realm - * @param user - * @param ldapUser - * @param ldapException - * @return true if mapper processed the AuthenticationException and did some actions based on that. In that case, AuthenticationException won't be rethrown! - */ - boolean onAuthenticationFailure(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel user, AuthenticationException ldapException, RealmModel realm); -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/LDAPFederationMapperBridge.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/LDAPFederationMapperBridge.java deleted file mode 100644 index 1a3521af358..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/LDAPFederationMapperBridge.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.models.GroupModel; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserFederationSyncResult; -import org.keycloak.models.UserModel; - -import javax.naming.AuthenticationException; -import java.util.List; - -/** - * Sufficient if mapper implementation is stateless and doesn't need to "close" any state - * - * @author Marek Posolda - */ -public class LDAPFederationMapperBridge implements LDAPFederationMapper { - - private final AbstractLDAPFederationMapperFactory factory; - - public LDAPFederationMapperBridge(AbstractLDAPFederationMapperFactory factory) { - this.factory = factory; - } - - // Sync groups from LDAP to Keycloak DB - @Override - public UserFederationSyncResult syncDataFromFederationProviderToKeycloak(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm) { - return getDelegate(mapperModel, federationProvider, realm).syncDataFromFederationProviderToKeycloak(); - } - - @Override - public UserFederationSyncResult syncDataFromKeycloakToFederationProvider(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, KeycloakSession session, RealmModel realm) { - return getDelegate(mapperModel, federationProvider, realm).syncDataFromKeycloakToFederationProvider(); - } - - @Override - public void onImportUserFromLDAP(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel user, RealmModel realm, boolean isCreate) { - getDelegate(mapperModel, ldapProvider, realm).onImportUserFromLDAP(ldapUser, user, isCreate); - } - - @Override - public void onRegisterUserToLDAP(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel localUser, RealmModel realm) { - getDelegate(mapperModel, ldapProvider, realm).onRegisterUserToLDAP(ldapUser, localUser); - } - - @Override - public UserModel proxy(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel delegate, RealmModel realm) { - return getDelegate(mapperModel, ldapProvider, realm).proxy(ldapUser, delegate); - } - - @Override - public void beforeLDAPQuery(UserFederationMapperModel mapperModel, LDAPQuery query) { - // Improve if needed - getDelegate(mapperModel, query.getLdapProvider(), null).beforeLDAPQuery(query); - } - - - @Override - public List getGroupMembers(UserFederationMapperModel mapperModel, UserFederationProvider ldapProvider, RealmModel realm, GroupModel group, int firstResult, int maxResults) { - return getDelegate(mapperModel, ldapProvider, realm).getGroupMembers(group, firstResult, maxResults); - } - - @Override - public boolean onAuthenticationFailure(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel user, AuthenticationException ldapException, RealmModel realm) { - return getDelegate(mapperModel, ldapProvider, realm).onAuthenticationFailure(ldapUser, user, ldapException); - } - - private AbstractLDAPFederationMapper getDelegate(UserFederationMapperModel mapperModel, UserFederationProvider federationProvider, RealmModel realm) { - LDAPFederationProvider ldapProvider = (LDAPFederationProvider) federationProvider; - return factory.createMapper(mapperModel, ldapProvider, realm); - } - - @Override - public void close() { - - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/LDAPMappersComparator.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/LDAPMappersComparator.java deleted file mode 100644 index 893cca765de..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/LDAPMappersComparator.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserModel; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * TODO: Possibly add "priority" to UserFederationMapper instead of hardcoding behaviour - * - * @author Marek Posolda - */ -public class LDAPMappersComparator { - - public static List sortAsc(LDAPConfig ldapConfig, Collection mappers) { - Comparator comparator = new ImportantFirstComparator(ldapConfig); - - List result = new ArrayList<>(mappers); - Collections.sort(result, comparator); - return result; - } - - public static List sortDesc(LDAPConfig ldapConfig, Collection mappers) { - Comparator comparator = new ImportantFirstComparator(ldapConfig).reversed(); - - List result = new ArrayList<>(mappers); - Collections.sort(result, comparator); - return result; - } - - - private static class ImportantFirstComparator implements Comparator { - - private final LDAPConfig ldapConfig; - - public ImportantFirstComparator(LDAPConfig ldapConfig) { - this.ldapConfig = ldapConfig; - } - - @Override - public int compare(UserFederationMapperModel o1, UserFederationMapperModel o2) { - // UserAttributeLDAPFederationMapper first - boolean isO1AttrMapper = o1.getFederationMapperType().equals(UserAttributeLDAPFederationMapperFactory.PROVIDER_ID); - boolean isO2AttrMapper = o2.getFederationMapperType().equals(UserAttributeLDAPFederationMapperFactory.PROVIDER_ID); - if (!isO1AttrMapper) { - if (isO2AttrMapper) { - return 1; - } else { - return 0; - } - } else if (!isO2AttrMapper) { - return -1; - } - - // Mapper for "username" attribute first - String model1 = o1.getConfig().get(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE); - String model2 = o2.getConfig().get(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE); - boolean isO1UsernameMapper = model1 != null && model1.equalsIgnoreCase(UserModel.USERNAME); - boolean isO2UsernameMapper = model2 != null && model2.equalsIgnoreCase(UserModel.USERNAME); - if (!isO1UsernameMapper) { - if (isO2UsernameMapper) { - return 1; - } else { - return 0; - } - } else if (!isO2UsernameMapper) { - return -1; - } - - // The username mapper corresponding to the same like configured username for federationProvider is first - String o1LdapAttr = o1.getConfig().get(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE); - String o2LdapAttr = o2.getConfig().get(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE); - boolean isO1LdapAttr = o1LdapAttr != null && ldapConfig.getUsernameLdapAttribute().equalsIgnoreCase(o1LdapAttr); - boolean isO2LdapAttr = o2LdapAttr != null && ldapConfig.getUsernameLdapAttribute().equalsIgnoreCase(o2LdapAttr); - - if (!isO1LdapAttr) { - if (isO2LdapAttr) { - return 1; - } else { - return 0; - } - } else if (!isO2LdapAttr) { - return -1; - } - - return 0; - } - - } - - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/PasswordUpdated.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/PasswordUpdated.java deleted file mode 100644 index 1ec5ad8e3c8..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/PasswordUpdated.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.keycloak.credential.CredentialInput; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.models.UserModel; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public interface PasswordUpdated { - void passwordUpdated(UserModel user, LDAPObject ldapUser, CredentialInput input); -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/TxAwareLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/TxAwareLDAPUserModelDelegate.java deleted file mode 100644 index 9126a668b85..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/TxAwareLDAPUserModelDelegate.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.jboss.logging.Logger; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.models.KeycloakTransaction; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.UserModelDelegate; - -/** - * @author Marek Posolda - */ -public abstract class TxAwareLDAPUserModelDelegate extends UserModelDelegate { - - public static final Logger logger = Logger.getLogger(TxAwareLDAPUserModelDelegate.class); - - protected LDAPFederationProvider provider; - protected LDAPObject ldapUser; - private final LDAPTransaction transaction; - - public TxAwareLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider, LDAPObject ldapUser) { - super(delegate); - this.provider = provider; - this.ldapUser = ldapUser; - this.transaction = findOrCreateTransaction(); - } - - public LDAPTransaction getTransaction() { - return transaction; - } - - // Try to find transaction in any delegate. We want to enlist just single transaction per all delegates - private LDAPTransaction findOrCreateTransaction() { - UserModelDelegate delegate = this; - while (true) { - UserModel deleg = delegate.getDelegate(); - if (!(deleg instanceof UserModelDelegate)) { - return new LDAPTransaction(); - } else { - delegate = (UserModelDelegate) deleg; - } - - if (delegate instanceof TxAwareLDAPUserModelDelegate) { - TxAwareLDAPUserModelDelegate txDelegate = (TxAwareLDAPUserModelDelegate) delegate; - return txDelegate.getTransaction(); - } - } - } - - protected void ensureTransactionStarted() { - if (transaction.state == TransactionState.NOT_STARTED) { - if (logger.isTraceEnabled()) { - logger.trace("Starting and enlisting transaction for object " + ldapUser.getDn().toString()); - } - - this.provider.getSession().getTransactionManager().enlistAfterCompletion(transaction); - } - } - - - - protected class LDAPTransaction implements KeycloakTransaction { - - protected TransactionState state = TransactionState.NOT_STARTED; - - @Override - public void begin() { - if (state != TransactionState.NOT_STARTED) { - throw new IllegalStateException("Transaction already started"); - } - - state = TransactionState.STARTED; - } - - @Override - public void commit() { - if (state != TransactionState.STARTED) { - throw new IllegalStateException("Transaction in illegal state for commit: " + state); - } - - if (logger.isTraceEnabled()) { - logger.trace("Transaction commit! Updating LDAP attributes for object " + ldapUser.getDn().toString() + ", attributes: " + ldapUser.getAttributes()); - } - - provider.getLdapIdentityStore().update(ldapUser); - state = TransactionState.FINISHED; - } - - @Override - public void rollback() { - if (state != TransactionState.STARTED && state != TransactionState.ROLLBACK_ONLY) { - throw new IllegalStateException("Transaction in illegal state for rollback: " + state); - } - - logger.warn("Transaction rollback! Ignoring LDAP updates for object " + ldapUser.getDn().toString()); - state = TransactionState.FINISHED; - } - - @Override - public void setRollbackOnly() { - state = TransactionState.ROLLBACK_ONLY; - } - - @Override - public boolean getRollbackOnly() { - return state == TransactionState.ROLLBACK_ONLY; - } - - @Override - public boolean isActive() { - return state == TransactionState.STARTED || state == TransactionState.ROLLBACK_ONLY; - } - } - - protected enum TransactionState { - NOT_STARTED, STARTED, ROLLBACK_ONLY, FINISHED - } - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java deleted file mode 100644 index bc1041595ea..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapper.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.jboss.logging.Logger; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.models.utils.UserModelDelegate; -import org.keycloak.models.utils.reflection.Property; -import org.keycloak.models.utils.reflection.PropertyCriteria; -import org.keycloak.models.utils.reflection.PropertyQueries; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @author Marek Posolda - */ -public class UserAttributeLDAPFederationMapper extends AbstractLDAPFederationMapper { - - private static final Logger logger = Logger.getLogger(UserAttributeLDAPFederationMapper.class); - - private static final Map> userModelProperties; - - static { - Map> userModelProps = PropertyQueries.createQuery(UserModel.class).addCriteria(new PropertyCriteria() { - - @Override - public boolean methodMatches(Method m) { - if ((m.getName().startsWith("get") || m.getName().startsWith("is")) && m.getParameterTypes().length > 0) { - return false; - } - - return true; - } - - }).getResultList(); - - // Convert to be keyed by lower-cased attribute names - userModelProperties = new HashMap<>(); - for (Map.Entry> entry : userModelProps.entrySet()) { - userModelProperties.put(entry.getKey().toLowerCase(), entry.getValue()); - } - } - - public static final String USER_MODEL_ATTRIBUTE = "user.model.attribute"; - public static final String LDAP_ATTRIBUTE = "ldap.attribute"; - public static final String READ_ONLY = "read.only"; - public static final String ALWAYS_READ_VALUE_FROM_LDAP = "always.read.value.from.ldap"; - public static final String IS_MANDATORY_IN_LDAP = "is.mandatory.in.ldap"; - - public UserAttributeLDAPFederationMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm) { - super(mapperModel, ldapProvider, realm); - } - - @Override - public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, boolean isCreate) { - String userModelAttrName = mapperModel.getConfig().get(USER_MODEL_ATTRIBUTE); - String ldapAttrName = mapperModel.getConfig().get(LDAP_ATTRIBUTE); - - Property userModelProperty = userModelProperties.get(userModelAttrName.toLowerCase()); - - if (userModelProperty != null) { - - // we have java property on UserModel - String ldapAttrValue = ldapUser.getAttributeAsString(ldapAttrName); - - checkDuplicateEmail(userModelAttrName, ldapAttrValue, realm, ldapProvider.getSession(), user); - - setPropertyOnUserModel(userModelProperty, user, ldapAttrValue); - } else { - - // we don't have java property. Let's set attribute - Set ldapAttrValue = ldapUser.getAttributeAsSet(ldapAttrName); - if (ldapAttrValue != null) { - user.setAttribute(userModelAttrName, new ArrayList<>(ldapAttrValue)); - } else { - user.removeAttribute(userModelAttrName); - } - } - } - - @Override - public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser) { - String userModelAttrName = mapperModel.getConfig().get(USER_MODEL_ATTRIBUTE); - String ldapAttrName = mapperModel.getConfig().get(LDAP_ATTRIBUTE); - boolean isMandatoryInLdap = parseBooleanParameter(mapperModel, IS_MANDATORY_IN_LDAP); - - Property userModelProperty = userModelProperties.get(userModelAttrName.toLowerCase()); - - if (userModelProperty != null) { - - // we have java property on UserModel. Assuming we support just properties of simple types - Object attrValue = userModelProperty.getValue(localUser); - - if (attrValue == null) { - if (isMandatoryInLdap) { - ldapUser.setSingleAttribute(ldapAttrName, LDAPConstants.EMPTY_ATTRIBUTE_VALUE); - } else { - ldapUser.setAttribute(ldapAttrName, new LinkedHashSet()); - } - } else { - ldapUser.setSingleAttribute(ldapAttrName, attrValue.toString()); - } - } else { - - // we don't have java property. Let's set attribute - List attrValues = localUser.getAttribute(userModelAttrName); - - if (attrValues.size() == 0) { - if (isMandatoryInLdap) { - ldapUser.setSingleAttribute(ldapAttrName, LDAPConstants.EMPTY_ATTRIBUTE_VALUE); - } else { - ldapUser.setAttribute(ldapAttrName, new LinkedHashSet()); - } - } else { - ldapUser.setAttribute(ldapAttrName, new LinkedHashSet<>(attrValues)); - } - } - - if (isReadOnly()) { - ldapUser.addReadOnlyAttributeName(ldapAttrName); - } - } - - // throw ModelDuplicateException if there is different user in model with same email - protected void checkDuplicateEmail(String userModelAttrName, String email, RealmModel realm, KeycloakSession session, UserModel user) { - if (UserModel.EMAIL.equalsIgnoreCase(userModelAttrName)) { - // lowercase before search - email = KeycloakModelUtils.toLowerCaseSafe(email); - - UserModel that = session.userStorage().getUserByEmail(email, realm); - if (that != null && !that.getId().equals(user.getId())) { - session.getTransactionManager().setRollbackOnly(); - String exceptionMessage = String.format("Can't import user '%s' from LDAP because email '%s' already exists in Keycloak. Existing user with this email is '%s'", user.getUsername(), email, that.getUsername()); - throw new ModelDuplicateException(exceptionMessage, UserModel.EMAIL); - } - } - } - - @Override - public UserModel proxy(final LDAPObject ldapUser, UserModel delegate) { - final String userModelAttrName = mapperModel.getConfig().get(USER_MODEL_ATTRIBUTE); - final String ldapAttrName = mapperModel.getConfig().get(LDAP_ATTRIBUTE); - boolean isAlwaysReadValueFromLDAP = parseBooleanParameter(mapperModel, ALWAYS_READ_VALUE_FROM_LDAP); - final boolean isMandatoryInLdap = parseBooleanParameter(mapperModel, IS_MANDATORY_IN_LDAP); - - // For writable mode, we want to propagate writing of attribute to LDAP as well - if (ldapProvider.getEditMode() == UserFederationProvider.EditMode.WRITABLE && !isReadOnly()) { - - delegate = new TxAwareLDAPUserModelDelegate(delegate, ldapProvider, ldapUser) { - - @Override - public void setSingleAttribute(String name, String value) { - setLDAPAttribute(name, value); - super.setSingleAttribute(name, value); - } - - @Override - public void setAttribute(String name, List values) { - setLDAPAttribute(name, values); - super.setAttribute(name, values); - } - - @Override - public void removeAttribute(String name) { - setLDAPAttribute(name, null); - super.removeAttribute(name); - } - - @Override - public void setEmail(String email) { - checkDuplicateEmail(userModelAttrName, email, realm, ldapProvider.getSession(), this); - - setLDAPAttribute(UserModel.EMAIL, email); - super.setEmail(email); - } - - @Override - public void setLastName(String lastName) { - setLDAPAttribute(UserModel.LAST_NAME, lastName); - super.setLastName(lastName); - } - - @Override - public void setFirstName(String firstName) { - setLDAPAttribute(UserModel.FIRST_NAME, firstName); - super.setFirstName(firstName); - } - - protected void setLDAPAttribute(String modelAttrName, Object value) { - if (modelAttrName.equalsIgnoreCase(userModelAttrName)) { - if (logger.isTraceEnabled()) { - logger.tracef("Pushing user attribute to LDAP. username: %s, Model attribute name: %s, LDAP attribute name: %s, Attribute value: %s", getUsername(), modelAttrName, ldapAttrName, value); - } - - ensureTransactionStarted(); - - if (value == null) { - if (isMandatoryInLdap) { - ldapUser.setSingleAttribute(ldapAttrName, LDAPConstants.EMPTY_ATTRIBUTE_VALUE); - } else { - ldapUser.setAttribute(ldapAttrName, new LinkedHashSet()); - } - } else if (value instanceof String) { - ldapUser.setSingleAttribute(ldapAttrName, (String) value); - } else { - List asList = (List) value; - if (asList.isEmpty() && isMandatoryInLdap) { - ldapUser.setSingleAttribute(ldapAttrName, LDAPConstants.EMPTY_ATTRIBUTE_VALUE); - } else { - ldapUser.setAttribute(ldapAttrName, new LinkedHashSet<>(asList)); - } - } - } - } - - }; - - } - - // We prefer to read attribute value from LDAP instead of from local Keycloak DB - if (isAlwaysReadValueFromLDAP) { - - delegate = new UserModelDelegate(delegate) { - - @Override - public String getFirstAttribute(String name) { - if (name.equalsIgnoreCase(userModelAttrName)) { - return ldapUser.getAttributeAsString(ldapAttrName); - } else { - return super.getFirstAttribute(name); - } - } - - @Override - public List getAttribute(String name) { - if (name.equalsIgnoreCase(userModelAttrName)) { - Collection ldapAttrValue = ldapUser.getAttributeAsSet(ldapAttrName); - if (ldapAttrValue == null) { - return Collections.emptyList(); - } else { - return new ArrayList<>(ldapAttrValue); - } - } else { - return super.getAttribute(name); - } - } - - @Override - public Map> getAttributes() { - Map> attrs = new HashMap<>(super.getAttributes()); - - // Ignore UserModel properties - if (userModelProperties.get(userModelAttrName.toLowerCase()) != null) { - return attrs; - } - - Set allLdapAttrValues = ldapUser.getAttributeAsSet(ldapAttrName); - if (allLdapAttrValues != null) { - attrs.put(userModelAttrName, new ArrayList<>(allLdapAttrValues)); - } - return attrs; - } - - @Override - public String getEmail() { - if (UserModel.EMAIL.equalsIgnoreCase(userModelAttrName)) { - return ldapUser.getAttributeAsString(ldapAttrName); - } else { - return super.getEmail(); - } - } - - @Override - public String getLastName() { - if (UserModel.LAST_NAME.equalsIgnoreCase(userModelAttrName)) { - return ldapUser.getAttributeAsString(ldapAttrName); - } else { - return super.getLastName(); - } - } - - @Override - public String getFirstName() { - if (UserModel.FIRST_NAME.equalsIgnoreCase(userModelAttrName)) { - return ldapUser.getAttributeAsString(ldapAttrName); - } else { - return super.getFirstName(); - } - } - - }; - } - - return delegate; - } - - @Override - public void beforeLDAPQuery(LDAPQuery query) { - String userModelAttrName = mapperModel.getConfig().get(USER_MODEL_ATTRIBUTE); - String ldapAttrName = mapperModel.getConfig().get(LDAP_ATTRIBUTE); - - // Add mapped attribute to returning ldap attributes - query.addReturningLdapAttribute(ldapAttrName); - if (isReadOnly()) { - query.addReturningReadOnlyLdapAttribute(ldapAttrName); - } - - // Change conditions and use ldapAttribute instead of userModel - for (Condition condition : query.getConditions()) { - condition.updateParameterName(userModelAttrName, ldapAttrName); - } - } - - private boolean isReadOnly() { - return parseBooleanParameter(mapperModel, READ_ONLY); - } - - - protected void setPropertyOnUserModel(Property userModelProperty, UserModel user, String ldapAttrValue) { - if (ldapAttrValue == null) { - userModelProperty.setValue(user, null); - } else { - Class clazz = userModelProperty.getJavaClass(); - - if (String.class.equals(clazz)) { - userModelProperty.setValue(user, ldapAttrValue); - } else if (Boolean.class.equals(clazz) || boolean.class.equals(clazz)) { - Boolean boolVal = Boolean.valueOf(ldapAttrValue); - userModelProperty.setValue(user, boolVal); - } else { - logger.warnf("Don't know how to set the property '%s' on user '%s' . Value of LDAP attribute is '%s' ", userModelProperty.getName(), user.getUsername(), ldapAttrValue.toString()); - } - } - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java deleted file mode 100755 index e0c0f8ec578..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers; - -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.provider.ProviderConfigProperty; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @author Marek Posolda - */ -public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory { - - public static final String PROVIDER_ID = "user-attribute-ldap-mapper"; - protected static final List configProperties = new ArrayList(); - - static { - ProviderConfigProperty userModelAttribute = createConfigProperty(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "User Model Attribute", - "Name of mapped UserModel property or UserModel attribute in Keycloak DB. For example 'firstName', 'lastName, 'email', 'street' etc.", ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(userModelAttribute); - - ProviderConfigProperty ldapAttribute = createConfigProperty(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "LDAP Attribute", - "Name of mapped attribute on LDAP object. For example 'cn', 'sn, 'mail', 'street' etc.", ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(ldapAttribute); - - ProviderConfigProperty readOnly = createConfigProperty(UserAttributeLDAPFederationMapper.READ_ONLY, "Read Only", - "Read-only attribute is imported from LDAP to Keycloak DB, but it's not saved back to LDAP when user is updated in Keycloak.", ProviderConfigProperty.BOOLEAN_TYPE, null); - configProperties.add(readOnly); - - ProviderConfigProperty alwaysReadValueFromLDAP = createConfigProperty(UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "Always Read Value From LDAP", - "If on, then during reading of the user will be value of attribute from LDAP always used instead of the value from Keycloak DB", ProviderConfigProperty.BOOLEAN_TYPE, null); - configProperties.add(alwaysReadValueFromLDAP); - - ProviderConfigProperty isMandatoryInLdap = createConfigProperty(UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "Is Mandatory In LDAP", - "If true, attribute is mandatory in LDAP. Hence if there is no value in Keycloak DB, the empty value will be set to be propagated to LDAP", ProviderConfigProperty.BOOLEAN_TYPE, null); - configProperties.add(isMandatoryInLdap); - } - - @Override - public String getHelpText() { - return "Used to map single attribute from LDAP user to attribute of UserModel in Keycloak DB"; - } - - @Override - public String getDisplayCategory() { - return ATTRIBUTE_MAPPER_CATEGORY; - } - - @Override - public String getDisplayType() { - return "User Attribute"; - } - - @Override - public List getConfigProperties() { - return configProperties; - } - - @Override - public Map getDefaultConfig(UserFederationProviderModel providerModel) { - Map defaultValues = new HashMap<>(); - LDAPConfig config = new LDAPConfig(providerModel.getConfig()); - - String readOnly = config.getEditMode() == UserFederationProvider.EditMode.WRITABLE ? "false" : "true"; - defaultValues.put(UserAttributeLDAPFederationMapper.READ_ONLY, readOnly); - - defaultValues.put(UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false"); - defaultValues.put(UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "false"); - - return defaultValues; - } - - @Override - public String getId() { - return PROVIDER_ID; - } - - @Override - public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException { - checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "User Model Attribute", mapperModel); - checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "LDAP Attribute", mapperModel); - } - - @Override - protected AbstractLDAPFederationMapper createMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider federationProvider, RealmModel realm) { - return new UserAttributeLDAPFederationMapper(mapperModel, federationProvider, realm); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/CommonLDAPGroupMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/CommonLDAPGroupMapper.java deleted file mode 100644 index 949b55d590d..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/CommonLDAPGroupMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership; - -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; - -/** - * Mapper related to mapping of LDAP groups to keycloak model objects (either keycloak roles or keycloak groups) - * - * @author Marek Posolda - */ -public interface CommonLDAPGroupMapper { - - LDAPQuery createLDAPGroupQuery(); - - CommonLDAPGroupMapperConfig getConfig(); -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/CommonLDAPGroupMapperConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/CommonLDAPGroupMapperConfig.java deleted file mode 100644 index ea50569710f..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/CommonLDAPGroupMapperConfig.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership; - -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelException; -import org.keycloak.models.UserFederationMapperModel; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author Marek Posolda - */ -public abstract class CommonLDAPGroupMapperConfig { - - // Name of LDAP attribute on role, which is used for membership mappings. Usually it will be "member" - public static final String MEMBERSHIP_LDAP_ATTRIBUTE = "membership.ldap.attribute"; - - // See docs for MembershipType enum - public static final String MEMBERSHIP_ATTRIBUTE_TYPE = "membership.attribute.type"; - - // See docs for Mode enum - public static final String MODE = "mode"; - - // See docs for UserRolesRetriever enum - public static final String USER_ROLES_RETRIEVE_STRATEGY = "user.roles.retrieve.strategy"; - - - protected final UserFederationMapperModel mapperModel; - - public CommonLDAPGroupMapperConfig(UserFederationMapperModel mapperModel) { - this.mapperModel = mapperModel; - } - - public String getMembershipLdapAttribute() { - String membershipAttrName = mapperModel.getConfig().get(MEMBERSHIP_LDAP_ATTRIBUTE); - return membershipAttrName!=null ? membershipAttrName : LDAPConstants.MEMBER; - } - - public MembershipType getMembershipTypeLdapAttribute() { - String membershipType = mapperModel.getConfig().get(MEMBERSHIP_ATTRIBUTE_TYPE); - return (membershipType!=null && !membershipType.isEmpty()) ? Enum.valueOf(MembershipType.class, membershipType) : MembershipType.DN; - } - - public LDAPGroupMapperMode getMode() { - String modeString = mapperModel.getConfig().get(MODE); - if (modeString == null || modeString.isEmpty()) { - throw new ModelException("Mode is missing! Check your configuration"); - } - - return Enum.valueOf(LDAPGroupMapperMode.class, modeString.toUpperCase()); - } - - protected Set getConfigValues(String str) { - String[] objClasses = str.split(","); - Set trimmed = new HashSet<>(); - for (String objectClass : objClasses) { - objectClass = objectClass.trim(); - if (objectClass.length() > 0) { - trimmed.add(objectClass); - } - } - return trimmed; - } - - public abstract String getLDAPGroupsDn(); - - public abstract String getLDAPGroupNameLdapAttribute(); - - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/LDAPGroupMapperMode.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/LDAPGroupMapperMode.java deleted file mode 100644 index dc447311e1e..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/LDAPGroupMapperMode.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership; - -/** - * @author Marek Posolda - */ -public enum LDAPGroupMapperMode { - - /** - * All role mappings are retrieved from LDAP and saved into LDAP - */ - LDAP_ONLY, - - /** - * Read-only LDAP mode. Role mappings are retrieved from LDAP for particular user just at the time when he is imported and then - * they are saved to local keycloak DB. Then all role mappings are always retrieved from keycloak DB, never from LDAP. - * Creating or deleting of role mapping is propagated only to DB. - * - * This is read-only mode LDAP mode and it's good for performance, but when user is put to some role directly in LDAP, it - * won't be seen by Keycloak - */ - IMPORT, - - /** - * Read-only LDAP mode. Role mappings are retrieved from both LDAP and DB and merged together. New role grants are not saved to LDAP but to DB. - * Deleting role mappings, which is mapped to LDAP, will throw an error. - */ - READ_ONLY - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/MembershipType.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/MembershipType.java deleted file mode 100644 index 4c9de3e7850..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/MembershipType.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership; - -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.LDAPUtils; -import org.keycloak.federation.ldap.idm.model.LDAPDn; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder; -import org.keycloak.federation.ldap.mappers.membership.group.GroupLDAPFederationMapper; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -/** - * @author Marek Posolda - */ -public enum MembershipType { - - /** - * Used if LDAP role has it's members declared in form of their full DN. For example ( "member: uid=john,ou=users,dc=example,dc=com" ) - */ - DN { - - @Override - public Set getLDAPSubgroups(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup) { - CommonLDAPGroupMapperConfig config = groupMapper.getConfig(); - return getLDAPMembersWithParent(ldapGroup, config.getMembershipLdapAttribute(), LDAPDn.fromString(config.getLDAPGroupsDn())); - } - - // Get just those members of specified group, which are descendants of "requiredParentDn" - protected Set getLDAPMembersWithParent(LDAPObject ldapGroup, String membershipLdapAttribute, LDAPDn requiredParentDn) { - Set allMemberships = LDAPUtils.getExistingMemberships(membershipLdapAttribute, ldapGroup); - - // Filter and keep just groups - Set result = new HashSet<>(); - for (String membership : allMemberships) { - LDAPDn childDn = LDAPDn.fromString(membership); - if (childDn.isDescendantOf(requiredParentDn)) { - result.add(childDn); - } - } - return result; - } - - @Override - public List getGroupMembers(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) { - RealmModel realm = groupMapper.getRealm(); - LDAPFederationProvider ldapProvider = groupMapper.getLdapProvider(); - CommonLDAPGroupMapperConfig config = groupMapper.getConfig(); - - LDAPDn usersDn = LDAPDn.fromString(ldapProvider.getLdapIdentityStore().getConfig().getUsersDn()); - Set userDns = getLDAPMembersWithParent(ldapGroup, config.getMembershipLdapAttribute(), usersDn); - - if (userDns == null) { - return Collections.emptyList(); - } - - if (userDns.size() <= firstResult) { - return Collections.emptyList(); - } - - List dns = new ArrayList<>(userDns); - int max = Math.min(dns.size(), firstResult + maxResults); - dns = dns.subList(firstResult, max); - - // If usernameAttrName is same like DN, we can just retrieve usernames from DNs - List usernames = new LinkedList<>(); - LDAPConfig ldapConfig = ldapProvider.getLdapIdentityStore().getConfig(); - if (ldapConfig.getUsernameLdapAttribute().equals(ldapConfig.getRdnLdapAttribute())) { - for (LDAPDn userDn : dns) { - String username = userDn.getFirstRdnAttrValue(); - usernames.add(username); - } - } else { - LDAPQuery query = LDAPUtils.createQueryForUserSearch(ldapProvider, realm); - LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder(); - Condition[] orSubconditions = new Condition[dns.size()]; - int index = 0; - for (LDAPDn userDn : dns) { - Condition condition = conditionsBuilder.equal(userDn.getFirstRdnAttrName(), userDn.getFirstRdnAttrValue()); - orSubconditions[index] = condition; - index++; - } - Condition orCondition = conditionsBuilder.orCondition(orSubconditions); - query.addWhereCondition(orCondition); - List ldapUsers = query.getResultList(); - for (LDAPObject ldapUser : ldapUsers) { - String username = LDAPUtils.getUsername(ldapUser, ldapConfig); - usernames.add(username); - } - } - - // We have dns of users, who are members of our group. Load them now - return ldapProvider.loadUsersByUsernames(usernames, realm); - } - - }, - - - /** - * Used if LDAP role has it's members declared in form of pure user uids. For example ( "memberUid: john" ) - */ - UID { - - // Group inheritance not supported for this config - @Override - public Set getLDAPSubgroups(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup) { - return Collections.emptySet(); - } - - @Override - public List getGroupMembers(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) { - String memberAttrName = groupMapper.getConfig().getMembershipLdapAttribute(); - Set memberUids = LDAPUtils.getExistingMemberships(memberAttrName, ldapGroup); - - if (memberUids == null || memberUids.size() <= firstResult) { - return Collections.emptyList(); - } - - List uids = new ArrayList<>(memberUids); - int max = Math.min(memberUids.size(), firstResult + maxResults); - uids = uids.subList(firstResult, max); - - return groupMapper.getLdapProvider().loadUsersByUsernames(uids, groupMapper.getRealm()); - } - - }; - - public abstract Set getLDAPSubgroups(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup); - - public abstract List getGroupMembers(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults); -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/UserRolesRetrieveStrategy.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/UserRolesRetrieveStrategy.java deleted file mode 100644 index fe33e3b1062..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/UserRolesRetrieveStrategy.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership; - - -import org.keycloak.federation.ldap.LDAPUtils; -import org.keycloak.federation.ldap.idm.model.LDAPDn; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder; -import org.keycloak.models.LDAPConstants; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -/** - * Strategy for how to retrieve LDAP roles of user - * - * @author Marek Posolda - */ -public interface UserRolesRetrieveStrategy { - - - List getLDAPRoleMappings(CommonLDAPGroupMapper roleOrGroupMapper, LDAPObject ldapUser); - - void beforeUserLDAPQuery(LDAPQuery query); - - - // Impl subclasses - - /** - * Roles of user will be retrieved by sending LDAP query to retrieve all roles where "member" is our user - */ - class LoadRolesByMember implements UserRolesRetrieveStrategy { - - @Override - public List getLDAPRoleMappings(CommonLDAPGroupMapper roleOrGroupMapper, LDAPObject ldapUser) { - LDAPQuery ldapQuery = roleOrGroupMapper.createLDAPGroupQuery(); - String membershipAttr = roleOrGroupMapper.getConfig().getMembershipLdapAttribute(); - - String userMembership = LDAPUtils.getMemberValueOfChildObject(ldapUser, roleOrGroupMapper.getConfig().getMembershipTypeLdapAttribute()); - - Condition membershipCondition = getMembershipCondition(membershipAttr, userMembership); - ldapQuery.addWhereCondition(membershipCondition); - return ldapQuery.getResultList(); - } - - @Override - public void beforeUserLDAPQuery(LDAPQuery query) { - } - - protected Condition getMembershipCondition(String membershipAttr, String userMembership) { - return new LDAPQueryConditionsBuilder().equal(membershipAttr, userMembership); - } - - }; - - /** - * Roles of user will be retrieved from "memberOf" attribute of our user - */ - class GetRolesFromUserMemberOfAttribute implements UserRolesRetrieveStrategy { - - @Override - public List getLDAPRoleMappings(CommonLDAPGroupMapper roleOrGroupMapper, LDAPObject ldapUser) { - Set memberOfValues = ldapUser.getAttributeAsSet(LDAPConstants.MEMBER_OF); - if (memberOfValues == null) { - return Collections.emptyList(); - } - - List roles = new LinkedList<>(); - LDAPDn parentDn = LDAPDn.fromString(roleOrGroupMapper.getConfig().getLDAPGroupsDn()); - - for (String roleDn : memberOfValues) { - LDAPDn roleDN = LDAPDn.fromString(roleDn); - if (roleDN.isDescendantOf(parentDn)) { - LDAPObject role = new LDAPObject(); - role.setDn(roleDN); - - String firstDN = roleDN.getFirstRdnAttrName(); - if (firstDN.equalsIgnoreCase(roleOrGroupMapper.getConfig().getLDAPGroupNameLdapAttribute())) { - role.setRdnAttributeName(firstDN); - role.setSingleAttribute(firstDN, roleDN.getFirstRdnAttrValue()); - roles.add(role); - } - } - } - return roles; - } - - @Override - public void beforeUserLDAPQuery(LDAPQuery query) { - query.addReturningLdapAttribute(LDAPConstants.MEMBER_OF); - query.addReturningReadOnlyLdapAttribute(LDAPConstants.MEMBER_OF); - } - - }; - - /** - * Extension specific to Active Directory. Roles of user will be retrieved by sending LDAP query to retrieve all roles where "member" is our user. - * The query will be able to retrieve memberships recursively with usage of AD specific extension LDAP_MATCHING_RULE_IN_CHAIN, so likely doesn't work on other LDAP servers - */ - class LoadRolesByMemberRecursively extends LoadRolesByMember { - - protected Condition getMembershipCondition(String membershipAttr, String userMembership) { - return new LDAPQueryConditionsBuilder().equal(membershipAttr + LDAPConstants.LDAP_MATCHING_RULE_IN_CHAIN, userMembership); - } - - }; - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java deleted file mode 100644 index d135dfa017b..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java +++ /dev/null @@ -1,648 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership.group; - -import org.jboss.logging.Logger; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.LDAPUtils; -import org.keycloak.federation.ldap.idm.model.LDAPDn; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.membership.CommonLDAPGroupMapper; -import org.keycloak.federation.ldap.mappers.membership.CommonLDAPGroupMapperConfig; -import org.keycloak.federation.ldap.mappers.membership.LDAPGroupMapperMode; -import org.keycloak.federation.ldap.mappers.membership.MembershipType; -import org.keycloak.federation.ldap.mappers.membership.UserRolesRetrieveStrategy; -import org.keycloak.models.GroupModel; -import org.keycloak.models.ModelException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationSyncResult; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.models.utils.RoleUtils; -import org.keycloak.models.utils.UserModelDelegate; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.keycloak.models.RoleModel; - -/** - * @author Marek Posolda - */ -public class GroupLDAPFederationMapper extends AbstractLDAPFederationMapper implements CommonLDAPGroupMapper { - - private static final Logger logger = Logger.getLogger(GroupLDAPFederationMapper.class); - - private final GroupMapperConfig config; - private final GroupLDAPFederationMapperFactory factory; - - // Flag to avoid syncing multiple times per transaction - private boolean syncFromLDAPPerformedInThisTransaction = false; - - public GroupLDAPFederationMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm, GroupLDAPFederationMapperFactory factory) { - super(mapperModel, ldapProvider, realm); - this.config = new GroupMapperConfig(mapperModel); - this.factory = factory; - } - - - // CommonLDAPGroupMapper interface - - @Override - public LDAPQuery createLDAPGroupQuery() { - return createGroupQuery(); - } - - @Override - public CommonLDAPGroupMapperConfig getConfig() { - return config; - } - - - - // LDAP Group CRUD operations - - public LDAPQuery createGroupQuery() { - LDAPQuery ldapQuery = new LDAPQuery(ldapProvider); - - // For now, use same search scope, which is configured "globally" and used for user's search. - ldapQuery.setSearchScope(ldapProvider.getLdapIdentityStore().getConfig().getSearchScope()); - - String groupsDn = config.getGroupsDn(); - ldapQuery.setSearchDn(groupsDn); - - Collection groupObjectClasses = config.getGroupObjectClasses(ldapProvider); - ldapQuery.addObjectClasses(groupObjectClasses); - - String customFilter = config.getCustomLdapFilter(); - if (customFilter != null && customFilter.trim().length() > 0) { - Condition customFilterCondition = new LDAPQueryConditionsBuilder().addCustomLDAPFilter(customFilter); - ldapQuery.addWhereCondition(customFilterCondition); - } - - ldapQuery.addReturningLdapAttribute(config.getGroupNameLdapAttribute()); - ldapQuery.addReturningLdapAttribute(config.getMembershipLdapAttribute()); - - for (String groupAttr : config.getGroupAttributes()) { - ldapQuery.addReturningLdapAttribute(groupAttr); - } - - return ldapQuery; - } - - public LDAPObject createLDAPGroup(String groupName, Map> additionalAttributes) { - LDAPObject ldapGroup = LDAPUtils.createLDAPGroup(ldapProvider, groupName, config.getGroupNameLdapAttribute(), config.getGroupObjectClasses(ldapProvider), - config.getGroupsDn(), additionalAttributes); - - logger.debugf("Creating group [%s] to LDAP with DN [%s]", groupName, ldapGroup.getDn().toString()); - return ldapGroup; - } - - public LDAPObject loadLDAPGroupByName(String groupName) { - LDAPQuery ldapQuery = createGroupQuery(); - Condition roleNameCondition = new LDAPQueryConditionsBuilder().equal(config.getGroupNameLdapAttribute(), groupName); - ldapQuery.addWhereCondition(roleNameCondition); - return ldapQuery.getFirstResult(); - } - - protected Set getLDAPSubgroups(LDAPObject ldapGroup) { - MembershipType membershipType = config.getMembershipTypeLdapAttribute(); - return membershipType.getLDAPSubgroups(this, ldapGroup); - } - - - // Sync from Ldap to KC - - public UserFederationSyncResult syncDataFromFederationProviderToKeycloak() { - UserFederationSyncResult syncResult = new UserFederationSyncResult() { - - @Override - public String getStatus() { - return String.format("%d imported groups, %d updated groups, %d removed groups", getAdded(), getUpdated(), getRemoved()); - } - - }; - - logger.debugf("Syncing groups from LDAP into Keycloak DB. Mapper is [%s], LDAP provider is [%s]", mapperModel.getName(), ldapProvider.getModel().getDisplayName()); - - // Get all LDAP groups - List ldapGroups = getAllLDAPGroups(); - - // Convert to internal format - Map ldapGroupsMap = new HashMap<>(); - List ldapGroupsRep = new LinkedList<>(); - - String groupsRdnAttr = config.getGroupNameLdapAttribute(); - for (LDAPObject ldapGroup : ldapGroups) { - String groupName = ldapGroup.getAttributeAsString(groupsRdnAttr); - - Set subgroupNames = new HashSet<>(); - for (LDAPDn groupDn : getLDAPSubgroups(ldapGroup)) { - subgroupNames.add(groupDn.getFirstRdnAttrValue()); - } - - ldapGroupsRep.add(new GroupTreeResolver.Group(groupName, subgroupNames)); - ldapGroupsMap.put(groupName, ldapGroup); - } - - // Now we have list of LDAP groups. Let's form the tree (if needed) - if (config.isPreserveGroupsInheritance()) { - try { - List groupTrees = new GroupTreeResolver().resolveGroupTree(ldapGroupsRep); - - updateKeycloakGroupTree(groupTrees, ldapGroupsMap, syncResult); - } catch (GroupTreeResolver.GroupTreeResolveException gre) { - throw new ModelException("Couldn't resolve groups from LDAP. Fix LDAP or skip preserve inheritance. Details: " + gre.getMessage(), gre); - } - } else { - Set visitedGroupIds = new HashSet<>(); - - // Just add flat structure of groups with all groups at top-level - for (Map.Entry groupEntry : ldapGroupsMap.entrySet()) { - String groupName = groupEntry.getKey(); - GroupModel kcExistingGroup = KeycloakModelUtils.findGroupByPath(realm, "/" + groupName); - - if (kcExistingGroup != null) { - updateAttributesOfKCGroup(kcExistingGroup, groupEntry.getValue()); - syncResult.increaseUpdated(); - visitedGroupIds.add(kcExistingGroup.getId()); - } else { - GroupModel kcGroup = realm.createGroup(groupName); - updateAttributesOfKCGroup(kcGroup, groupEntry.getValue()); - realm.moveGroup(kcGroup, null); - syncResult.increaseAdded(); - visitedGroupIds.add(kcGroup.getId()); - } - } - - // Possibly remove keycloak groups, which doesn't exists in LDAP - if (config.isDropNonExistingGroupsDuringSync()) { - dropNonExistingKcGroups(syncResult, visitedGroupIds); - } - } - - syncFromLDAPPerformedInThisTransaction = true; - - return syncResult; - } - - private void updateKeycloakGroupTree(List groupTrees, Map ldapGroups, UserFederationSyncResult syncResult) { - Set visitedGroupIds = new HashSet<>(); - - for (GroupTreeResolver.GroupTreeEntry groupEntry : groupTrees) { - updateKeycloakGroupTreeEntry(groupEntry, ldapGroups, null, syncResult, visitedGroupIds); - } - - // Possibly remove keycloak groups, which doesn't exists in LDAP - if (config.isDropNonExistingGroupsDuringSync()) { - dropNonExistingKcGroups(syncResult, visitedGroupIds); - } - } - - private void updateKeycloakGroupTreeEntry(GroupTreeResolver.GroupTreeEntry groupTreeEntry, Map ldapGroups, GroupModel kcParent, UserFederationSyncResult syncResult, Set visitedGroupIds) { - String groupName = groupTreeEntry.getGroupName(); - - // Check if group already exists - GroupModel kcGroup = null; - Collection subgroups = kcParent == null ? realm.getTopLevelGroups() : kcParent.getSubGroups(); - for (GroupModel group : subgroups) { - if (group.getName().equals(groupName)) { - kcGroup = group; - break; - } - } - - if (kcGroup != null) { - logger.debugf("Updated Keycloak group '%s' from LDAP", kcGroup.getName()); - updateAttributesOfKCGroup(kcGroup, ldapGroups.get(kcGroup.getName())); - syncResult.increaseUpdated(); - } else { - kcGroup = realm.createGroup(groupTreeEntry.getGroupName()); - if (kcParent == null) { - realm.moveGroup(kcGroup, null); - logger.debugf("Imported top-level group '%s' from LDAP", kcGroup.getName()); - } else { - realm.moveGroup(kcGroup, kcParent); - logger.debugf("Imported group '%s' from LDAP as child of group '%s'", kcGroup.getName(), kcParent.getName()); - } - - updateAttributesOfKCGroup(kcGroup, ldapGroups.get(kcGroup.getName())); - syncResult.increaseAdded(); - } - - visitedGroupIds.add(kcGroup.getId()); - - for (GroupTreeResolver.GroupTreeEntry childEntry : groupTreeEntry.getChildren()) { - updateKeycloakGroupTreeEntry(childEntry, ldapGroups, kcGroup, syncResult, visitedGroupIds); - } - } - - private void dropNonExistingKcGroups(UserFederationSyncResult syncResult, Set visitedGroupIds) { - // Remove keycloak groups, which doesn't exists in LDAP - List allGroups = realm.getGroups(); - for (GroupModel kcGroup : allGroups) { - if (!visitedGroupIds.contains(kcGroup.getId())) { - logger.debugf("Removing Keycloak group '%s', which doesn't exist in LDAP", kcGroup.getName()); - realm.removeGroup(kcGroup); - syncResult.increaseRemoved(); - } - } - } - - private void updateAttributesOfKCGroup(GroupModel kcGroup, LDAPObject ldapGroup) { - Collection groupAttributes = config.getGroupAttributes(); - - for (String attrName : groupAttributes) { - Set attrValues = ldapGroup.getAttributeAsSet(attrName); - if (attrValues==null) { - kcGroup.removeAttribute(attrName); - } else { - kcGroup.setAttribute(attrName, new LinkedList<>(attrValues)); - } - } - } - - - protected GroupModel findKcGroupByLDAPGroup(LDAPObject ldapGroup) { - String groupNameAttr = config.getGroupNameLdapAttribute(); - String groupName = ldapGroup.getAttributeAsString(groupNameAttr); - - if (config.isPreserveGroupsInheritance()) { - // Override if better effectivity or different algorithm is needed - List groups = realm.getGroups(); - for (GroupModel group : groups) { - if (group.getName().equals(groupName)) { - return group; - } - } - - return null; - } else { - // Without preserved inheritance, it's always top-level group - return KeycloakModelUtils.findGroupByPath(realm, "/" + groupName); - } - } - - protected GroupModel findKcGroupOrSyncFromLDAP(LDAPObject ldapGroup, UserModel user) { - GroupModel kcGroup = findKcGroupByLDAPGroup(ldapGroup); - - if (kcGroup == null) { - - if (config.isPreserveGroupsInheritance()) { - - // Better to sync all groups from LDAP with preserved inheritance - if (!syncFromLDAPPerformedInThisTransaction) { - syncDataFromFederationProviderToKeycloak(); - kcGroup = findKcGroupByLDAPGroup(ldapGroup); - } - } else { - String groupNameAttr = config.getGroupNameLdapAttribute(); - String groupName = ldapGroup.getAttributeAsString(groupNameAttr); - - kcGroup = realm.createGroup(groupName); - updateAttributesOfKCGroup(kcGroup, ldapGroup); - realm.moveGroup(kcGroup, null); - } - - // Could theoretically happen on some LDAP servers if 'memberof' style is used and 'memberof' attribute of user references non-existing group - if (kcGroup == null) { - String groupName = ldapGroup.getAttributeAsString(config.getGroupNameLdapAttribute()); - logger.warnf("User '%s' is member of group '%s', which doesn't exists in LDAP", user.getUsername(), groupName); - } - } - - return kcGroup; - } - - // Send LDAP query to retrieve all groups - protected List getAllLDAPGroups() { - LDAPQuery ldapGroupQuery = createGroupQuery(); - return LDAPUtils.loadAllLDAPObjects(ldapGroupQuery, ldapProvider); - } - - - // Sync from Keycloak to LDAP - - public UserFederationSyncResult syncDataFromKeycloakToFederationProvider() { - UserFederationSyncResult syncResult = new UserFederationSyncResult() { - - @Override - public String getStatus() { - return String.format("%d groups imported to LDAP, %d groups updated to LDAP, %d groups removed from LDAP", getAdded(), getUpdated(), getRemoved()); - } - - }; - - if (config.getMode() != LDAPGroupMapperMode.LDAP_ONLY) { - logger.warnf("Ignored sync for federation mapper '%s' as it's mode is '%s'", mapperModel.getName(), config.getMode().toString()); - return syncResult; - } - - logger.debugf("Syncing groups from Keycloak into LDAP. Mapper is [%s], LDAP provider is [%s]", mapperModel.getName(), ldapProvider.getModel().getDisplayName()); - - // Query existing LDAP groups - LDAPQuery ldapQuery = createGroupQuery(); - List ldapGroups = ldapQuery.getResultList(); - - // Convert them to Map - Map ldapGroupsMap = new HashMap<>(); - String groupsRdnAttr = config.getGroupNameLdapAttribute(); - for (LDAPObject ldapGroup : ldapGroups) { - String groupName = ldapGroup.getAttributeAsString(groupsRdnAttr); - ldapGroupsMap.put(groupName, ldapGroup); - } - - // Map to track all LDAP groups also exists in Keycloak - Set ldapGroupNames = new HashSet<>(); - - // Create or update KC groups to LDAP including their attributes - for (GroupModel kcGroup : realm.getTopLevelGroups()) { - processLdapGroupSyncToLDAP(kcGroup, ldapGroupsMap, ldapGroupNames, syncResult); - } - - // If dropNonExisting, then drop all groups, which doesn't exist in KC from LDAP as well - if (config.isDropNonExistingGroupsDuringSync()) { - Set copy = new HashSet<>(ldapGroupsMap.keySet()); - for (String groupName : copy) { - if (!ldapGroupNames.contains(groupName)) { - LDAPObject ldapGroup = ldapGroupsMap.remove(groupName); - ldapProvider.getLdapIdentityStore().remove(ldapGroup); - syncResult.increaseRemoved(); - } - } - } - - // Finally process memberships, - if (config.isPreserveGroupsInheritance()) { - for (GroupModel kcGroup : realm.getTopLevelGroups()) { - processLdapGroupMembershipsSyncToLDAP(kcGroup, ldapGroupsMap); - } - } - - return syncResult; - } - - // For given kcGroup check if it exists in LDAP (map) by name - // If not, create it in LDAP including attributes. Otherwise update attributes in LDAP. - // Process this recursively for all subgroups of KC group - private void processLdapGroupSyncToLDAP(GroupModel kcGroup, Map ldapGroupsMap, Set ldapGroupNames, UserFederationSyncResult syncResult) { - String groupName = kcGroup.getName(); - - // extract group attributes to be updated to LDAP - Map> supportedLdapAttributes = new HashMap<>(); - for (String attrName : config.getGroupAttributes()) { - List kcAttrValues = kcGroup.getAttribute(attrName); - Set attrValues2 = (kcAttrValues == null || kcAttrValues.isEmpty()) ? null : new HashSet<>(kcAttrValues); - supportedLdapAttributes.put(attrName, attrValues2); - } - - LDAPObject ldapGroup = ldapGroupsMap.get(groupName); - - if (ldapGroup == null) { - ldapGroup = createLDAPGroup(groupName, supportedLdapAttributes); - syncResult.increaseAdded(); - } else { - for (Map.Entry> attrEntry : supportedLdapAttributes.entrySet()) { - ldapGroup.setAttribute(attrEntry.getKey(), attrEntry.getValue()); - } - - ldapProvider.getLdapIdentityStore().update(ldapGroup); - syncResult.increaseUpdated(); - } - - ldapGroupsMap.put(groupName, ldapGroup); - ldapGroupNames.add(groupName); - - // process KC subgroups - for (GroupModel kcSubgroup : kcGroup.getSubGroups()) { - processLdapGroupSyncToLDAP(kcSubgroup, ldapGroupsMap, ldapGroupNames, syncResult); - } - } - - // Sync memberships update. Update memberships of group in LDAP based on subgroups from KC. Do it recursively - private void processLdapGroupMembershipsSyncToLDAP(GroupModel kcGroup, Map ldapGroupsMap) { - LDAPObject ldapGroup = ldapGroupsMap.get(kcGroup.getName()); - Set toRemoveSubgroupsDNs = getLDAPSubgroups(ldapGroup); - - // Add LDAP subgroups, which are KC subgroups - Set kcSubgroups = kcGroup.getSubGroups(); - for (GroupModel kcSubgroup : kcSubgroups) { - LDAPObject ldapSubgroup = ldapGroupsMap.get(kcSubgroup.getName()); - LDAPUtils.addMember(ldapProvider, MembershipType.DN, config.getMembershipLdapAttribute(), ldapGroup, ldapSubgroup, false); - toRemoveSubgroupsDNs.remove(ldapSubgroup.getDn()); - } - - // Remove LDAP subgroups, which are not members in KC anymore - for (LDAPDn toRemoveDN : toRemoveSubgroupsDNs) { - LDAPObject fakeGroup = new LDAPObject(); - fakeGroup.setDn(toRemoveDN); - LDAPUtils.deleteMember(ldapProvider, MembershipType.DN, config.getMembershipLdapAttribute(), ldapGroup, fakeGroup, false); - } - - // Update group to LDAP - if (!kcGroup.getSubGroups().isEmpty() || !toRemoveSubgroupsDNs.isEmpty()) { - ldapProvider.getLdapIdentityStore().update(ldapGroup); - } - - for (GroupModel kcSubgroup : kcGroup.getSubGroups()) { - processLdapGroupMembershipsSyncToLDAP(kcSubgroup, ldapGroupsMap); - } - } - - - // group-user membership operations - - - @Override - public List getGroupMembers(GroupModel kcGroup, int firstResult, int maxResults) { - LDAPObject ldapGroup = loadLDAPGroupByName(kcGroup.getName()); - if (ldapGroup == null) { - return Collections.emptyList(); - } - - MembershipType membershipType = config.getMembershipTypeLdapAttribute(); - return membershipType.getGroupMembers(this, ldapGroup, firstResult, maxResults); - } - - public void addGroupMappingInLDAP(String groupName, LDAPObject ldapUser) { - LDAPObject ldapGroup = loadLDAPGroupByName(groupName); - if (ldapGroup == null) { - syncDataFromKeycloakToFederationProvider(); - ldapGroup = loadLDAPGroupByName(groupName); - } - - LDAPUtils.addMember(ldapProvider, config.getMembershipTypeLdapAttribute(), config.getMembershipLdapAttribute(), ldapGroup, ldapUser, true); - } - - public void deleteGroupMappingInLDAP(LDAPObject ldapUser, LDAPObject ldapGroup) { - LDAPUtils.deleteMember(ldapProvider, config.getMembershipTypeLdapAttribute(), config.getMembershipLdapAttribute(), ldapGroup, ldapUser, true); - } - - protected List getLDAPGroupMappings(LDAPObject ldapUser) { - String strategyKey = config.getUserGroupsRetrieveStrategy(); - UserRolesRetrieveStrategy strategy = factory.getUserGroupsRetrieveStrategy(strategyKey); - return strategy.getLDAPRoleMappings(this, ldapUser); - } - - public void beforeLDAPQuery(LDAPQuery query) { - String strategyKey = config.getUserGroupsRetrieveStrategy(); - UserRolesRetrieveStrategy strategy = factory.getUserGroupsRetrieveStrategy(strategyKey); - strategy.beforeUserLDAPQuery(query); - } - - public UserModel proxy(LDAPObject ldapUser, UserModel delegate) { - final LDAPGroupMapperMode mode = config.getMode(); - - // For IMPORT mode, all operations are performed against local DB - if (mode == LDAPGroupMapperMode.IMPORT) { - return delegate; - } else { - return new LDAPGroupMappingsUserDelegate(delegate, ldapUser); - } - } - - public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser) { - } - - public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, boolean isCreate) { - LDAPGroupMapperMode mode = config.getMode(); - - // For now, import LDAP group mappings just during create - if (mode == LDAPGroupMapperMode.IMPORT && isCreate) { - - List ldapGroups = getLDAPGroupMappings(ldapUser); - - // Import role mappings from LDAP into Keycloak DB - for (LDAPObject ldapGroup : ldapGroups) { - - GroupModel kcGroup = findKcGroupOrSyncFromLDAP(ldapGroup, user); - if (kcGroup != null) { - logger.debugf("User '%s' joins group '%s' during import from LDAP", user.getUsername(), kcGroup.getName()); - user.joinGroup(kcGroup); - } - } - } - } - - - public class LDAPGroupMappingsUserDelegate extends UserModelDelegate { - - private final LDAPObject ldapUser; - - // Avoid loading group mappings from LDAP more times per-request - private Set cachedLDAPGroupMappings; - - public LDAPGroupMappingsUserDelegate(UserModel user, LDAPObject ldapUser) { - super(user); - this.ldapUser = ldapUser; - } - - @Override - public boolean hasRole(RoleModel role) { - return super.hasRole(role) || RoleUtils.hasRoleFromGroup(getGroups(), role, true); - } - - @Override - public Set getGroups() { - Set ldapGroupMappings = getLDAPGroupMappingsConverted(); - if (config.getMode() == LDAPGroupMapperMode.LDAP_ONLY) { - // Use just group mappings from LDAP - return ldapGroupMappings; - } else { - // Merge mappings from both DB and LDAP - Set modelGroupMappings = super.getGroups(); - ldapGroupMappings.addAll(modelGroupMappings); - return ldapGroupMappings; - } - } - - @Override - public void joinGroup(GroupModel group) { - if (config.getMode() == LDAPGroupMapperMode.LDAP_ONLY) { - // We need to create new role mappings in LDAP - cachedLDAPGroupMappings = null; - addGroupMappingInLDAP(group.getName(), ldapUser); - } else { - super.joinGroup(group); - } - } - - @Override - public void leaveGroup(GroupModel group) { - LDAPQuery ldapQuery = createGroupQuery(); - LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder(); - Condition roleNameCondition = conditionsBuilder.equal(config.getGroupNameLdapAttribute(), group.getName()); - String membershipUserAttr = LDAPUtils.getMemberValueOfChildObject(ldapUser, config.getMembershipTypeLdapAttribute()); - Condition membershipCondition = conditionsBuilder.equal(config.getMembershipLdapAttribute(), membershipUserAttr); - ldapQuery.addWhereCondition(roleNameCondition).addWhereCondition(membershipCondition); - LDAPObject ldapGroup = ldapQuery.getFirstResult(); - - if (ldapGroup == null) { - // Group mapping doesn't exist in LDAP. For LDAP_ONLY mode, we don't need to do anything. For READ_ONLY, delete it in local DB. - if (config.getMode() == LDAPGroupMapperMode.READ_ONLY) { - super.leaveGroup(group); - } - } else { - // Group mappings exists in LDAP. For LDAP_ONLY mode, we can just delete it in LDAP. For READ_ONLY we can't delete it -> throw error - if (config.getMode() == LDAPGroupMapperMode.READ_ONLY) { - throw new ModelException("Not possible to delete LDAP group mappings as mapper mode is READ_ONLY"); - } else { - // Delete ldap role mappings - cachedLDAPGroupMappings = null; - deleteGroupMappingInLDAP(ldapUser, ldapGroup); - } - } - } - - @Override - public boolean isMemberOf(GroupModel group) { - Set ldapGroupMappings = getGroups(); - return ldapGroupMappings.contains(group); - } - - protected Set getLDAPGroupMappingsConverted() { - if (cachedLDAPGroupMappings != null) { - return new HashSet<>(cachedLDAPGroupMappings); - } - - List ldapGroups = getLDAPGroupMappings(ldapUser); - - Set result = new HashSet<>(); - for (LDAPObject ldapGroup : ldapGroups) { - GroupModel kcGroup = findKcGroupOrSyncFromLDAP(ldapGroup, this); - if (kcGroup != null) { - result.add(kcGroup); - } - } - - cachedLDAPGroupMappings = new HashSet<>(result); - - return result; - } - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java deleted file mode 100644 index a9c5056275c..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership.group; - -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.LDAPUtils; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapperFactory; -import org.keycloak.federation.ldap.mappers.membership.CommonLDAPGroupMapperConfig; -import org.keycloak.federation.ldap.mappers.membership.LDAPGroupMapperMode; -import org.keycloak.federation.ldap.mappers.membership.MembershipType; -import org.keycloak.federation.ldap.mappers.membership.UserRolesRetrieveStrategy; -import org.keycloak.federation.ldap.mappers.membership.role.RoleMapperConfig; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.provider.ProviderConfigProperty; -import org.keycloak.representations.idm.UserFederationMapperSyncConfigRepresentation; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * @author Marek Posolda - */ -public class GroupLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory { - - public static final String PROVIDER_ID = "group-ldap-mapper"; - - protected static final List configProperties = new ArrayList<>(); - protected static final Map userGroupsStrategies = new LinkedHashMap<>(); - - // TODO: Merge with RoleLDAPFederationMapperFactory as there are lot of similar properties - static { - userGroupsStrategies.put(GroupMapperConfig.LOAD_GROUPS_BY_MEMBER_ATTRIBUTE, new UserRolesRetrieveStrategy.LoadRolesByMember()); - userGroupsStrategies.put(GroupMapperConfig.GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE, new UserRolesRetrieveStrategy.GetRolesFromUserMemberOfAttribute()); - userGroupsStrategies.put(GroupMapperConfig.LOAD_GROUPS_BY_MEMBER_ATTRIBUTE_RECURSIVELY, new UserRolesRetrieveStrategy.LoadRolesByMemberRecursively()); - - ProviderConfigProperty groupsDn = createConfigProperty(GroupMapperConfig.GROUPS_DN, "LDAP Groups DN", - "LDAP DN where are groups of this tree saved. For example 'ou=groups,dc=example,dc=org' ", ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(groupsDn); - - ProviderConfigProperty groupNameLDAPAttribute = createConfigProperty(GroupMapperConfig.GROUP_NAME_LDAP_ATTRIBUTE, "Group Name LDAP Attribute", - "Name of LDAP attribute, which is used in group objects for name and RDN of group. Usually it will be 'cn' . In this case typical group/role object may have DN like 'cn=Group1,ou=groups,dc=example,dc=org' ", - ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(groupNameLDAPAttribute); - - ProviderConfigProperty groupObjectClasses = createConfigProperty(GroupMapperConfig.GROUP_OBJECT_CLASSES, "Group Object Classes", - "Object class (or classes) of the group object. It's divided by comma if more classes needed. In typical LDAP deployment it could be 'groupOfNames' . In Active Directory it's usually 'group' ", - ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(groupObjectClasses); - - ProviderConfigProperty preserveGroupInheritance = createConfigProperty(GroupMapperConfig.PRESERVE_GROUP_INHERITANCE, "Preserve Group Inheritance", - "Flag whether group inheritance from LDAP should be propagated to Keycloak. If false, then all LDAP groups will be mapped as flat top-level groups in Keycloak. Otherwise group inheritance is " + - "preserved into Keycloak, but the group sync might fail if LDAP structure contains recursions or multiple parent groups per child groups", - ProviderConfigProperty.BOOLEAN_TYPE, null); - configProperties.add(preserveGroupInheritance); - - ProviderConfigProperty membershipLDAPAttribute = createConfigProperty(GroupMapperConfig.MEMBERSHIP_LDAP_ATTRIBUTE, "Membership LDAP Attribute", - "Name of LDAP attribute on group, which is used for membership mappings. Usually it will be 'member' ", - ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(membershipLDAPAttribute); - - List membershipTypes = new LinkedList<>(); - for (MembershipType membershipType : MembershipType.values()) { - membershipTypes.add(membershipType.toString()); - } - ProviderConfigProperty membershipType = createConfigProperty(GroupMapperConfig.MEMBERSHIP_ATTRIBUTE_TYPE, "Membership Attribute Type", - "DN means that LDAP group has it's members declared in form of their full DN. For example 'member: uid=john,ou=users,dc=example,dc=com' . " + - "UID means that LDAP group has it's members declared in form of pure user uids. For example 'memberUid: john' .", - ProviderConfigProperty.LIST_TYPE, membershipTypes); - configProperties.add(membershipType); - - ProviderConfigProperty ldapFilter = createConfigProperty(GroupMapperConfig.GROUPS_LDAP_FILTER, - "LDAP Filter", - "LDAP Filter adds additional custom filter to the whole query for retrieve LDAP groups. Leave this empty if no additional filtering is needed and you want to retrieve all groups from LDAP. Otherwise make sure that filter starts with '(' and ends with ')'", - ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(ldapFilter); - - List modes = new LinkedList<>(); - for (LDAPGroupMapperMode mode : LDAPGroupMapperMode.values()) { - modes.add(mode.toString()); - } - ProviderConfigProperty mode = createConfigProperty(GroupMapperConfig.MODE, "Mode", - "LDAP_ONLY means that all group mappings of users are retrieved from LDAP and saved into LDAP. READ_ONLY is Read-only LDAP mode where group mappings are " + - "retrieved from both LDAP and DB and merged together. New group joins are not saved to LDAP but to DB. IMPORT is Read-only LDAP mode where group mappings are " + - "retrieved from LDAP just at the time when user is imported from LDAP and then " + - "they are saved to local keycloak DB.", - ProviderConfigProperty.LIST_TYPE, modes); - configProperties.add(mode); - - List roleRetrievers = new LinkedList<>(userGroupsStrategies.keySet()); - ProviderConfigProperty retriever = createConfigProperty(GroupMapperConfig.USER_ROLES_RETRIEVE_STRATEGY, "User Groups Retrieve Strategy", - "Specify how to retrieve groups of user. LOAD_GROUPS_BY_MEMBER_ATTRIBUTE means that roles of user will be retrieved by sending LDAP query to retrieve all groups where 'member' is our user. " + - "GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE means that groups of user will be retrieved from 'memberOf' attribute of our user. " + - "LOAD_GROUPS_BY_MEMBER_ATTRIBUTE_RECURSIVELY is applicable just in Active Directory and it means that groups of user will be retrieved recursively with usage of LDAP_MATCHING_RULE_IN_CHAIN Ldap extension." - , - ProviderConfigProperty.LIST_TYPE, roleRetrievers); - configProperties.add(retriever); - - ProviderConfigProperty mappedGroupAttributes = createConfigProperty(GroupMapperConfig.MAPPED_GROUP_ATTRIBUTES, "Mapped Group Attributes", - "List of names of attributes divided by comma. This points to the list of attributes on LDAP group, which will be mapped as attributes of Group in Keycloak. " + - "Leave this empty if no additional group attributes are required to be mapped in Keycloak. ", - ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(mappedGroupAttributes); - - ProviderConfigProperty dropNonExistingGroupsDuringSync = createConfigProperty(GroupMapperConfig.DROP_NON_EXISTING_GROUPS_DURING_SYNC, "Drop non-existing groups during sync", - "If this flag is true, then during sync of groups from LDAP to Keycloak, we will keep just those Keycloak groups, which still exists in LDAP. Rest will be deleted", - ProviderConfigProperty.BOOLEAN_TYPE, null); - configProperties.add(dropNonExistingGroupsDuringSync); - } - - @Override - public String getHelpText() { - return "Used to map group mappings of groups from some LDAP DN to Keycloak group mappings"; - } - - @Override - public String getDisplayCategory() { - return GROUP_MAPPER_CATEGORY; - } - - @Override - public String getDisplayType() { - return "Group mappings"; - } - - @Override - public List getConfigProperties() { - return configProperties; - } - - @Override - public Map getDefaultConfig(UserFederationProviderModel providerModel) { - Map defaultValues = new HashMap<>(); - LDAPConfig config = new LDAPConfig(providerModel.getConfig()); - - defaultValues.put(GroupMapperConfig.GROUP_NAME_LDAP_ATTRIBUTE, LDAPConstants.CN); - - String roleObjectClasses = config.isActiveDirectory() ? LDAPConstants.GROUP : LDAPConstants.GROUP_OF_NAMES; - defaultValues.put(GroupMapperConfig.GROUP_OBJECT_CLASSES, roleObjectClasses); - - defaultValues.put(GroupMapperConfig.PRESERVE_GROUP_INHERITANCE, "true"); - defaultValues.put(GroupMapperConfig.MEMBERSHIP_LDAP_ATTRIBUTE, LDAPConstants.MEMBER); - defaultValues.put(GroupMapperConfig.MEMBERSHIP_ATTRIBUTE_TYPE, MembershipType.DN.toString()); - - String mode = config.getEditMode() == UserFederationProvider.EditMode.WRITABLE ? LDAPGroupMapperMode.LDAP_ONLY.toString() : LDAPGroupMapperMode.READ_ONLY.toString(); - defaultValues.put(GroupMapperConfig.MODE, mode); - defaultValues.put(RoleMapperConfig.USER_ROLES_RETRIEVE_STRATEGY, GroupMapperConfig.LOAD_GROUPS_BY_MEMBER_ATTRIBUTE); - - defaultValues.put(GroupMapperConfig.DROP_NON_EXISTING_GROUPS_DURING_SYNC, "false"); - - return defaultValues; - } - - @Override - public String getId() { - return PROVIDER_ID; - } - - @Override - public UserFederationMapperSyncConfigRepresentation getSyncConfig() { - return new UserFederationMapperSyncConfigRepresentation(true, "sync-ldap-groups-to-keycloak", true, "sync-keycloak-groups-to-ldap"); - } - - @Override - public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException { - checkMandatoryConfigAttribute(GroupMapperConfig.GROUPS_DN, "LDAP Groups DN", mapperModel); - checkMandatoryConfigAttribute(GroupMapperConfig.MODE, "Mode", mapperModel); - - String mt = mapperModel.getConfig().get(CommonLDAPGroupMapperConfig.MEMBERSHIP_ATTRIBUTE_TYPE); - MembershipType membershipType = mt==null ? MembershipType.DN : Enum.valueOf(MembershipType.class, mt); - boolean preserveGroupInheritance = Boolean.parseBoolean(mapperModel.getConfig().get(GroupMapperConfig.PRESERVE_GROUP_INHERITANCE)); - if (preserveGroupInheritance && membershipType != MembershipType.DN) { - throw new FederationConfigValidationException("ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType"); - } - - LDAPUtils.validateCustomLdapFilter(mapperModel.getConfig().get(GroupMapperConfig.GROUPS_LDAP_FILTER)); - } - - @Override - protected AbstractLDAPFederationMapper createMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider federationProvider, RealmModel realm) { - return new GroupLDAPFederationMapper(mapperModel, federationProvider, realm, this); - } - - protected UserRolesRetrieveStrategy getUserGroupsRetrieveStrategy(String strategyKey) { - return userGroupsStrategies.get(strategyKey); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupMapperConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupMapperConfig.java deleted file mode 100644 index ae27a8806c7..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupMapperConfig.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership.group; - -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.membership.CommonLDAPGroupMapperConfig; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelException; -import org.keycloak.models.UserFederationMapperModel; - -import java.util.Collection; -import java.util.Collections; - -/** - * @author Marek Posolda - */ -public class GroupMapperConfig extends CommonLDAPGroupMapperConfig { - - // LDAP DN where are groups of this tree saved. - public static final String GROUPS_DN = "groups.dn"; - - // Name of LDAP attribute, which is used in group objects for name and RDN of group. Usually it will be "cn" - public static final String GROUP_NAME_LDAP_ATTRIBUTE = "group.name.ldap.attribute"; - - // Object classes of the group object. - public static final String GROUP_OBJECT_CLASSES = "group.object.classes"; - - // Flag whether group inheritance from LDAP should be propagated to Keycloak group inheritance. - public static final String PRESERVE_GROUP_INHERITANCE = "preserve.group.inheritance"; - - // Customized LDAP filter which is added to the whole LDAP query - public static final String GROUPS_LDAP_FILTER = "groups.ldap.filter"; - - // Name of attributes of the LDAP group object, which will be mapped as attributes of Group in Keycloak - public static final String MAPPED_GROUP_ATTRIBUTES = "mapped.group.attributes"; - - // During sync of groups from LDAP to Keycloak, we will keep just those Keycloak groups, which still exists in LDAP. Rest will be deleted - public static final String DROP_NON_EXISTING_GROUPS_DURING_SYNC = "drop.non.existing.groups.during.sync"; - - // See UserRolesRetrieveStrategy - public static final String LOAD_GROUPS_BY_MEMBER_ATTRIBUTE = "LOAD_GROUPS_BY_MEMBER_ATTRIBUTE"; - public static final String GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE = "GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE"; - public static final String LOAD_GROUPS_BY_MEMBER_ATTRIBUTE_RECURSIVELY = "LOAD_GROUPS_BY_MEMBER_ATTRIBUTE_RECURSIVELY"; - - public GroupMapperConfig(UserFederationMapperModel mapperModel) { - super(mapperModel); - } - - - public String getGroupsDn() { - String groupsDn = mapperModel.getConfig().get(GROUPS_DN); - if (groupsDn == null) { - throw new ModelException("Groups DN is null! Check your configuration"); - } - return groupsDn; - } - - @Override - public String getLDAPGroupsDn() { - return getGroupsDn(); - } - - public String getGroupNameLdapAttribute() { - String rolesRdnAttr = mapperModel.getConfig().get(GROUP_NAME_LDAP_ATTRIBUTE); - return rolesRdnAttr!=null ? rolesRdnAttr : LDAPConstants.CN; - } - - @Override - public String getLDAPGroupNameLdapAttribute() { - return getGroupNameLdapAttribute(); - } - - public boolean isPreserveGroupsInheritance() { - return AbstractLDAPFederationMapper.parseBooleanParameter(mapperModel, PRESERVE_GROUP_INHERITANCE); - } - - public String getMembershipLdapAttribute() { - String membershipAttrName = mapperModel.getConfig().get(MEMBERSHIP_LDAP_ATTRIBUTE); - return membershipAttrName!=null ? membershipAttrName : LDAPConstants.MEMBER; - } - - public Collection getGroupObjectClasses(LDAPFederationProvider ldapProvider) { - String objectClasses = mapperModel.getConfig().get(GROUP_OBJECT_CLASSES); - if (objectClasses == null) { - // For Active directory, the default is 'group' . For other servers 'groupOfNames' - objectClasses = ldapProvider.getLdapIdentityStore().getConfig().isActiveDirectory() ? LDAPConstants.GROUP : LDAPConstants.GROUP_OF_NAMES; - } - - return getConfigValues(objectClasses); - } - - public Collection getGroupAttributes() { - String groupAttrs = mapperModel.getConfig().get(MAPPED_GROUP_ATTRIBUTES); - return (groupAttrs == null) ? Collections.emptySet() : getConfigValues(groupAttrs); - } - - public String getCustomLdapFilter() { - return mapperModel.getConfig().get(GROUPS_LDAP_FILTER); - } - - public boolean isDropNonExistingGroupsDuringSync() { - return AbstractLDAPFederationMapper.parseBooleanParameter(mapperModel, DROP_NON_EXISTING_GROUPS_DURING_SYNC); - } - - public String getUserGroupsRetrieveStrategy() { - String strategyString = mapperModel.getConfig().get(USER_ROLES_RETRIEVE_STRATEGY); - return strategyString!=null ? strategyString : LOAD_GROUPS_BY_MEMBER_ATTRIBUTE; - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupTreeResolver.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupTreeResolver.java deleted file mode 100644 index cfe4dbfeff9..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupTreeResolver.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership.group; - -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; - -/** - * @author Marek Posolda - */ -public class GroupTreeResolver { - - - /** - * Fully resolves list of group trees to be used in Keycloak. The input is group info (usually from LDAP) where each "Group" object contains - * just it's name and direct children. - * - * The operation also performs validation as rules for LDAP are less strict than for Keycloak (In LDAP, the recursion is possible and multiple parents of single group is also allowed) - * - * @param groups - * @return - * @throws GroupTreeResolveException - */ - public List resolveGroupTree(List groups) throws GroupTreeResolveException { - // 1- Get parents of each group - Map> parentsTree = getParentsTree(groups); - - // 2 - Get rootGroups (groups without parent) and check if there is no group with multiple parents - List rootGroups = new LinkedList<>(); - for (Map.Entry> group : parentsTree.entrySet()) { - int parentCount = group.getValue().size(); - if (parentCount == 0) { - rootGroups.add(group.getKey()); - } else if (parentCount > 1) { - throw new GroupTreeResolveException("Group '" + group.getKey() + "' detected to have multiple parents. This is not allowed in Keycloak. Parents are: " + group.getValue()); - } - } - - // 3 - Just convert to map for easier retrieval - Map asMap = new TreeMap<>(); - for (Group group : groups) { - asMap.put(group.getGroupName(), group); - } - - // 4 - Now we have rootGroups. Let's resolve them - List finalResult = new LinkedList<>(); - Set visitedGroups = new TreeSet<>(); - for (String rootGroupName : rootGroups) { - List subtree = new LinkedList<>(); - subtree.add(rootGroupName); - GroupTreeEntry groupTree = resolveGroupTree(rootGroupName, asMap, visitedGroups, subtree); - finalResult.add(groupTree); - } - - - // 5 - Check recursion - if (visitedGroups.size() != asMap.size()) { - // Recursion detected. Try to find where it is - for (Map.Entry entry : asMap.entrySet()) { - String groupName = entry.getKey(); - if (!visitedGroups.contains(groupName)) { - List subtree = new LinkedList<>(); - subtree.add(groupName); - - Set newVisitedGroups = new TreeSet<>(); - resolveGroupTree(groupName, asMap, newVisitedGroups, subtree); - visitedGroups.addAll(newVisitedGroups); - } - } - - // Shouldn't happen - throw new GroupTreeResolveException("Illegal state: Recursion detected, but wasn't able to find it"); - } - - return finalResult; - } - - private Map> getParentsTree(List groups) throws GroupTreeResolveException { - Map> result = new TreeMap<>(); - - for (Group group : groups) { - result.put(group.getGroupName(), new LinkedList()); - } - - for (Group group : groups) { - for (String child : group.getChildrenNames()) { - List list = result.get(child); - if (list == null) { - throw new GroupTreeResolveException("Group '" + child + "' referenced as member of group '" + group.getGroupName() + "' doesn't exists"); - } - list.add(group.getGroupName()); - } - } - return result; - } - - private GroupTreeEntry resolveGroupTree(String groupName, Map asMap, Set visitedGroups, List currentSubtree) throws GroupTreeResolveException { - if (visitedGroups.contains(groupName)) { - throw new GroupTreeResolveException("Recursion detected when trying to resolve group '" + groupName + "'. Whole recursion path: " + currentSubtree); - } - - visitedGroups.add(groupName); - - Group group = asMap.get(groupName); - - List children = new LinkedList<>(); - GroupTreeEntry result = new GroupTreeEntry(group.getGroupName(), children); - - for (String childrenName : group.getChildrenNames()) { - List subtreeCopy = new LinkedList<>(currentSubtree); - subtreeCopy.add(childrenName); - GroupTreeEntry childEntry = resolveGroupTree(childrenName, asMap, visitedGroups, subtreeCopy); - children.add(childEntry); - } - - return result; - } - - - - // static classes - - public static class GroupTreeResolveException extends Exception { - - public GroupTreeResolveException(String message) { - super(message); - } - } - - - public static class Group { - - private final String groupName; - private final List childrenNames; - - public Group(String groupName, String... childrenNames) { - this(groupName, Arrays.asList(childrenNames)); - } - - public Group(String groupName, Collection childrenNames) { - this.groupName = groupName; - this.childrenNames = new LinkedList<>(childrenNames); - } - - public String getGroupName() { - return groupName; - } - - public List getChildrenNames() { - return childrenNames; - } - } - - public static class GroupTreeEntry { - - private final String groupName; - private final List children; - - public GroupTreeEntry(String groupName, List children) { - this.groupName = groupName; - this.children = children; - } - - public String getGroupName() { - return groupName; - } - - public List getChildren() { - return children; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("{ " + groupName + " -> [ "); - for (GroupTreeEntry child : children) { - builder.append(child.toString()); - } - builder.append(" ]}"); - - return builder.toString(); - } - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java deleted file mode 100644 index b45f348fabe..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership.role; - -import org.jboss.logging.Logger; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.LDAPUtils; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.Condition; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.membership.CommonLDAPGroupMapper; -import org.keycloak.federation.ldap.mappers.membership.CommonLDAPGroupMapperConfig; -import org.keycloak.federation.ldap.mappers.membership.LDAPGroupMapperMode; -import org.keycloak.federation.ldap.mappers.membership.UserRolesRetrieveStrategy; -import org.keycloak.models.ClientModel; -import org.keycloak.models.ModelException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleContainerModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationSyncResult; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.models.utils.RoleUtils; -import org.keycloak.models.utils.UserModelDelegate; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Map realm roles or roles of particular client to LDAP groups - * - * @author Marek Posolda - */ -public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper implements CommonLDAPGroupMapper { - - private static final Logger logger = Logger.getLogger(RoleLDAPFederationMapper.class); - - private final RoleMapperConfig config; - private final RoleLDAPFederationMapperFactory factory; - - public RoleLDAPFederationMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm, RoleLDAPFederationMapperFactory factory) { - super(mapperModel, ldapProvider, realm); - this.config = new RoleMapperConfig(mapperModel); - this.factory = factory; - } - - - @Override - public LDAPQuery createLDAPGroupQuery() { - return createRoleQuery(); - } - - @Override - public CommonLDAPGroupMapperConfig getConfig() { - return config; - } - - - @Override - public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, boolean isCreate) { - LDAPGroupMapperMode mode = config.getMode(); - - // For now, import LDAP role mappings just during create - if (mode == LDAPGroupMapperMode.IMPORT && isCreate) { - - List ldapRoles = getLDAPRoleMappings(ldapUser); - - // Import role mappings from LDAP into Keycloak DB - String roleNameAttr = config.getRoleNameLdapAttribute(); - for (LDAPObject ldapRole : ldapRoles) { - String roleName = ldapRole.getAttributeAsString(roleNameAttr); - - RoleContainerModel roleContainer = getTargetRoleContainer(); - RoleModel role = roleContainer.getRole(roleName); - - if (role == null) { - role = roleContainer.addRole(roleName); - } - - logger.debugf("Granting role [%s] to user [%s] during import from LDAP", roleName, user.getUsername()); - user.grantRole(role); - } - } - } - - @Override - public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser) { - } - - - // Sync roles from LDAP to Keycloak DB - @Override - public UserFederationSyncResult syncDataFromFederationProviderToKeycloak() { - UserFederationSyncResult syncResult = new UserFederationSyncResult() { - - @Override - public String getStatus() { - return String.format("%d imported roles, %d roles already exists in Keycloak", getAdded(), getUpdated()); - } - - }; - - logger.debugf("Syncing roles from LDAP into Keycloak DB. Mapper is [%s], LDAP provider is [%s]", mapperModel.getName(), ldapProvider.getModel().getDisplayName()); - - // Send LDAP query to load all roles - LDAPQuery ldapRoleQuery = createRoleQuery(); - List ldapRoles = LDAPUtils.loadAllLDAPObjects(ldapRoleQuery, ldapProvider); - - RoleContainerModel roleContainer = getTargetRoleContainer(); - String rolesRdnAttr = config.getRoleNameLdapAttribute(); - for (LDAPObject ldapRole : ldapRoles) { - String roleName = ldapRole.getAttributeAsString(rolesRdnAttr); - - if (roleContainer.getRole(roleName) == null) { - logger.debugf("Syncing role [%s] from LDAP to keycloak DB", roleName); - roleContainer.addRole(roleName); - syncResult.increaseAdded(); - } else { - syncResult.increaseUpdated(); - } - } - - return syncResult; - } - - - // Sync roles from Keycloak back to LDAP - @Override - public UserFederationSyncResult syncDataFromKeycloakToFederationProvider() { - UserFederationSyncResult syncResult = new UserFederationSyncResult() { - - @Override - public String getStatus() { - return String.format("%d roles imported to LDAP, %d roles already existed in LDAP", getAdded(), getUpdated()); - } - - }; - - if (config.getMode() != LDAPGroupMapperMode.LDAP_ONLY) { - logger.warnf("Ignored sync for federation mapper '%s' as it's mode is '%s'", mapperModel.getName(), config.getMode().toString()); - return syncResult; - } - - logger.debugf("Syncing roles from Keycloak into LDAP. Mapper is [%s], LDAP provider is [%s]", mapperModel.getName(), ldapProvider.getModel().getDisplayName()); - - // Send LDAP query to see which roles exists there - LDAPQuery ldapQuery = createRoleQuery(); - List ldapRoles = ldapQuery.getResultList(); - - Set ldapRoleNames = new HashSet<>(); - String rolesRdnAttr = config.getRoleNameLdapAttribute(); - for (LDAPObject ldapRole : ldapRoles) { - String roleName = ldapRole.getAttributeAsString(rolesRdnAttr); - ldapRoleNames.add(roleName); - } - - - RoleContainerModel roleContainer = getTargetRoleContainer(); - Set keycloakRoles = roleContainer.getRoles(); - - for (RoleModel keycloakRole : keycloakRoles) { - String roleName = keycloakRole.getName(); - if (ldapRoleNames.contains(roleName)) { - syncResult.increaseUpdated(); - } else { - logger.debugf("Syncing role [%s] from Keycloak to LDAP", roleName); - createLDAPRole(roleName); - syncResult.increaseAdded(); - } - } - - return syncResult; - } - - // TODO: Possible to merge with GroupMapper and move to common class - public LDAPQuery createRoleQuery() { - LDAPQuery ldapQuery = new LDAPQuery(ldapProvider); - - // For now, use same search scope, which is configured "globally" and used for user's search. - ldapQuery.setSearchScope(ldapProvider.getLdapIdentityStore().getConfig().getSearchScope()); - - String rolesDn = config.getRolesDn(); - ldapQuery.setSearchDn(rolesDn); - - Collection roleObjectClasses = config.getRoleObjectClasses(ldapProvider); - ldapQuery.addObjectClasses(roleObjectClasses); - - String rolesRdnAttr = config.getRoleNameLdapAttribute(); - - String customFilter = config.getCustomLdapFilter(); - if (customFilter != null && customFilter.trim().length() > 0) { - Condition customFilterCondition = new LDAPQueryConditionsBuilder().addCustomLDAPFilter(customFilter); - ldapQuery.addWhereCondition(customFilterCondition); - } - - String membershipAttr = config.getMembershipLdapAttribute(); - ldapQuery.addReturningLdapAttribute(rolesRdnAttr); - ldapQuery.addReturningLdapAttribute(membershipAttr); - - return ldapQuery; - } - - protected RoleContainerModel getTargetRoleContainer() { - boolean realmRolesMapping = config.isRealmRolesMapping(); - if (realmRolesMapping) { - return realm; - } else { - String clientId = config.getClientId(); - if (clientId == null) { - throw new ModelException("Using client roles mapping is requested, but parameter client.id not found!"); - } - ClientModel client = realm.getClientByClientId(clientId); - if (client == null) { - throw new ModelException("Can't found requested client with clientId: " + clientId); - } - return client; - } - } - - - public LDAPObject createLDAPRole(String roleName) { - LDAPObject ldapRole = LDAPUtils.createLDAPGroup(ldapProvider, roleName, config.getRoleNameLdapAttribute(), config.getRoleObjectClasses(ldapProvider), - config.getRolesDn(), Collections.>emptyMap()); - - logger.debugf("Creating role [%s] to LDAP with DN [%s]", roleName, ldapRole.getDn().toString()); - return ldapRole; - } - - public void addRoleMappingInLDAP(String roleName, LDAPObject ldapUser) { - LDAPObject ldapRole = loadLDAPRoleByName(roleName); - if (ldapRole == null) { - ldapRole = createLDAPRole(roleName); - } - - LDAPUtils.addMember(ldapProvider, config.getMembershipTypeLdapAttribute(), config.getMembershipLdapAttribute(), ldapRole, ldapUser, true); - } - - public void deleteRoleMappingInLDAP(LDAPObject ldapUser, LDAPObject ldapRole) { - LDAPUtils.deleteMember(ldapProvider, config.getMembershipTypeLdapAttribute(), config.getMembershipLdapAttribute(), ldapRole, ldapUser, true); - } - - public LDAPObject loadLDAPRoleByName(String roleName) { - LDAPQuery ldapQuery = createRoleQuery(); - Condition roleNameCondition = new LDAPQueryConditionsBuilder().equal(config.getRoleNameLdapAttribute(), roleName); - ldapQuery.addWhereCondition(roleNameCondition); - return ldapQuery.getFirstResult(); - } - - protected List getLDAPRoleMappings(LDAPObject ldapUser) { - String strategyKey = config.getUserRolesRetrieveStrategy(); - UserRolesRetrieveStrategy strategy = factory.getUserRolesRetrieveStrategy(strategyKey); - return strategy.getLDAPRoleMappings(this, ldapUser); - } - - @Override - public UserModel proxy(LDAPObject ldapUser, UserModel delegate) { - final LDAPGroupMapperMode mode = config.getMode(); - - // For IMPORT mode, all operations are performed against local DB - if (mode == LDAPGroupMapperMode.IMPORT) { - return delegate; - } else { - return new LDAPRoleMappingsUserDelegate(delegate, ldapUser); - } - } - - @Override - public void beforeLDAPQuery(LDAPQuery query) { - String strategyKey = config.getUserRolesRetrieveStrategy(); - UserRolesRetrieveStrategy strategy = factory.getUserRolesRetrieveStrategy(strategyKey); - strategy.beforeUserLDAPQuery(query); - } - - - - public class LDAPRoleMappingsUserDelegate extends UserModelDelegate { - - private final LDAPObject ldapUser; - private final RoleContainerModel roleContainer; - - // Avoid loading role mappings from LDAP more times per-request - private Set cachedLDAPRoleMappings; - - public LDAPRoleMappingsUserDelegate(UserModel user, LDAPObject ldapUser) { - super(user); - this.ldapUser = ldapUser; - this.roleContainer = getTargetRoleContainer(); - } - - @Override - public Set getRealmRoleMappings() { - if (roleContainer.equals(realm)) { - Set ldapRoleMappings = getLDAPRoleMappingsConverted(); - - if (config.getMode() == LDAPGroupMapperMode.LDAP_ONLY) { - // Use just role mappings from LDAP - return ldapRoleMappings; - } else { - // Merge mappings from both DB and LDAP - Set modelRoleMappings = super.getRealmRoleMappings(); - ldapRoleMappings.addAll(modelRoleMappings); - return ldapRoleMappings; - } - } else { - return super.getRealmRoleMappings(); - } - } - - @Override - public Set getClientRoleMappings(ClientModel client) { - if (roleContainer.equals(client)) { - Set ldapRoleMappings = getLDAPRoleMappingsConverted(); - - if (config.getMode() == LDAPGroupMapperMode.LDAP_ONLY) { - // Use just role mappings from LDAP - return ldapRoleMappings; - } else { - // Merge mappings from both DB and LDAP - Set modelRoleMappings = super.getClientRoleMappings(client); - ldapRoleMappings.addAll(modelRoleMappings); - return ldapRoleMappings; - } - } else { - return super.getClientRoleMappings(client); - } - } - - @Override - public boolean hasRole(RoleModel role) { - Set roles = getRoleMappings(); - return RoleUtils.hasRole(roles, role) - || RoleUtils.hasRoleFromGroup(getGroups(), role, true); - } - - @Override - public void grantRole(RoleModel role) { - if (config.getMode() == LDAPGroupMapperMode.LDAP_ONLY) { - - if (role.getContainer().equals(roleContainer)) { - - // We need to create new role mappings in LDAP - cachedLDAPRoleMappings = null; - addRoleMappingInLDAP(role.getName(), ldapUser); - } else { - super.grantRole(role); - } - } else { - super.grantRole(role); - } - } - - @Override - public Set getRoleMappings() { - Set modelRoleMappings = super.getRoleMappings(); - - Set ldapRoleMappings = getLDAPRoleMappingsConverted(); - - if (config.getMode() == LDAPGroupMapperMode.LDAP_ONLY) { - // For LDAP-only we want to retrieve role mappings of target container just from LDAP - Set modelRolesCopy = new HashSet<>(modelRoleMappings); - for (RoleModel role : modelRolesCopy) { - if (role.getContainer().equals(roleContainer)) { - modelRoleMappings.remove(role); - } - } - } - - modelRoleMappings.addAll(ldapRoleMappings); - return modelRoleMappings; - } - - protected Set getLDAPRoleMappingsConverted() { - if (cachedLDAPRoleMappings != null) { - return new HashSet<>(cachedLDAPRoleMappings); - } - - List ldapRoles = getLDAPRoleMappings(ldapUser); - - Set roles = new HashSet<>(); - String roleNameLdapAttr = config.getRoleNameLdapAttribute(); - for (LDAPObject role : ldapRoles) { - String roleName = role.getAttributeAsString(roleNameLdapAttr); - RoleModel modelRole = roleContainer.getRole(roleName); - if (modelRole == null) { - // Add role to local DB - modelRole = roleContainer.addRole(roleName); - } - roles.add(modelRole); - } - - cachedLDAPRoleMappings = new HashSet<>(roles); - - return roles; - } - - @Override - public void deleteRoleMapping(RoleModel role) { - if (role.getContainer().equals(roleContainer)) { - - LDAPQuery ldapQuery = createRoleQuery(); - LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder(); - Condition roleNameCondition = conditionsBuilder.equal(config.getRoleNameLdapAttribute(), role.getName()); - String membershipUserAttr = LDAPUtils.getMemberValueOfChildObject(ldapUser, config.getMembershipTypeLdapAttribute()); - Condition membershipCondition = conditionsBuilder.equal(config.getMembershipLdapAttribute(), membershipUserAttr); - ldapQuery.addWhereCondition(roleNameCondition).addWhereCondition(membershipCondition); - LDAPObject ldapRole = ldapQuery.getFirstResult(); - - if (ldapRole == null) { - // Role mapping doesn't exist in LDAP. For LDAP_ONLY mode, we don't need to do anything. For READ_ONLY, delete it in local DB. - if (config.getMode() == LDAPGroupMapperMode.READ_ONLY) { - super.deleteRoleMapping(role); - } - } else { - // Role mappings exists in LDAP. For LDAP_ONLY mode, we can just delete it in LDAP. For READ_ONLY we can't delete it -> throw error - if (config.getMode() == LDAPGroupMapperMode.READ_ONLY) { - throw new ModelException("Not possible to delete LDAP role mappings as mapper mode is READ_ONLY"); - } else { - // Delete ldap role mappings - cachedLDAPRoleMappings = null; - deleteRoleMappingInLDAP(ldapUser, ldapRole); - } - } - } else { - super.deleteRoleMapping(role); - } - } - } - - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java deleted file mode 100644 index 416e4b4c110..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership.role; - -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.LDAPUtils; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapperFactory; -import org.keycloak.federation.ldap.mappers.membership.LDAPGroupMapperMode; -import org.keycloak.federation.ldap.mappers.membership.MembershipType; -import org.keycloak.federation.ldap.mappers.membership.UserRolesRetrieveStrategy; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.provider.ProviderConfigProperty; -import org.keycloak.representations.idm.UserFederationMapperSyncConfigRepresentation; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * @author Marek Posolda - */ -public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory { - - public static final String PROVIDER_ID = "role-ldap-mapper"; - - protected static final List configProperties = new ArrayList<>(); - protected static final Map userRolesStrategies = new LinkedHashMap<>(); - - - static { - userRolesStrategies.put(RoleMapperConfig.LOAD_ROLES_BY_MEMBER_ATTRIBUTE, new UserRolesRetrieveStrategy.LoadRolesByMember()); - userRolesStrategies.put(RoleMapperConfig.GET_ROLES_FROM_USER_MEMBEROF_ATTRIBUTE, new UserRolesRetrieveStrategy.GetRolesFromUserMemberOfAttribute()); - userRolesStrategies.put(RoleMapperConfig.LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY, new UserRolesRetrieveStrategy.LoadRolesByMemberRecursively()); - - ProviderConfigProperty rolesDn = createConfigProperty(RoleMapperConfig.ROLES_DN, "LDAP Roles DN", - "LDAP DN where are roles of this tree saved. For example 'ou=finance,dc=example,dc=org' ", ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(rolesDn); - - ProviderConfigProperty roleNameLDAPAttribute = createConfigProperty(RoleMapperConfig.ROLE_NAME_LDAP_ATTRIBUTE, "Role Name LDAP Attribute", - "Name of LDAP attribute, which is used in role objects for name and RDN of role. Usually it will be 'cn' . In this case typical group/role object may have DN like 'cn=role1,ou=finance,dc=example,dc=org' ", - ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(roleNameLDAPAttribute); - - ProviderConfigProperty roleObjectClasses = createConfigProperty(RoleMapperConfig.ROLE_OBJECT_CLASSES, "Role Object Classes", - "Object class (or classes) of the role object. It's divided by comma if more classes needed. In typical LDAP deployment it could be 'groupOfNames' . In Active Directory it's usually 'group' ", - ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(roleObjectClasses); - - ProviderConfigProperty membershipLDAPAttribute = createConfigProperty(RoleMapperConfig.MEMBERSHIP_LDAP_ATTRIBUTE, "Membership LDAP Attribute", - "Name of LDAP attribute on role, which is used for membership mappings. Usually it will be 'member' ", - ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(membershipLDAPAttribute); - - - List membershipTypes = new LinkedList<>(); - for (MembershipType membershipType : MembershipType.values()) { - membershipTypes.add(membershipType.toString()); - } - ProviderConfigProperty membershipType = createConfigProperty(RoleMapperConfig.MEMBERSHIP_ATTRIBUTE_TYPE, "Membership Attribute Type", - "DN means that LDAP role has it's members declared in form of their full DN. For example 'member: uid=john,ou=users,dc=example,dc=com' . " + - "UID means that LDAP role has it's members declared in form of pure user uids. For example 'memberUid: john' .", - ProviderConfigProperty.LIST_TYPE, membershipTypes); - configProperties.add(membershipType); - - - ProviderConfigProperty ldapFilter = createConfigProperty(RoleMapperConfig.ROLES_LDAP_FILTER, - "LDAP Filter", - "LDAP Filter adds additional custom filter to the whole query for retrieve LDAP roles. Leave this empty if no additional filtering is needed and you want to retrieve all roles from LDAP. Otherwise make sure that filter starts with '(' and ends with ')'", - ProviderConfigProperty.STRING_TYPE, null); - configProperties.add(ldapFilter); - - - List modes = new LinkedList<>(); - for (LDAPGroupMapperMode mode : LDAPGroupMapperMode.values()) { - modes.add(mode.toString()); - } - ProviderConfigProperty mode = createConfigProperty(RoleMapperConfig.MODE, "Mode", - "LDAP_ONLY means that all role mappings are retrieved from LDAP and saved into LDAP. READ_ONLY is Read-only LDAP mode where role mappings are " + - "retrieved from both LDAP and DB and merged together. New role grants are not saved to LDAP but to DB. IMPORT is Read-only LDAP mode where role mappings are retrieved from LDAP just at the time when user is imported from LDAP and then " + - "they are saved to local keycloak DB.", - ProviderConfigProperty.LIST_TYPE, modes); - configProperties.add(mode); - - - List roleRetrievers = new LinkedList<>(userRolesStrategies.keySet()); - ProviderConfigProperty retriever = createConfigProperty(RoleMapperConfig.USER_ROLES_RETRIEVE_STRATEGY, "User Roles Retrieve Strategy", - "Specify how to retrieve roles of user. LOAD_ROLES_BY_MEMBER_ATTRIBUTE means that roles of user will be retrieved by sending LDAP query to retrieve all roles where 'member' is our user. " + - "GET_ROLES_FROM_USER_MEMBEROF_ATTRIBUTE means that roles of user will be retrieved from 'memberOf' attribute of our user. " + - "LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY is applicable just in Active Directory and it means that roles of user will be retrieved recursively with usage of LDAP_MATCHING_RULE_IN_CHAIN Ldap extension." - , - ProviderConfigProperty.LIST_TYPE, roleRetrievers); - configProperties.add(retriever); - - - ProviderConfigProperty useRealmRolesMappings = createConfigProperty(RoleMapperConfig.USE_REALM_ROLES_MAPPING, "Use Realm Roles Mapping", - "If true, then LDAP role mappings will be mapped to realm role mappings in Keycloak. Otherwise it will be mapped to client role mappings", ProviderConfigProperty.BOOLEAN_TYPE, null); - configProperties.add(useRealmRolesMappings); - - ProviderConfigProperty clientIdProperty = createConfigProperty(RoleMapperConfig.CLIENT_ID, "Client ID", - "Client ID of client to which LDAP role mappings will be mapped. Applicable just if 'Use Realm Roles Mapping' is false", - ProviderConfigProperty.CLIENT_LIST_TYPE, null); - configProperties.add(clientIdProperty); - } - - @Override - public String getHelpText() { - return "Used to map role mappings of roles from some LDAP DN to Keycloak role mappings of either realm roles or client roles of particular client"; - } - - @Override - public String getDisplayCategory() { - return ROLE_MAPPER_CATEGORY; - } - - @Override - public String getDisplayType() { - return "Role mappings"; - } - - @Override - public List getConfigProperties() { - return configProperties; - } - - @Override - public Map getDefaultConfig(UserFederationProviderModel providerModel) { - Map defaultValues = new HashMap<>(); - LDAPConfig config = new LDAPConfig(providerModel.getConfig()); - - defaultValues.put(RoleMapperConfig.ROLE_NAME_LDAP_ATTRIBUTE, LDAPConstants.CN); - - String roleObjectClasses = config.isActiveDirectory() ? LDAPConstants.GROUP : LDAPConstants.GROUP_OF_NAMES; - defaultValues.put(RoleMapperConfig.ROLE_OBJECT_CLASSES, roleObjectClasses); - - defaultValues.put(RoleMapperConfig.MEMBERSHIP_LDAP_ATTRIBUTE, LDAPConstants.MEMBER); - defaultValues.put(RoleMapperConfig.MEMBERSHIP_ATTRIBUTE_TYPE, MembershipType.DN.toString()); - - String mode = config.getEditMode() == UserFederationProvider.EditMode.WRITABLE ? LDAPGroupMapperMode.LDAP_ONLY.toString() : LDAPGroupMapperMode.READ_ONLY.toString(); - defaultValues.put(RoleMapperConfig.MODE, mode); - - defaultValues.put(RoleMapperConfig.USER_ROLES_RETRIEVE_STRATEGY, RoleMapperConfig.LOAD_ROLES_BY_MEMBER_ATTRIBUTE); - defaultValues.put(RoleMapperConfig.USE_REALM_ROLES_MAPPING, "true"); - return defaultValues; - } - - @Override - public String getId() { - return PROVIDER_ID; - } - - @Override - public UserFederationMapperSyncConfigRepresentation getSyncConfig() { - return new UserFederationMapperSyncConfigRepresentation(true, "sync-ldap-roles-to-keycloak", true, "sync-keycloak-roles-to-ldap"); - } - - @Override - public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException { - checkMandatoryConfigAttribute(RoleMapperConfig.ROLES_DN, "LDAP Roles DN", mapperModel); - checkMandatoryConfigAttribute(RoleMapperConfig.MODE, "Mode", mapperModel); - - String realmMappings = mapperModel.getConfig().get(RoleMapperConfig.USE_REALM_ROLES_MAPPING); - boolean useRealmMappings = Boolean.parseBoolean(realmMappings); - if (!useRealmMappings) { - String clientId = mapperModel.getConfig().get(RoleMapperConfig.CLIENT_ID); - if (clientId == null || clientId.trim().isEmpty()) { - throw new FederationConfigValidationException("ldapErrorMissingClientId"); - } - } - - LDAPUtils.validateCustomLdapFilter(mapperModel.getConfig().get(RoleMapperConfig.ROLES_LDAP_FILTER)); - } - - @Override - protected AbstractLDAPFederationMapper createMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider federationProvider, RealmModel realm) { - return new RoleLDAPFederationMapper(mapperModel, federationProvider, realm, this); - } - - protected UserRolesRetrieveStrategy getUserRolesRetrieveStrategy(String strategyKey) { - return userRolesStrategies.get(strategyKey); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleMapperConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleMapperConfig.java deleted file mode 100755 index 950d923839f..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleMapperConfig.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.membership.role; - -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.mappers.membership.CommonLDAPGroupMapperConfig; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelException; -import org.keycloak.models.UserFederationMapperModel; - -import java.util.Collection; - -/** - * @author Marek Posolda - */ -public class RoleMapperConfig extends CommonLDAPGroupMapperConfig { - - // LDAP DN where are roles of this tree saved. - public static final String ROLES_DN = "roles.dn"; - - // Name of LDAP attribute, which is used in role objects for name and RDN of role. Usually it will be "cn" - public static final String ROLE_NAME_LDAP_ATTRIBUTE = "role.name.ldap.attribute"; - - // Object classes of the role object. - public static final String ROLE_OBJECT_CLASSES = "role.object.classes"; - - // Boolean option. If true, we will map LDAP roles to realm roles. If false, we will map to client roles (client specified by option CLIENT_ID) - public static final String USE_REALM_ROLES_MAPPING = "use.realm.roles.mapping"; - - // ClientId, which we want to map roles. Applicable just if "USE_REALM_ROLES_MAPPING" is false - public static final String CLIENT_ID = "client.id"; - - // Customized LDAP filter which is added to the whole LDAP query - public static final String ROLES_LDAP_FILTER = "roles.ldap.filter"; - - // See UserRolesRetrieveStrategy - public static final String LOAD_ROLES_BY_MEMBER_ATTRIBUTE = "LOAD_ROLES_BY_MEMBER_ATTRIBUTE"; - public static final String GET_ROLES_FROM_USER_MEMBEROF_ATTRIBUTE = "GET_ROLES_FROM_USER_MEMBEROF_ATTRIBUTE"; - public static final String LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY = "LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY"; - - - public RoleMapperConfig(UserFederationMapperModel mapperModel) { - super(mapperModel); - } - - public String getRolesDn() { - String rolesDn = mapperModel.getConfig().get(ROLES_DN); - if (rolesDn == null) { - throw new ModelException("Roles DN is null! Check your configuration"); - } - return rolesDn; - } - - @Override - public String getLDAPGroupsDn() { - return getRolesDn(); - } - - public String getRoleNameLdapAttribute() { - String rolesRdnAttr = mapperModel.getConfig().get(ROLE_NAME_LDAP_ATTRIBUTE); - return rolesRdnAttr!=null ? rolesRdnAttr : LDAPConstants.CN; - } - - @Override - public String getLDAPGroupNameLdapAttribute() { - return getRoleNameLdapAttribute(); - } - - public Collection getRoleObjectClasses(LDAPFederationProvider ldapProvider) { - String objectClasses = mapperModel.getConfig().get(ROLE_OBJECT_CLASSES); - if (objectClasses == null) { - // For Active directory, the default is 'group' . For other servers 'groupOfNames' - objectClasses = ldapProvider.getLdapIdentityStore().getConfig().isActiveDirectory() ? LDAPConstants.GROUP : LDAPConstants.GROUP_OF_NAMES; - } - - return getConfigValues(objectClasses); - } - - public String getCustomLdapFilter() { - return mapperModel.getConfig().get(ROLES_LDAP_FILTER); - } - - public boolean isRealmRolesMapping() { - String realmRolesMapping = mapperModel.getConfig().get(USE_REALM_ROLES_MAPPING); - return realmRolesMapping==null || Boolean.parseBoolean(realmRolesMapping); - } - - public String getClientId() { - return mapperModel.getConfig().get(CLIENT_ID); - } - - - public String getUserRolesRetrieveStrategy() { - String strategyString = mapperModel.getConfig().get(USER_ROLES_RETRIEVE_STRATEGY); - return strategyString!=null ? strategyString : LOAD_ROLES_BY_MEMBER_ATTRIBUTE; - } - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapper.java deleted file mode 100644 index d974f3c21c8..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapper.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.msad; - -import org.jboss.logging.Logger; -import org.keycloak.credential.CredentialInput; -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.idm.model.LDAPObject; -import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.PasswordUpdated; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.ModelException; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.UserModelDelegate; - -import javax.naming.AuthenticationException; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Mapper specific to MSAD. It's able to read the userAccountControl and pwdLastSet attributes and set actions in Keycloak based on that. - * It's also able to handle exception code from LDAP user authentication (See http://www-01.ibm.com/support/docview.wss?uid=swg21290631 ) - * - * @author Marek Posolda - */ -public class MSADUserAccountControlMapper extends AbstractLDAPFederationMapper implements PasswordUpdated { - - private static final Logger logger = Logger.getLogger(MSADUserAccountControlMapper.class); - - private static final Pattern AUTH_EXCEPTION_REGEX = Pattern.compile(".*AcceptSecurityContext error, data ([0-9a-f]*), v.*"); - private static final Pattern AUTH_INVALID_NEW_PASSWORD = Pattern.compile(".*error code ([0-9a-f]+) .*WILL_NOT_PERFORM.*"); - - public MSADUserAccountControlMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm) { - super(mapperModel, ldapProvider, realm); - ldapProvider.setUpdater(this); - } - - @Override - public void beforeLDAPQuery(LDAPQuery query) { - query.addReturningLdapAttribute(LDAPConstants.PWD_LAST_SET); - query.addReturningLdapAttribute(LDAPConstants.USER_ACCOUNT_CONTROL); - - // This needs to be read-only and can be set to writable just on demand - query.addReturningReadOnlyLdapAttribute(LDAPConstants.PWD_LAST_SET); - - if (ldapProvider.getEditMode() != UserFederationProvider.EditMode.WRITABLE) { - query.addReturningReadOnlyLdapAttribute(LDAPConstants.USER_ACCOUNT_CONTROL); - } - } - - @Override - public void passwordUpdated(UserModel user, LDAPObject ldapUser, CredentialInput input) { - logger.debugf("Going to update userAccountControl for ldap user '%s' after successful password update", ldapUser.getDn().toString()); - - // Normally it's read-only - ldapUser.removeReadOnlyAttributeName(LDAPConstants.PWD_LAST_SET); - - ldapUser.setSingleAttribute(LDAPConstants.PWD_LAST_SET, "-1"); - - UserAccountControl control = getUserAccountControl(ldapUser); - control.remove(UserAccountControl.PASSWD_NOTREQD); - control.remove(UserAccountControl.PASSWORD_EXPIRED); - - if (user.isEnabled()) { - control.remove(UserAccountControl.ACCOUNTDISABLE); - } - - updateUserAccountControl(ldapUser, control); - } - - @Override - public UserModel proxy(LDAPObject ldapUser, UserModel delegate) { - return new MSADUserModelDelegate(delegate, ldapUser); - } - - @Override - public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser) { - - } - - @Override - public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, boolean isCreate) { - - } - - @Override - public boolean onAuthenticationFailure(LDAPObject ldapUser, UserModel user, AuthenticationException ldapException) { - String exceptionMessage = ldapException.getMessage(); - Matcher m = AUTH_EXCEPTION_REGEX.matcher(exceptionMessage); - if (m.matches()) { - String errorCode = m.group(1); - return processAuthErrorCode(errorCode, user); - } else { - return false; - } - } - - protected boolean processAuthErrorCode(String errorCode, UserModel user) { - logger.debugf("MSAD Error code is '%s' after failed LDAP login of user '%s'", errorCode, user.getUsername()); - - if (ldapProvider.getEditMode() == UserFederationProvider.EditMode.WRITABLE) { - if (errorCode.equals("532") || errorCode.equals("773")) { - // User needs to change his MSAD password. Allow him to login, but add UPDATE_PASSWORD required action - user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD); - return true; - } else if (errorCode.equals("533")) { - // User is disabled in MSAD. Set him to disabled in KC as well - user.setEnabled(false); - return true; - } else if (errorCode.equals("775")) { - logger.warnf("Locked user '%s' attempt to login", user.getUsername()); - } - } - - return false; - } - - - protected ModelException processFailedPasswordUpdateException(ModelException e) { - if (e.getCause() == null || e.getCause().getMessage() == null) { - return e; - } - - String exceptionMessage = e.getCause().getMessage().replace('\n', ' '); - Matcher m = AUTH_INVALID_NEW_PASSWORD.matcher(exceptionMessage); - if (m.matches()) { - String errorCode = m.group(1); - if (errorCode.equals("53")) { - ModelException me = new ModelException("invalidPasswordRegexPatternMessage", e); - me.setParameters(new Object[]{"passwordConstraintViolation"}); - return me; - } - } - - return e; - } - - protected UserAccountControl getUserAccountControl(LDAPObject ldapUser) { - String userAccountControl = ldapUser.getAttributeAsString(LDAPConstants.USER_ACCOUNT_CONTROL); - long longValue = userAccountControl == null ? 0 : Long.parseLong(userAccountControl); - return new UserAccountControl(longValue); - } - - // Update user in LDAP - protected void updateUserAccountControl(LDAPObject ldapUser, UserAccountControl accountControl) { - String userAccountControlValue = String.valueOf(accountControl.getValue()); - logger.debugf("Updating userAccountControl of user '%s' to value '%s'", ldapUser.getDn().toString(), userAccountControlValue); - - ldapUser.setSingleAttribute(LDAPConstants.USER_ACCOUNT_CONTROL, userAccountControlValue); - ldapProvider.getLdapIdentityStore().update(ldapUser); - } - - - - public class MSADUserModelDelegate extends UserModelDelegate { - - private final LDAPObject ldapUser; - - public MSADUserModelDelegate(UserModel delegate, LDAPObject ldapUser) { - super(delegate); - this.ldapUser = ldapUser; - } - - @Override - public boolean isEnabled() { - boolean kcEnabled = super.isEnabled(); - - if (getPwdLastSet() > 0) { - // Merge KC and MSAD - return kcEnabled && !getUserAccountControl(ldapUser).has(UserAccountControl.ACCOUNTDISABLE); - } else { - // If new MSAD user is created and pwdLastSet is still 0, MSAD account is in disabled state. So read just from Keycloak DB. User is not able to login via MSAD anyway - return kcEnabled; - } - } - - @Override - public void setEnabled(boolean enabled) { - // Always update DB - super.setEnabled(enabled); - - if (ldapProvider.getEditMode() == UserFederationProvider.EditMode.WRITABLE && getPwdLastSet() > 0) { - logger.debugf("Going to propagate enabled=%s for ldapUser '%s' to MSAD", enabled, ldapUser.getDn().toString()); - - UserAccountControl control = getUserAccountControl(ldapUser); - if (enabled) { - control.remove(UserAccountControl.ACCOUNTDISABLE); - } else { - control.add(UserAccountControl.ACCOUNTDISABLE); - } - - updateUserAccountControl(ldapUser, control); - } - } - - @Override - public void addRequiredAction(RequiredAction action) { - String actionName = action.name(); - addRequiredAction(actionName); - } - - @Override - public void addRequiredAction(String action) { - // Always update DB - super.addRequiredAction(action); - - if (ldapProvider.getEditMode() == UserFederationProvider.EditMode.WRITABLE && RequiredAction.UPDATE_PASSWORD.toString().equals(action)) { - logger.debugf("Going to propagate required action UPDATE_PASSWORD to MSAD for ldap user '%s' ", ldapUser.getDn().toString()); - - // Normally it's read-only - ldapUser.removeReadOnlyAttributeName(LDAPConstants.PWD_LAST_SET); - - ldapUser.setSingleAttribute(LDAPConstants.PWD_LAST_SET, "0"); - ldapProvider.getLdapIdentityStore().update(ldapUser); - } - } - - @Override - public void removeRequiredAction(RequiredAction action) { - String actionName = action.name(); - removeRequiredAction(actionName); - } - - @Override - public void removeRequiredAction(String action) { - // Always update DB - super.removeRequiredAction(action); - - if (ldapProvider.getEditMode() == UserFederationProvider.EditMode.WRITABLE && RequiredAction.UPDATE_PASSWORD.toString().equals(action)) { - - // Don't set pwdLastSet in MSAD when it is new user - UserAccountControl accountControl = getUserAccountControl(ldapUser); - if (accountControl.getValue() != 0 && !accountControl.has(UserAccountControl.PASSWD_NOTREQD)) { - logger.debugf("Going to remove required action UPDATE_PASSWORD from MSAD for ldap user '%s' ", ldapUser.getDn().toString()); - - // Normally it's read-only - ldapUser.removeReadOnlyAttributeName(LDAPConstants.PWD_LAST_SET); - - ldapUser.setSingleAttribute(LDAPConstants.PWD_LAST_SET, "-1"); - ldapProvider.getLdapIdentityStore().update(ldapUser); - } - } - } - - @Override - public Set getRequiredActions() { - Set requiredActions = super.getRequiredActions(); - - if (ldapProvider.getEditMode() == UserFederationProvider.EditMode.WRITABLE) { - if (getPwdLastSet() == 0 || getUserAccountControl(ldapUser).has(UserAccountControl.PASSWORD_EXPIRED)) { - requiredActions = new HashSet<>(requiredActions); - requiredActions.add(RequiredAction.UPDATE_PASSWORD.toString()); - return requiredActions; - } - } - - return requiredActions; - } - - protected long getPwdLastSet() { - String pwdLastSet = ldapUser.getAttributeAsString(LDAPConstants.PWD_LAST_SET); - return pwdLastSet == null ? 0 : Long.parseLong(pwdLastSet); - } - - - } - -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java deleted file mode 100644 index a8afd1cb9f5..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.msad; - -import org.keycloak.federation.ldap.LDAPFederationProvider; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapperFactory; -import org.keycloak.mappers.FederationConfigValidationException; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserFederationMapperModel; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.provider.ProviderConfigProperty; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * @author Marek Posolda - */ -public class MSADUserAccountControlMapperFactory extends AbstractLDAPFederationMapperFactory { - - public static final String PROVIDER_ID = LDAPConstants.MSAD_USER_ACCOUNT_CONTROL_MAPPER; - protected static final List configProperties = new ArrayList(); - - static { - } - - @Override - public String getHelpText() { - return "Mapper specific to MSAD. It's able to integrate the MSAD user account state into Keycloak account state (account enabled, password is expired etc). It's using userAccountControl and pwdLastSet MSAD attributes for that. " + - "For example if pwdLastSet is 0, the Keycloak user is required to update password, if userAccountControl is 514 (disabled account) the Keycloak user is disabled as well etc. Mapper is also able to handle exception code from LDAP user authentication."; - } - - @Override - public String getDisplayCategory() { - return ATTRIBUTE_MAPPER_CATEGORY; - } - - @Override - public String getDisplayType() { - return "MSAD User Account Control"; - } - - @Override - public List getConfigProperties() { - return configProperties; - } - - @Override - public Map getDefaultConfig(UserFederationProviderModel providerModel) { - return Collections.emptyMap(); - } - - @Override - public String getId() { - return PROVIDER_ID; - } - - @Override - public void validateConfig(RealmModel realm, UserFederationProviderModel fedProviderModel, UserFederationMapperModel mapperModel) throws FederationConfigValidationException { - } - - @Override - protected AbstractLDAPFederationMapper createMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider federationProvider, RealmModel realm) { - return new MSADUserAccountControlMapper(mapperModel, federationProvider, realm); - } -} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/UserAccountControl.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/UserAccountControl.java deleted file mode 100644 index cab01bea4bc..00000000000 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/UserAccountControl.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.federation.ldap.mappers.msad; - -/** - * See https://support.microsoft.com/en-us/kb/305144 - * - * @author Marek Posolda - */ -public class UserAccountControl { - - public static final long SCRIPT = 0x0001L; - public static final long ACCOUNTDISABLE = 0x0002L; - public static final long HOMEDIR_REQUIRED = 0x0008L; - public static final long LOCKOUT = 0x0010L; - public static final long PASSWD_NOTREQD = 0x0020L; - public static final long PASSWD_CANT_CHANGE = 0x0040L; - public static final long ENCRYPTED_TEXT_PWD_ALLOWED = 0x0080L; - public static final long TEMP_DUPLICATE_ACCOUNT = 0x0100L; - public static final long NORMAL_ACCOUNT = 0x0200L; - public static final long INTERDOMAIN_TRUST_ACCOUNT = 0x0800L; - public static final long WORKSTATION_TRUST_ACCOUNT = 0x1000L; - public static final long SERVER_TRUST_ACCOUNT = 0x2000L; - public static final long DONT_EXPIRE_PASSWORD = 0x10000L; - public static final long MNS_LOGON_ACCOUNT = 0x20000L; - public static final long SMARTCARD_REQUIRED = 0x40000L; - public static final long TRUSTED_FOR_DELEGATION = 0x80000L; - public static final long NOT_DELEGATED = 0x100000L; - public static final long USE_DES_KEY_ONLY = 0x200000L; - public static final long DONT_REQ_PREAUTH = 0x400000L; - public static final long PASSWORD_EXPIRED = 0x800000L; - public static final long TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000L; - public static final long PARTIAL_SECRETS_ACCOUNT = 0x04000000L; - - private long value; - - public UserAccountControl(long value) { - this.value = value; - } - - public boolean has(long feature) { - return (this.value & feature) > 0; - } - - public void add(long feature) { - if (!has(feature)) { - this.value += feature; - } - } - - public void remove(long feature) { - if (has(feature)) { - this.value -= feature; - } - } - - public long getValue() { - return value; - } -} diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPConfig.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPConfig.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPConfig.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPConfig.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPIdentityStoreRegistry.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPIdentityStoreRegistry.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPIdentityStoreRegistry.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPIdentityStoreRegistry.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPUtils.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPUtils.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/LDAPUtils.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPUtils.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/ReadonlyLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/ReadonlyLDAPUserModelDelegate.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/ReadonlyLDAPUserModelDelegate.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/ReadonlyLDAPUserModelDelegate.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/UnsyncedLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/UnsyncedLDAPUserModelDelegate.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/UnsyncedLDAPUserModelDelegate.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/UnsyncedLDAPUserModelDelegate.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/WritableLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/WritableLDAPUserModelDelegate.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/WritableLDAPUserModelDelegate.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/WritableLDAPUserModelDelegate.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPDn.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPDn.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPDn.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPDn.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPObject.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPObject.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPObject.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/model/LDAPObject.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/Condition.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/Condition.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/Condition.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/Condition.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/Sort.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/Sort.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/Sort.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/Sort.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/BetweenCondition.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/BetweenCondition.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/BetweenCondition.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/BetweenCondition.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/CustomLDAPFilter.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/CustomLDAPFilter.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/CustomLDAPFilter.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/CustomLDAPFilter.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/EqualCondition.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/EqualCondition.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/EqualCondition.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/EqualCondition.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/GreaterThanCondition.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/GreaterThanCondition.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/GreaterThanCondition.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/GreaterThanCondition.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/InCondition.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/InCondition.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/InCondition.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/InCondition.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LDAPQuery.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LDAPQuery.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LDAPQuery.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LDAPQuery.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LessThanCondition.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LessThanCondition.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LessThanCondition.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/LessThanCondition.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/NamedParameterCondition.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/NamedParameterCondition.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/NamedParameterCondition.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/NamedParameterCondition.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/OrCondition.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/OrCondition.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/query/internal/OrCondition.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/query/internal/OrCondition.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/store/IdentityStore.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/IdentityStore.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/store/IdentityStore.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/IdentityStore.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPUtil.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPUtil.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPUtil.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPUtil.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/kerberos/LDAPProviderKerberosConfig.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/kerberos/LDAPProviderKerberosConfig.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/kerberos/LDAPProviderKerberosConfig.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/kerberos/LDAPProviderKerberosConfig.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapper.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapper.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapper.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapperFactory.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapperFactory.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapperFactory.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapper.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapperFactory.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapperFactory.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/FullNameLDAPStorageMapperFactory.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedLDAPRoleStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedLDAPRoleStorageMapper.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedLDAPRoleStorageMapper.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedLDAPRoleStorageMapper.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedLDAPRoleStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedLDAPRoleStorageMapperFactory.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedLDAPRoleStorageMapperFactory.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/HardcodedLDAPRoleStorageMapperFactory.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPMappersComparator.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPMappersComparator.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPMappersComparator.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPMappersComparator.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapper.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapper.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapper.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperBridge.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperBridge.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperBridge.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperBridge.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperFactory.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperFactory.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperFactory.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperSpi.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperSpi.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperSpi.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapperSpi.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/PasswordUpdated.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/PasswordUpdated.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/PasswordUpdated.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/PasswordUpdated.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/TxAwareLDAPUserModelDelegate.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/TxAwareLDAPUserModelDelegate.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/TxAwareLDAPUserModelDelegate.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/TxAwareLDAPUserModelDelegate.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapper.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapper.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapper.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapperFactory.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapperFactory.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/UserAttributeLDAPStorageMapperFactory.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapper.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapper.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapper.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapperConfig.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapperConfig.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapperConfig.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapperConfig.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/LDAPGroupMapperMode.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/LDAPGroupMapperMode.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/LDAPGroupMapperMode.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/LDAPGroupMapperMode.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/MembershipType.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/MembershipType.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/MembershipType.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/MembershipType.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/UserRolesRetrieveStrategy.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/UserRolesRetrieveStrategy.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/UserRolesRetrieveStrategy.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/UserRolesRetrieveStrategy.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapper.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapper.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapper.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapperFactory.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapperFactory.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapperFactory.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupMapperConfig.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupMapperConfig.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupMapperConfig.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupMapperConfig.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupTreeResolver.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupTreeResolver.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupTreeResolver.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupTreeResolver.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapperFactory.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapperFactory.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapperFactory.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleMapperConfig.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleMapperConfig.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleMapperConfig.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleMapperConfig.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapperFactory.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapperFactory.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapperFactory.java diff --git a/federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/msad/UserAccountControl.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/UserAccountControl.java similarity index 100% rename from federation/ldap2/src/main/java/org/keycloak/storage/ldap/mappers/msad/UserAccountControl.java rename to federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/UserAccountControl.java diff --git a/federation/ldap/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory b/federation/ldap/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory deleted file mode 100644 index 361b2e64dc7..00000000000 --- a/federation/ldap/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory +++ /dev/null @@ -1,23 +0,0 @@ -# -# Copyright 2016 Red Hat, Inc. and/or its affiliates -# and other contributors as indicated by the @author tags. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory -org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory -org.keycloak.federation.ldap.mappers.HardcodedLDAPRoleMapperFactory -org.keycloak.federation.ldap.mappers.membership.role.RoleLDAPFederationMapperFactory -org.keycloak.federation.ldap.mappers.membership.group.GroupLDAPFederationMapperFactory -org.keycloak.federation.ldap.mappers.msad.MSADUserAccountControlMapperFactory \ No newline at end of file diff --git a/federation/ldap/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory b/federation/ldap/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory deleted file mode 100755 index fe38506cfcb..00000000000 --- a/federation/ldap/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright 2016 Red Hat, Inc. and/or its affiliates -# and other contributors as indicated by the @author tags. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -org.keycloak.federation.ldap.LDAPFederationProviderFactory \ No newline at end of file diff --git a/federation/ldap2/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/federation/ldap/src/main/resources/META-INF/services/org.keycloak.provider.Spi similarity index 100% rename from federation/ldap2/src/main/resources/META-INF/services/org.keycloak.provider.Spi rename to federation/ldap/src/main/resources/META-INF/services/org.keycloak.provider.Spi diff --git a/federation/ldap2/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory b/federation/ldap/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory similarity index 100% rename from federation/ldap2/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory rename to federation/ldap/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory diff --git a/federation/ldap2/src/main/resources/META-INF/services/org.keycloak.storage.ldap.mappers.LDAPStorageMapperFactory b/federation/ldap/src/main/resources/META-INF/services/org.keycloak.storage.ldap.mappers.LDAPStorageMapperFactory similarity index 100% rename from federation/ldap2/src/main/resources/META-INF/services/org.keycloak.storage.ldap.mappers.LDAPStorageMapperFactory rename to federation/ldap/src/main/resources/META-INF/services/org.keycloak.storage.ldap.mappers.LDAPStorageMapperFactory diff --git a/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/GroupTreeResolverTest.java b/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/GroupTreeResolverTest.java index 66cddf7f435..78974a2fc18 100644 --- a/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/GroupTreeResolverTest.java +++ b/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/GroupTreeResolverTest.java @@ -15,11 +15,11 @@ * limitations under the License. */ -package org.keycloak.federation.ldap.idm.model; +package org.keycloak.storage.ldap.idm.model; import org.junit.Assert; import org.junit.Test; -import org.keycloak.federation.ldap.mappers.membership.group.GroupTreeResolver; +import org.keycloak.storage.ldap.mappers.membership.group.GroupTreeResolver; import java.util.Arrays; import java.util.List; diff --git a/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java b/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java index 620a1665af6..566d6c9efbc 100644 --- a/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java +++ b/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.federation.ldap.idm.model; +package org.keycloak.storage.ldap.idm.model; import org.junit.Assert; import org.junit.Test; diff --git a/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPMappersComparatorTest.java b/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPMappersComparatorTest.java index 061ef50097e..41bf3ec9b09 100644 --- a/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPMappersComparatorTest.java +++ b/federation/ldap/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPMappersComparatorTest.java @@ -15,26 +15,25 @@ * limitations under the License. */ -package org.keycloak.federation.ldap.idm.model; +package org.keycloak.storage.ldap.idm.model; import org.junit.Assert; import org.junit.Test; -import org.keycloak.federation.ldap.LDAPConfig; -import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory; -import org.keycloak.federation.ldap.mappers.LDAPMappersComparator; -import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapper; -import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory; +import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.component.ComponentModel; import org.keycloak.models.LDAPConstants; -import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.storage.ldap.LDAPConfig; +import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapper; +import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapperFactory; +import org.keycloak.storage.ldap.mappers.LDAPMappersComparator; +import org.keycloak.storage.ldap.mappers.LDAPStorageMapper; +import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper; +import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapperFactory; -import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.Set; /** * @author Marek Posolda @@ -45,11 +44,11 @@ public class LDAPMappersComparatorTest { @Test public void testCompareWithCNUsername() { - Map cfg = new HashMap<>(); - cfg.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, LDAPConstants.CN); + MultivaluedHashMap cfg = new MultivaluedHashMap<>(); + cfg.add(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, LDAPConstants.CN); LDAPConfig config = new LDAPConfig(cfg); - List sorted = LDAPMappersComparator.sortAsc(config, getMappers()); + List sorted = LDAPMappersComparator.sortAsc(config, getMappers()); assertOrder(sorted, "username-cn", "sAMAccountName", "first name", "full name"); sorted = LDAPMappersComparator.sortDesc(config, getMappers()); @@ -58,57 +57,57 @@ public class LDAPMappersComparatorTest { @Test public void testCompareWithSAMAccountNameUsername() { - Map cfg = new HashMap<>(); - cfg.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, LDAPConstants.SAM_ACCOUNT_NAME); + MultivaluedHashMap cfg = new MultivaluedHashMap<>(); + cfg.add(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, LDAPConstants.SAM_ACCOUNT_NAME); LDAPConfig config = new LDAPConfig(cfg); - List sorted = LDAPMappersComparator.sortAsc(config, getMappers()); + List sorted = LDAPMappersComparator.sortAsc(config, getMappers()); assertOrder(sorted, "sAMAccountName", "username-cn", "first name", "full name"); sorted = LDAPMappersComparator.sortDesc(config, getMappers()); assertOrder(sorted, "full name", "first name", "username-cn", "sAMAccountName"); } - private void assertOrder(List result, String... names) { + private void assertOrder(List result, String... names) { Assert.assertEquals(result.size(), names.length); for (int i=0 ; i getMappers() { - Set result = new HashSet<>(); + private List getMappers() { + List result = new LinkedList<>(); - UserFederationMapperModel mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", "fed-provider", UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME, - UserAttributeLDAPFederationMapper.READ_ONLY, "true", - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "true", - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "true"); + ComponentModel mapperModel = KeycloakModelUtils.createComponentModel("first name", "fed-provider", UserAttributeLDAPStorageMapperFactory.PROVIDER_ID, LDAPStorageMapper.class.getName(), + UserAttributeLDAPStorageMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, + UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME, + UserAttributeLDAPStorageMapper.READ_ONLY, "true", + UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "true", + UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "true"); mapperModel.setId("idd1"); result.add(mapperModel); - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("username-cn", "fed-provider", UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN, - UserAttributeLDAPFederationMapper.READ_ONLY, "true", - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false", - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "true"); + mapperModel = KeycloakModelUtils.createComponentModel("username-cn", "fed-provider", UserAttributeLDAPStorageMapperFactory.PROVIDER_ID,LDAPStorageMapper.class.getName(), + UserAttributeLDAPStorageMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, + UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE, LDAPConstants.CN, + UserAttributeLDAPStorageMapper.READ_ONLY, "true", + UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false", + UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "true"); mapperModel.setId("idd2"); result.add(mapperModel); - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", "fed-provider", FullNameLDAPFederationMapperFactory.PROVIDER_ID, - FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN, - UserAttributeLDAPFederationMapper.READ_ONLY, "true"); + mapperModel = KeycloakModelUtils.createComponentModel("full name", "fed-provider", FullNameLDAPStorageMapperFactory.PROVIDER_ID,LDAPStorageMapper.class.getName(), + FullNameLDAPStorageMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN, + UserAttributeLDAPStorageMapper.READ_ONLY, "true"); mapperModel.setId("idd3"); result.add(mapperModel); - mapperModel = KeycloakModelUtils.createUserFederationMapperModel("sAMAccountName", "fed-provider", UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, - UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, - UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.SAM_ACCOUNT_NAME, - UserAttributeLDAPFederationMapper.READ_ONLY, "false", - UserAttributeLDAPFederationMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false", - UserAttributeLDAPFederationMapper.IS_MANDATORY_IN_LDAP, "true"); + mapperModel = KeycloakModelUtils.createComponentModel("sAMAccountName", "fed-provider", UserAttributeLDAPStorageMapperFactory.PROVIDER_ID,LDAPStorageMapper.class.getName(), + UserAttributeLDAPStorageMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, + UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE, LDAPConstants.SAM_ACCOUNT_NAME, + UserAttributeLDAPStorageMapper.READ_ONLY, "false", + UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false", + UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "true"); mapperModel.setId("idd4"); result.add(mapperModel); diff --git a/federation/ldap2/pom.xml b/federation/ldap2/pom.xml deleted file mode 100755 index bf3feeab028..00000000000 --- a/federation/ldap2/pom.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - keycloak-parent - org.keycloak - 2.4.0.CR1-SNAPSHOT - ../../pom.xml - - 4.0.0 - - keycloak-ldap-federation - Keycloak LDAP UserStoreProvider - - - - 1.8 - 1.8 - - - - - org.keycloak - keycloak-core - provided - - - org.keycloak - keycloak-server-spi - provided - - - org.keycloak - keycloak-kerberos-federation - provided - - - org.jboss.resteasy - resteasy-jaxrs - provided - - - log4j - log4j - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-simple - - - - - org.jboss.logging - jboss-logging - provided - - - junit - junit - test - - - org.jboss.spec.javax.transaction - jboss-transaction-api_1.2_spec - provided - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - ${maven.compiler.source} - ${maven.compiler.target} - - - - - - diff --git a/federation/ldap2/src/test/java/org/keycloak/storage/ldap/idm/model/GroupTreeResolverTest.java b/federation/ldap2/src/test/java/org/keycloak/storage/ldap/idm/model/GroupTreeResolverTest.java deleted file mode 100644 index 78974a2fc18..00000000000 --- a/federation/ldap2/src/test/java/org/keycloak/storage/ldap/idm/model/GroupTreeResolverTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.storage.ldap.idm.model; - -import org.junit.Assert; -import org.junit.Test; -import org.keycloak.storage.ldap.mappers.membership.group.GroupTreeResolver; - -import java.util.Arrays; -import java.util.List; - -/** - * @author Marek Posolda - */ -public class GroupTreeResolverTest { - - @Test - public void testGroupResolvingCorrect() throws GroupTreeResolver.GroupTreeResolveException { - GroupTreeResolver.Group group1 = new GroupTreeResolver.Group("group1", "group2", "group3"); - GroupTreeResolver.Group group2 = new GroupTreeResolver.Group("group2", "group4", "group5"); - GroupTreeResolver.Group group3 = new GroupTreeResolver.Group("group3", "group6"); - GroupTreeResolver.Group group4 = new GroupTreeResolver.Group("group4"); - GroupTreeResolver.Group group5 = new GroupTreeResolver.Group("group5"); - GroupTreeResolver.Group group6 = new GroupTreeResolver.Group("group6", "group7"); - GroupTreeResolver.Group group7 = new GroupTreeResolver.Group("group7"); - List groups = Arrays.asList(group1, group2, group3, group4, group5, group6, group7); - - GroupTreeResolver resolver = new GroupTreeResolver(); - List groupTree = resolver.resolveGroupTree(groups); - Assert.assertEquals(1, groupTree.size()); - Assert.assertEquals("{ group1 -> [ { group2 -> [ { group4 -> [ ]}{ group5 -> [ ]} ]}{ group3 -> [ { group6 -> [ { group7 -> [ ]} ]} ]} ]}", groupTree.get(0).toString()); - } - - @Test - public void testGroupResolvingCorrect2_multipleRootGroups() throws GroupTreeResolver.GroupTreeResolveException { - GroupTreeResolver.Group group1 = new GroupTreeResolver.Group("group1", "group8"); - GroupTreeResolver.Group group2 = new GroupTreeResolver.Group("group2"); - GroupTreeResolver.Group group3 = new GroupTreeResolver.Group("group3", "group2"); - GroupTreeResolver.Group group4 = new GroupTreeResolver.Group("group4", "group1", "group5"); - GroupTreeResolver.Group group5 = new GroupTreeResolver.Group("group5", "group6", "group7"); - GroupTreeResolver.Group group6 = new GroupTreeResolver.Group("group6"); - GroupTreeResolver.Group group7 = new GroupTreeResolver.Group("group7"); - GroupTreeResolver.Group group8 = new GroupTreeResolver.Group("group8", "group9"); - GroupTreeResolver.Group group9 = new GroupTreeResolver.Group("group9"); - List groups = Arrays.asList(group1, group2, group3, group4, group5, group6, group7, group8, group9); - - GroupTreeResolver resolver = new GroupTreeResolver(); - List groupTree = resolver.resolveGroupTree(groups); - - Assert.assertEquals(2, groupTree.size()); - Assert.assertEquals("{ group3 -> [ { group2 -> [ ]} ]}", groupTree.get(0).toString()); - Assert.assertEquals("{ group4 -> [ { group1 -> [ { group8 -> [ { group9 -> [ ]} ]} ]}{ group5 -> [ { group6 -> [ ]}{ group7 -> [ ]} ]} ]}", groupTree.get(1).toString()); - } - - - @Test - public void testGroupResolvingRecursion() { - GroupTreeResolver.Group group1 = new GroupTreeResolver.Group("group1", "group2", "group3"); - GroupTreeResolver.Group group2 = new GroupTreeResolver.Group("group2"); - GroupTreeResolver.Group group3 = new GroupTreeResolver.Group("group3", "group4"); - GroupTreeResolver.Group group4 = new GroupTreeResolver.Group("group4", "group5"); - GroupTreeResolver.Group group5 = new GroupTreeResolver.Group("group5", "group1"); - GroupTreeResolver.Group group6 = new GroupTreeResolver.Group("group6", "group7"); - GroupTreeResolver.Group group7 = new GroupTreeResolver.Group("group7"); - List groups = Arrays.asList(group1, group2, group3, group4, group5, group6, group7); - - GroupTreeResolver resolver = new GroupTreeResolver(); - try { - resolver.resolveGroupTree(groups); - Assert.fail("Exception expected because of recursion"); - } catch (GroupTreeResolver.GroupTreeResolveException gre) { - Assert.assertTrue(gre.getMessage().startsWith("Recursion detected")); - } - } - - @Test - public void testGroupResolvingMultipleParents() { - GroupTreeResolver.Group group1 = new GroupTreeResolver.Group("group1", "group2"); - GroupTreeResolver.Group group2 = new GroupTreeResolver.Group("group2"); - GroupTreeResolver.Group group3 = new GroupTreeResolver.Group("group3", "group2"); - GroupTreeResolver.Group group4 = new GroupTreeResolver.Group("group4", "group1", "group5"); - GroupTreeResolver.Group group5 = new GroupTreeResolver.Group("group5", "group4"); - List groups = Arrays.asList(group1, group2, group3, group4, group5); - - GroupTreeResolver resolver = new GroupTreeResolver(); - try { - resolver.resolveGroupTree(groups); - Assert.fail("Exception expected because of some groups have multiple parents"); - } catch (GroupTreeResolver.GroupTreeResolveException gre) { - Assert.assertTrue(gre.getMessage().contains("detected to have multiple parents")); - } - } - - - @Test - public void testGroupResolvingMissingGroup() { - GroupTreeResolver.Group group1 = new GroupTreeResolver.Group("group1", "group2"); - GroupTreeResolver.Group group2 = new GroupTreeResolver.Group("group2", "group3"); - GroupTreeResolver.Group group4 = new GroupTreeResolver.Group("group4"); - List groups = Arrays.asList(group1, group2, group4); - - GroupTreeResolver resolver = new GroupTreeResolver(); - try { - resolver.resolveGroupTree(groups); - Assert.fail("Exception expected because of missing referenced group"); - } catch (GroupTreeResolver.GroupTreeResolveException gre) { - Assert.assertEquals("Group 'group3' referenced as member of group 'group2' doesn't exists", gre.getMessage()); - } - } -} diff --git a/federation/ldap2/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java b/federation/ldap2/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java deleted file mode 100644 index 566d6c9efbc..00000000000 --- a/federation/ldap2/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPDnTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.storage.ldap.idm.model; - -import org.junit.Assert; -import org.junit.Test; - -/** - * @author Marek Posolda - */ -public class LDAPDnTest { - - @Test - public void testDn() throws Exception { - LDAPDn dn = LDAPDn.fromString("dc=keycloak, dc=org"); - dn.addFirst("ou", "People"); - Assert.assertEquals("ou=People,dc=keycloak,dc=org", dn.toString()); - - dn.addFirst("uid", "Johny,Depp+Pepp"); - Assert.assertEquals("uid=Johny\\,Depp\\+Pepp,ou=People,dc=keycloak,dc=org", dn.toString()); - Assert.assertEquals(LDAPDn.fromString("uid=Johny\\,Depp\\+Pepp,ou=People,dc=keycloak,dc=org"), dn); - - Assert.assertEquals("ou=People,dc=keycloak,dc=org", dn.getParentDn()); - - Assert.assertTrue(dn.isDescendantOf(LDAPDn.fromString("dc=keycloak, dc=org"))); - Assert.assertTrue(dn.isDescendantOf(LDAPDn.fromString("dc=org"))); - Assert.assertTrue(dn.isDescendantOf(LDAPDn.fromString("DC=keycloak, DC=org"))); - Assert.assertFalse(dn.isDescendantOf(LDAPDn.fromString("dc=keycloakk, dc=org"))); - Assert.assertFalse(dn.isDescendantOf(dn)); - - Assert.assertEquals("uid", dn.getFirstRdnAttrName()); - Assert.assertEquals("Johny\\,Depp\\+Pepp", dn.getFirstRdnAttrValue()); - } -} diff --git a/federation/ldap2/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPMappersComparatorTest.java b/federation/ldap2/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPMappersComparatorTest.java deleted file mode 100644 index 41bf3ec9b09..00000000000 --- a/federation/ldap2/src/test/java/org/keycloak/storage/ldap/idm/model/LDAPMappersComparatorTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed 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.keycloak.storage.ldap.idm.model; - -import org.junit.Assert; -import org.junit.Test; -import org.keycloak.common.util.MultivaluedHashMap; -import org.keycloak.component.ComponentModel; -import org.keycloak.models.LDAPConstants; -import org.keycloak.models.UserModel; -import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.storage.ldap.LDAPConfig; -import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapper; -import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapperFactory; -import org.keycloak.storage.ldap.mappers.LDAPMappersComparator; -import org.keycloak.storage.ldap.mappers.LDAPStorageMapper; -import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper; -import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapperFactory; - -import java.util.LinkedList; -import java.util.List; - -/** - * @author Marek Posolda - */ -public class LDAPMappersComparatorTest { - - - - @Test - public void testCompareWithCNUsername() { - MultivaluedHashMap cfg = new MultivaluedHashMap<>(); - cfg.add(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, LDAPConstants.CN); - LDAPConfig config = new LDAPConfig(cfg); - - List sorted = LDAPMappersComparator.sortAsc(config, getMappers()); - assertOrder(sorted, "username-cn", "sAMAccountName", "first name", "full name"); - - sorted = LDAPMappersComparator.sortDesc(config, getMappers()); - assertOrder(sorted, "full name", "first name", "sAMAccountName", "username-cn"); - } - - @Test - public void testCompareWithSAMAccountNameUsername() { - MultivaluedHashMap cfg = new MultivaluedHashMap<>(); - cfg.add(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, LDAPConstants.SAM_ACCOUNT_NAME); - LDAPConfig config = new LDAPConfig(cfg); - - List sorted = LDAPMappersComparator.sortAsc(config, getMappers()); - assertOrder(sorted, "sAMAccountName", "username-cn", "first name", "full name"); - - sorted = LDAPMappersComparator.sortDesc(config, getMappers()); - assertOrder(sorted, "full name", "first name", "username-cn", "sAMAccountName"); - } - - private void assertOrder(List result, String... names) { - Assert.assertEquals(result.size(), names.length); - for (int i=0 ; i getMappers() { - List result = new LinkedList<>(); - - ComponentModel mapperModel = KeycloakModelUtils.createComponentModel("first name", "fed-provider", UserAttributeLDAPStorageMapperFactory.PROVIDER_ID, LDAPStorageMapper.class.getName(), - UserAttributeLDAPStorageMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME, - UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE, LDAPConstants.GIVENNAME, - UserAttributeLDAPStorageMapper.READ_ONLY, "true", - UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "true", - UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "true"); - mapperModel.setId("idd1"); - result.add(mapperModel); - - mapperModel = KeycloakModelUtils.createComponentModel("username-cn", "fed-provider", UserAttributeLDAPStorageMapperFactory.PROVIDER_ID,LDAPStorageMapper.class.getName(), - UserAttributeLDAPStorageMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, - UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE, LDAPConstants.CN, - UserAttributeLDAPStorageMapper.READ_ONLY, "true", - UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false", - UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "true"); - mapperModel.setId("idd2"); - result.add(mapperModel); - - mapperModel = KeycloakModelUtils.createComponentModel("full name", "fed-provider", FullNameLDAPStorageMapperFactory.PROVIDER_ID,LDAPStorageMapper.class.getName(), - FullNameLDAPStorageMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN, - UserAttributeLDAPStorageMapper.READ_ONLY, "true"); - mapperModel.setId("idd3"); - result.add(mapperModel); - - mapperModel = KeycloakModelUtils.createComponentModel("sAMAccountName", "fed-provider", UserAttributeLDAPStorageMapperFactory.PROVIDER_ID,LDAPStorageMapper.class.getName(), - UserAttributeLDAPStorageMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME, - UserAttributeLDAPStorageMapper.LDAP_ATTRIBUTE, LDAPConstants.SAM_ACCOUNT_NAME, - UserAttributeLDAPStorageMapper.READ_ONLY, "false", - UserAttributeLDAPStorageMapper.ALWAYS_READ_VALUE_FROM_LDAP, "false", - UserAttributeLDAPStorageMapper.IS_MANDATORY_IN_LDAP, "true"); - mapperModel.setId("idd4"); - result.add(mapperModel); - - return result; - } -} diff --git a/federation/pom.xml b/federation/pom.xml index c0cd4941e5a..a08ef631ef4 100755 --- a/federation/pom.xml +++ b/federation/pom.xml @@ -34,7 +34,7 @@ kerberos - ldap2 + ldap sssd