From 9f6c20607859917be8e4b229c68014baf2ae58df Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Thu, 31 Jul 2014 17:28:48 -0400 Subject: [PATCH] ldap edit mode --- .../ldap/LDAPFederationProvider.java | 32 +- .../ldap/ReadonlyLDAPUserModelDelegate.java | 57 ++++ .../ldap/UnsyncedLDAPUserModelDelegate.java | 28 ++ .../ldap/WritableLDAPUserModelDelegate.java | 132 ++++++++ .../resources/partials/federated-ldap.html | 13 + .../models/UserFederationManager.java | 4 +- .../models/UserFederationProvider.java | 30 +- .../models/utils/UserModelDelegate.java | 310 +++++++----------- .../DummyUserFederationProvider.java | 2 +- .../FederationProvidersIntegrationTest.java | 77 ++++- 10 files changed, 484 insertions(+), 201 deletions(-) create mode 100755 federation/ldap/src/main/java/org/keycloak/federation/ldap/ReadonlyLDAPUserModelDelegate.java create mode 100755 federation/ldap/src/main/java/org/keycloak/federation/ldap/UnsyncedLDAPUserModelDelegate.java create mode 100755 federation/ldap/src/main/java/org/keycloak/federation/ldap/WritableLDAPUserModelDelegate.java rename federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUserModelDelegate.java => model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java (51%) 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 index 5ad113600d..587cc39120 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java @@ -1,6 +1,7 @@ package org.keycloak.federation.ldap; import org.jboss.logging.Logger; +import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.KeycloakSession; @@ -19,6 +20,7 @@ import org.picketlink.idm.model.basic.BasicModel; import org.picketlink.idm.model.basic.User; import org.picketlink.idm.query.IdentityQuery; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -35,10 +37,12 @@ public class LDAPFederationProvider implements UserFederationProvider { private static final Logger logger = Logger.getLogger(LDAPFederationProvider.class); public static final String LDAP_ID = "LDAP_ID"; public static final String SYNC_REGISTRATIONS = "syncRegistrations"; + public static final String EDIT_MODE = "editMode"; protected KeycloakSession session; protected UserFederationProviderModel model; protected PartitionManager partitionManager; + protected EditMode editMode; protected static final Set supportedCredentialTypes = new HashSet(); @@ -51,6 +55,9 @@ public class LDAPFederationProvider implements UserFederationProvider { this.session = session; this.model = model; this.partitionManager = partitionManager; + String editModeString = model.getConfig().get(EDIT_MODE); + if (editModeString == null) editMode = EditMode.READ_ONLY; + editMode = EditMode.valueOf(editModeString); } private ModelException convertIDMException(IdentityManagementException ie) { @@ -77,22 +84,37 @@ public class LDAPFederationProvider implements UserFederationProvider { @Override public UserModel proxy(UserModel local) { - // todo - return new LDAPUserModelDelegate(local, this); + switch (editMode) { + case READ_ONLY: + return new ReadonlyLDAPUserModelDelegate(local, this); + case WRITABLE: + return new WritableLDAPUserModelDelegate(local, this); + case UNSYNCED: + return new UnsyncedLDAPUserModelDelegate(local, this); + } + return local; } @Override - public Set getSupportedCredentialTypes() { + public Set getSupportedCredentialTypes(UserModel local) { + if (editMode == EditMode.UNSYNCED ) { + for (UserCredentialValueModel cred : local.getCredentialsDirectly()) { + if (cred.getType().equals(UserCredentialModel.PASSWORD)) { + return Collections.emptySet(); + } + } + } return supportedCredentialTypes; } @Override public boolean synchronizeRegistrations() { - return "true".equalsIgnoreCase(model.getConfig().get(SYNC_REGISTRATIONS)); + return "true".equalsIgnoreCase(model.getConfig().get(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"); IdentityManager identityManager = getIdentityManager(); @@ -112,6 +134,8 @@ public class LDAPFederationProvider implements UserFederationProvider { @Override public boolean removeUser(RealmModel realm, UserModel user) { + if (editMode == EditMode.READ_ONLY || editMode == EditMode.UNSYNCED) return false; + IdentityManager identityManager = getIdentityManager(); try { 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 new file mode 100755 index 0000000000..30dca4c002 --- /dev/null +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/ReadonlyLDAPUserModelDelegate.java @@ -0,0 +1,57 @@ +package org.keycloak.federation.ldap; + +import org.jboss.logging.Logger; +import org.keycloak.models.ModelException; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.UserModelDelegate; +import org.picketlink.idm.IdentityManagementException; +import org.picketlink.idm.IdentityManager; +import org.picketlink.idm.credential.Password; +import org.picketlink.idm.credential.TOTPCredential; +import org.picketlink.idm.model.basic.BasicModel; +import org.picketlink.idm.model.basic.User; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements UserModel { + private static final Logger logger = Logger.getLogger(ReadonlyLDAPUserModelDelegate.class); + + protected LDAPFederationProvider provider; + + public ReadonlyLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) { + super(delegate); + this.provider = provider; + } + + @Override + public void setUsername(String username) { + throw new IllegalStateException("Federated storage is not writable"); + } + + @Override + public void setLastName(String lastName) { + throw new IllegalStateException("Federated storage is not writable"); + } + + @Override + public void setFirstName(String first) { + throw new IllegalStateException("Federated storage is not writable"); + } + + @Override + public void updateCredential(UserCredentialModel cred) { + if (provider.getSupportedCredentialTypes(delegate).contains(cred.getType())) { + throw new IllegalStateException("Federated storage is not writable"); + } + delegate.updateCredential(cred); + } + + @Override + public void setEmail(String email) { + throw new IllegalStateException("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 new file mode 100755 index 0000000000..043f8d7683 --- /dev/null +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/UnsyncedLDAPUserModelDelegate.java @@ -0,0 +1,28 @@ +package org.keycloak.federation.ldap; + +import org.jboss.logging.Logger; +import org.keycloak.models.ModelException; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.UserModelDelegate; +import org.picketlink.idm.IdentityManagementException; +import org.picketlink.idm.IdentityManager; +import org.picketlink.idm.credential.Password; +import org.picketlink.idm.credential.TOTPCredential; +import org.picketlink.idm.model.basic.BasicModel; +import org.picketlink.idm.model.basic.User; + +/** + * @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 new file mode 100755 index 0000000000..a98d2b80bb --- /dev/null +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/WritableLDAPUserModelDelegate.java @@ -0,0 +1,132 @@ +package org.keycloak.federation.ldap; + +import org.jboss.logging.Logger; +import org.keycloak.models.ApplicationModel; +import org.keycloak.models.AuthenticationLinkModel; +import org.keycloak.models.ModelException; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserCredentialValueModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.UserModelDelegate; +import org.picketlink.idm.IdentityManagementException; +import org.picketlink.idm.IdentityManager; +import org.picketlink.idm.credential.Password; +import org.picketlink.idm.credential.TOTPCredential; +import org.picketlink.idm.model.basic.BasicModel; +import org.picketlink.idm.model.basic.User; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @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; + + public WritableLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) { + super(delegate); + this.provider = provider; + } + + @Override + public void setUsername(String username) { + IdentityManager identityManager = provider.getIdentityManager(); + + try { + User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername()); + if (picketlinkUser == null) { + throw new IllegalStateException("User not found in LDAP storage!"); + } + picketlinkUser.setLoginName(username); + identityManager.update(picketlinkUser); + } catch (IdentityManagementException ie) { + throw new ModelException(ie); + } + delegate.setUsername(username); + } + + @Override + public void setLastName(String lastName) { + IdentityManager identityManager = provider.getIdentityManager(); + + try { + User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername()); + if (picketlinkUser == null) { + throw new IllegalStateException("User not found in LDAP storage!"); + } + picketlinkUser.setLastName(lastName); + identityManager.update(picketlinkUser); + } catch (IdentityManagementException ie) { + throw new ModelException(ie); + } + delegate.setLastName(lastName); + } + + @Override + public void setFirstName(String first) { + IdentityManager identityManager = provider.getIdentityManager(); + + try { + User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername()); + if (picketlinkUser == null) { + throw new IllegalStateException("User not found in LDAP storage!"); + } + picketlinkUser.setFirstName(first); + identityManager.update(picketlinkUser); + } catch (IdentityManagementException ie) { + throw new ModelException(ie); + } + delegate.setFirstName(first); + } + + @Override + public void updateCredential(UserCredentialModel cred) { + if (!provider.getSupportedCredentialTypes(delegate).contains(cred.getType())) { + delegate.updateCredential(cred); + return; + } + IdentityManager identityManager = provider.getIdentityManager(); + + try { + User picketlinkUser = BasicModel.getUser(identityManager, getUsername()); + if (picketlinkUser == null) { + logger.debugf("User '%s' doesn't exists. Skip password update", getUsername()); + throw new IllegalStateException("User doesn't exist in LDAP storage"); + } + if (cred.getType().equals(UserCredentialModel.PASSWORD)) { + identityManager.updateCredential(picketlinkUser, new Password(cred.getValue().toCharArray())); + } else if (cred.getType().equals(UserCredentialModel.TOTP)) { + TOTPCredential credential = new TOTPCredential(cred.getValue()); + credential.setDevice(cred.getDevice()); + identityManager.updateCredential(picketlinkUser, credential); + } + } catch (IdentityManagementException ie) { + throw new ModelException(ie); + } + + } + + @Override + public void setEmail(String email) { + IdentityManager identityManager = provider.getIdentityManager(); + + try { + User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername()); + if (picketlinkUser == null) { + throw new IllegalStateException("User not found in LDAP storage!"); + } + picketlinkUser.setEmail(email); + identityManager.update(picketlinkUser); + } catch (IdentityManagementException ie) { + throw new ModelException(ie); + } + delegate.setEmail(email); + } + +} diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html index ffac6b7ab0..1be350b40c 100755 --- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html +++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html @@ -33,6 +33,19 @@ +
+ +
+
+ +
+
+
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java index 787ca90d44..0e887d3a53 100755 --- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java +++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java @@ -307,7 +307,7 @@ public class UserFederationManager implements UserProvider { UserFederationProvider link = getFederationLink(realm, user); if (link != null) { validateUser(realm, user); - if (link.getSupportedCredentialTypes().size() > 0) { + if (link.getSupportedCredentialTypes(user).size() > 0) { List fedCreds = new ArrayList(); List localCreds = new ArrayList(); for (UserCredentialModel cred : input) { @@ -331,7 +331,7 @@ public class UserFederationManager implements UserProvider { UserFederationProvider link = getFederationLink(realm, user); if (link != null) { validateUser(realm, user); - Set supportedCredentialTypes = link.getSupportedCredentialTypes(); + Set supportedCredentialTypes = link.getSupportedCredentialTypes(user); if (supportedCredentialTypes.size() > 0) { List fedCreds = new ArrayList(); List localCreds = new ArrayList(); diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java b/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java index c45812f1e6..c0923e4e69 100755 --- a/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java +++ b/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java @@ -19,6 +19,28 @@ public interface UserFederationProvider extends Provider { public static final String FIRST_NAME = UserModel.EMAIL; public static final String LAST_NAME = UserModel.LAST_NAME; + /** + * Optional type that can be by implementations to describe edit mode of federation storage + * + */ + enum EditMode { + /** + * federation storage is read-only + */ + READ_ONLY, + /** + * federation storage is writable + * + */ + WRITABLE, + /** + * updates to user are stored locally and not synced with federation storage. + * + */ + UNSYNCED + } + + /** * Gives the provider an option to proxy UserModels loaded from local storage. * This method is called whenever a UserModel is pulled from local storage. @@ -81,12 +103,12 @@ public interface UserFederationProvider extends Provider { boolean isValid(UserModel local); /** - * What UserCredentialModel types are supported by this provider. Keycloak will only call + * What UserCredentialModel types should be handled by this provider for this user? Keycloak will only call * validCredentials() with the credential types specified in this method. * * @return */ - Set getSupportedCredentialTypes(); + Set getSupportedCredentialTypes(UserModel user); /** * Validate credentials for this user. @@ -98,4 +120,6 @@ public interface UserFederationProvider extends Provider { */ boolean validCredentials(RealmModel realm, UserModel user, List input); boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input); - void close();} + void close(); + +} diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUserModelDelegate.java b/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java similarity index 51% rename from federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUserModelDelegate.java rename to model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java index 2a7fad51a9..cabdc568ca 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUserModelDelegate.java +++ b/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java @@ -1,19 +1,11 @@ -package org.keycloak.federation.ldap; +package org.keycloak.models.utils; -import org.jboss.logging.Logger; import org.keycloak.models.ApplicationModel; import org.keycloak.models.AuthenticationLinkModel; -import org.keycloak.models.ModelException; import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserModel; -import org.picketlink.idm.IdentityManagementException; -import org.picketlink.idm.IdentityManager; -import org.picketlink.idm.credential.Password; -import org.picketlink.idm.credential.TOTPCredential; -import org.picketlink.idm.model.basic.BasicModel; -import org.picketlink.idm.model.basic.User; import java.util.List; import java.util.Map; @@ -23,15 +15,11 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class LDAPUserModelDelegate implements UserModel { - private static final Logger logger = Logger.getLogger(LDAPUserModelDelegate.class); - +public class UserModelDelegate implements UserModel { protected UserModel delegate; - protected LDAPFederationProvider provider; - public LDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider) { + public UserModelDelegate(UserModel delegate) { this.delegate = delegate; - this.provider = provider; } @Override @@ -40,13 +28,33 @@ public class LDAPUserModelDelegate implements UserModel { } @Override - public void setAttribute(String name, String value) { - delegate.setAttribute(name, value); + public String getUsername() { + return delegate.getUsername(); } @Override - public boolean isEmailVerified() { - return delegate.isEmailVerified(); + public void setUsername(String username) { + delegate.setUsername(username); + } + + @Override + public boolean isEnabled() { + return delegate.isEnabled(); + } + + @Override + public boolean isTotp() { + return delegate.isTotp(); + } + + @Override + public void setEnabled(boolean enabled) { + delegate.setEnabled(enabled); + } + + @Override + public void setAttribute(String name, String value) { + delegate.setAttribute(name, value); } @Override @@ -54,14 +62,89 @@ public class LDAPUserModelDelegate implements UserModel { delegate.removeAttribute(name); } + @Override + public String getAttribute(String name) { + return delegate.getAttribute(name); + } + + @Override + public Map getAttributes() { + return delegate.getAttributes(); + } + + @Override + public Set getRequiredActions() { + return delegate.getRequiredActions(); + } + + @Override + public void addRequiredAction(RequiredAction action) { + delegate.addRequiredAction(action); + } + + @Override + public void removeRequiredAction(RequiredAction action) { + delegate.removeRequiredAction(action); + } + + @Override + public String getFirstName() { + return delegate.getFirstName(); + } + + @Override + public void setFirstName(String firstName) { + delegate.setFirstName(firstName); + } + @Override public String getLastName() { return delegate.getLastName(); } @Override - public void setFederationLink(String link) { - delegate.setFederationLink(link); + public void setLastName(String lastName) { + delegate.setLastName(lastName); + } + + @Override + public String getEmail() { + return delegate.getEmail(); + } + + @Override + public void setEmail(String email) { + delegate.setEmail(email); + } + + @Override + public boolean isEmailVerified() { + return delegate.isEmailVerified(); + } + + @Override + public void setEmailVerified(boolean verified) { + delegate.setEmailVerified(verified); + } + + @Override + public void setTotp(boolean totp) { + delegate.setTotp(totp); + } + + @Override + public void updateCredential(UserCredentialModel cred) { + delegate.updateCredential(cred); + } + + @Override + public List getCredentialsDirectly() { + return delegate.getCredentialsDirectly(); + } + + @Override + public void updateCredentialDirectly(UserCredentialValueModel cred) { + delegate.updateCredentialDirectly(cred); } @Override @@ -70,8 +153,18 @@ public class LDAPUserModelDelegate implements UserModel { } @Override - public Map getAttributes() { - return delegate.getAttributes(); + public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) { + delegate.setAuthenticationLink(authenticationLink); + } + + @Override + public Set getRealmRoleMappings() { + return delegate.getRealmRoleMappings(); + } + + @Override + public Set getApplicationRoleMappings(ApplicationModel app) { + return delegate.getApplicationRoleMappings(app); } @Override @@ -85,13 +178,8 @@ public class LDAPUserModelDelegate implements UserModel { } @Override - public void setEnabled(boolean enabled) { - delegate.setEnabled(enabled); - } - - @Override - public void removeRequiredAction(UserModel.RequiredAction action) { - delegate.removeRequiredAction(action); + public Set getRoleMappings() { + return delegate.getRoleMappings(); } @Override @@ -99,171 +187,13 @@ public class LDAPUserModelDelegate implements UserModel { delegate.deleteRoleMapping(role); } - @Override - public void setUsername(String username) { - IdentityManager identityManager = provider.getIdentityManager(); - - try { - User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername()); - if (picketlinkUser == null) { - throw new IllegalStateException("User not found in LDAP storage!"); - } - picketlinkUser.setLoginName(username); - identityManager.update(picketlinkUser); - } catch (IdentityManagementException ie) { - throw new ModelException(ie); - } - delegate.setUsername(username); - } - - @Override - public boolean isEnabled() { - return delegate.isEnabled(); - } - - @Override - public String getFirstName() { - return delegate.getFirstName(); - } - - @Override - public void setLastName(String lastName) { - IdentityManager identityManager = provider.getIdentityManager(); - - try { - User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername()); - if (picketlinkUser == null) { - throw new IllegalStateException("User not found in LDAP storage!"); - } - picketlinkUser.setLastName(lastName); - identityManager.update(picketlinkUser); - } catch (IdentityManagementException ie) { - throw new ModelException(ie); - } - delegate.setLastName(lastName); - } - - @Override - public void setEmailVerified(boolean verified) { - delegate.setEmailVerified(verified); - } - - @Override - public void updateCredential(UserCredentialModel cred) { - if (!provider.getSupportedCredentialTypes().contains(cred.getType())) { - delegate.updateCredential(cred); - return; - } - IdentityManager identityManager = provider.getIdentityManager(); - - try { - User picketlinkUser = BasicModel.getUser(identityManager, getUsername()); - if (picketlinkUser == null) { - logger.debugf("User '%s' doesn't exists. Skip password update", getUsername()); - throw new IllegalStateException("User doesn't exist in LDAP storage"); - } - if (cred.getType().equals(UserCredentialModel.PASSWORD)) { - identityManager.updateCredential(picketlinkUser, new Password(cred.getValue().toCharArray())); - } else if (cred.getType().equals(UserCredentialModel.TOTP)) { - TOTPCredential credential = new TOTPCredential(cred.getValue()); - credential.setDevice(cred.getDevice()); - identityManager.updateCredential(picketlinkUser, credential); - } - } catch (IdentityManagementException ie) { - throw new ModelException(ie); - } - - } - - @Override - public void setEmail(String email) { - IdentityManager identityManager = provider.getIdentityManager(); - - try { - User picketlinkUser = BasicModel.getUser(identityManager, delegate.getUsername()); - if (picketlinkUser == null) { - throw new IllegalStateException("User not found in LDAP storage!"); - } - picketlinkUser.setEmail(email); - identityManager.update(picketlinkUser); - } catch (IdentityManagementException ie) { - throw new ModelException(ie); - } - delegate.setEmail(email); - } - - @Override - public void addRequiredAction(UserModel.RequiredAction action) { - delegate.addRequiredAction(action); - } - - @Override - public List getCredentialsDirectly() { - return delegate.getCredentialsDirectly(); - } - - @Override - public boolean isTotp() { - return delegate.isTotp(); - } - - @Override - public void setFirstName(String firstName) { - delegate.setFirstName(firstName); - } - - @Override - public Set getRequiredActions() { - return delegate.getRequiredActions(); - } - - @Override - public String getEmail() { - return delegate.getEmail(); - } - - @Override - public void setTotp(boolean totp) { - delegate.setTotp(totp); - } - - @Override - public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) { - delegate.setAuthenticationLink(authenticationLink); - } - - @Override - public String getUsername() { - return delegate.getUsername(); - } - @Override public String getFederationLink() { return delegate.getFederationLink(); } @Override - public Set getRealmRoleMappings() { - return delegate.getRealmRoleMappings(); - } - - @Override - public Set getRoleMappings() { - return delegate.getRoleMappings(); - } - - @Override - public Set getApplicationRoleMappings(ApplicationModel app) { - return delegate.getApplicationRoleMappings(app); - } - - @Override - public String getAttribute(String name) { - return delegate.getAttribute(name); - } - - @Override - public void updateCredentialDirectly(UserCredentialValueModel cred) { - delegate.updateCredentialDirectly(cred); + public void setFederationLink(String link) { + delegate.setFederationLink(link); } } diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java index 7ebf592e7e..2559b39792 100755 --- a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java @@ -67,7 +67,7 @@ public class DummyUserFederationProvider implements UserFederationProvider { } @Override - public Set getSupportedCredentialTypes() { + public Set getSupportedCredentialTypes(UserModel user) { return Collections.emptySet(); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java index 81304c8ced..73959d0224 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java @@ -12,6 +12,8 @@ import org.junit.runners.MethodSorters; import org.keycloak.OAuth2Constants; import org.keycloak.federation.ldap.LDAPFederationProvider; import org.keycloak.federation.ldap.LDAPFederationProviderFactory; +import org.keycloak.models.UserCredentialValueModel; +import org.keycloak.models.UserFederationProvider; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.testutils.LDAPEmbeddedServer; import org.keycloak.testsuite.LDAPTestUtils; @@ -66,10 +68,11 @@ public class FederationProvidersIntegrationTest { String vendor = ldapServer.getVendor(); ldapConfig.put(LDAPConstants.VENDOR, vendor); ldapConfig.put(LDAPFederationProvider.SYNC_REGISTRATIONS, "true"); + ldapConfig.put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.WRITABLE.toString()); - ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, null); + ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap"); // Configure LDAP ldapRule.getEmbeddedServer().setupLdapInRealm(appRealm); @@ -201,4 +204,76 @@ public class FederationProvidersIntegrationTest { Assert.assertEquals(user.getFederationLink(), ldapModel.getId()); keycloakRule.stopSession(session, false); } + + @Test + public void testReadonly() { + KeycloakSession session = keycloakRule.startSession(); + RealmModel appRealm = session.realms().getRealmByName("test"); + + UserFederationProviderModel model = new UserFederationProviderModel(ldapModel.getId(), ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(), ldapModel.getDisplayName()); + model.getConfig().put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.READ_ONLY.toString()); + appRealm.updateUserFederationProvider(model); + UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm); + Assert.assertNotNull(user); + Assert.assertNotNull(user.getFederationLink()); + Assert.assertEquals(user.getFederationLink(), ldapModel.getId()); + try { + user.setEmail("error@error.com"); + Assert.fail("should fail"); + } catch (Exception e) { + + } + try { + user.setLastName("Berk"); + Assert.fail("should fail"); + } catch (Exception e) { + + } + try { + user.setFirstName("Bilbo"); + Assert.fail("should fail"); + } catch (Exception e) { + + } + try { + UserCredentialModel cred = UserCredentialModel.password("poop"); + user.updateCredential(cred); + Assert.fail("should fail"); + } catch (Exception e) { + + } + session.getTransaction().rollback(); + session.close(); + session = keycloakRule.startSession(); + appRealm = session.realms().getRealmByName("test"); + Assert.assertEquals(UserFederationProvider.EditMode.WRITABLE.toString(), appRealm.getUserFederationProviders().get(0).getConfig().get(LDAPFederationProvider.EDIT_MODE)); + keycloakRule.stopSession(session, false); + } + + @Test + public void testUnsynced() { + KeycloakSession session = keycloakRule.startSession(); + RealmModel appRealm = session.realms().getRealmByName("test"); + + UserFederationProviderModel model = new UserFederationProviderModel(ldapModel.getId(), ldapModel.getProviderName(), ldapModel.getConfig(), ldapModel.getPriority(), ldapModel.getDisplayName()); + model.getConfig().put(LDAPFederationProvider.EDIT_MODE, UserFederationProvider.EditMode.UNSYNCED.toString()); + appRealm.updateUserFederationProvider(model); + UserModel user = session.users().getUserByUsername("johnkeycloak", appRealm); + Assert.assertNotNull(user); + Assert.assertNotNull(user.getFederationLink()); + Assert.assertEquals(user.getFederationLink(), ldapModel.getId()); + + UserCredentialModel cred = UserCredentialModel.password("candy"); + user.updateCredential(cred); + UserCredentialValueModel userCredentialValueModel = user.getCredentialsDirectly().get(0); + Assert.assertEquals(UserCredentialModel.PASSWORD, userCredentialValueModel.getType()); + Assert.assertTrue(session.users().validCredentials(appRealm, user, cred)); + session.getTransaction().rollback(); + session.close(); + session = keycloakRule.startSession(); + appRealm = session.realms().getRealmByName("test"); + Assert.assertEquals(UserFederationProvider.EditMode.WRITABLE.toString(), appRealm.getUserFederationProviders().get(0).getConfig().get(LDAPFederationProvider.EDIT_MODE)); + keycloakRule.stopSession(session, false); + } + }