keycloak-uncached

Details

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 ad3b235..cb948ba 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
@@ -311,8 +311,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
                     currentUser.setLastName(picketlinkUser.getLastName());
                     logger.infof("Updated user from LDAP: " + currentUser.getUsername());
                 } else {
-                    // TODO: We have local user of same username like LDAP user, but not linked. What to do? Delete him and import again?
-                    throw new IllegalStateException("User " + username + " has invalid LDAP ID or doesn't have federation link");
+                    logger.warnf("User '%s' is not updated during sync as he is not linked to federation provider '%s'", username, fedModel.getDisplayName());
                 }
             }
         }
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
index 8177e63..3151407 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/ReadonlyLDAPUserModelDelegate.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/ReadonlyLDAPUserModelDelegate.java
@@ -1,6 +1,6 @@
 package org.keycloak.federation.ldap;
 
-import org.jboss.logging.Logger;
+import org.keycloak.models.ModelReadOnlyException;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.UserModelDelegate;
@@ -10,7 +10,6 @@ import org.keycloak.models.utils.UserModelDelegate;
  * @version $Revision: 1 $
  */
 public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements UserModel {
-    private static final Logger logger = Logger.getLogger(ReadonlyLDAPUserModelDelegate.class);
 
     protected LDAPFederationProvider provider;
 
@@ -21,30 +20,30 @@ public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements 
 
     @Override
     public void setUsername(String username) {
-        throw new IllegalStateException("Federated storage is not writable");
+        throw new ModelReadOnlyException("Federated storage is not writable");
     }
 
     @Override
     public void setLastName(String lastName) {
-        throw new IllegalStateException("Federated storage is not writable");
+        throw new ModelReadOnlyException("Federated storage is not writable");
     }
 
     @Override
     public void setFirstName(String first) {
-        throw new IllegalStateException("Federated storage is not writable");
+        throw new ModelReadOnlyException("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");
+            throw new ModelReadOnlyException("Federated storage is not writable");
         }
         delegate.updateCredential(cred);
     }
 
     @Override
     public void setEmail(String email) {
-        throw new IllegalStateException("Federated storage is not writable");
+        throw new ModelReadOnlyException("Federated storage is not writable");
     }
 
 }
diff --git a/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties b/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties
index 152f61c..75d2f49 100755
--- a/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties
+++ b/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties
@@ -19,6 +19,8 @@ missingTotp=Please specify authenticator code
 invalidPasswordExisting=Invalid existing password
 invalidPasswordConfirm=Password confirmation doesn't match
 invalidTotp=Invalid authenticator code
+readOnlyUser=You can't update your account as it is read only
+readOnlyPassword=You can't update your password as your account is read only
 
 successTotp=Google authenticator configured.
 successTotpRemoved=Google authenticator removed.
diff --git a/model/api/src/main/java/org/keycloak/models/ModelReadOnlyException.java b/model/api/src/main/java/org/keycloak/models/ModelReadOnlyException.java
new file mode 100644
index 0000000..36e13de
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/ModelReadOnlyException.java
@@ -0,0 +1,22 @@
+package org.keycloak.models;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ModelReadOnlyException extends ModelException {
+
+    public ModelReadOnlyException() {
+    }
+
+    public ModelReadOnlyException(String message) {
+        super(message);
+    }
+
+    public ModelReadOnlyException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ModelReadOnlyException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/messages/Messages.java b/services/src/main/java/org/keycloak/services/messages/Messages.java
index 01c6f80..740d971 100755
--- a/services/src/main/java/org/keycloak/services/messages/Messages.java
+++ b/services/src/main/java/org/keycloak/services/messages/Messages.java
@@ -37,6 +37,10 @@ public class Messages {
 
     public static final String INVALID_USER = "invalidUser";
 
+    public static final String READ_ONLY_USER = "readOnlyUser";
+
+    public static final String READ_ONLY_PASSWORD = "readOnlyPassword";
+
     public static final String MISSING_EMAIL = "missingEmail";
 
     public static final String MISSING_FIRST_NAME = "missingFirstName";
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 3c4a1fa..01c7765 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -39,6 +39,7 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelReadOnlyException;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.SocialLinkModel;
 import org.keycloak.models.UserCredentialModel;
@@ -342,23 +343,28 @@ public class AccountService {
             return account.setError(error).createResponse(AccountPages.ACCOUNT);
         }
 
-        user.setFirstName(formData.getFirst("firstName"));
-        user.setLastName(formData.getFirst("lastName"));
+        try {
+            user.setFirstName(formData.getFirst("firstName"));
+            user.setLastName(formData.getFirst("lastName"));
 
-        String email = formData.getFirst("email");
-        String oldEmail = user.getEmail();
-        boolean emailChanged = oldEmail != null ? !oldEmail.equals(email) : email != null;
+            String email = formData.getFirst("email");
+            String oldEmail = user.getEmail();
+            boolean emailChanged = oldEmail != null ? !oldEmail.equals(email) : email != null;
 
-        user.setEmail(formData.getFirst("email"));
+            user.setEmail(formData.getFirst("email"));
 
-        audit.event(EventType.UPDATE_PROFILE).client(auth.getClient()).user(auth.getUser()).success();
+            audit.event(EventType.UPDATE_PROFILE).client(auth.getClient()).user(auth.getUser()).success();
 
-        if (emailChanged) {
-            user.setEmailVerified(false);
-            audit.clone().event(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
+            if (emailChanged) {
+                user.setEmailVerified(false);
+                audit.clone().event(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
+            }
+            setReferrerOnPage();
+            return account.setSuccess("accountUpdated").createResponse(AccountPages.ACCOUNT);
+        } catch (ModelReadOnlyException roe) {
+            setReferrerOnPage();
+            return account.setError(Messages.READ_ONLY_USER).createResponse(AccountPages.ACCOUNT);
         }
-        setReferrerOnPage();
-        return account.setSuccess("accountUpdated").createResponse(AccountPages.ACCOUNT);
     }
 
     @Path("totp-remove")
@@ -510,7 +516,10 @@ public class AccountService {
 
         try {
             session.users().updateCredential(realm, user, UserCredentialModel.password(passwordNew));
-         } catch (Exception ape) {
+        } catch (ModelReadOnlyException mre) {
+            setReferrerOnPage();
+            return account.setError(Messages.READ_ONLY_PASSWORD).createResponse(AccountPages.PASSWORD);
+        } catch (Exception ape) {
             logger.error("Failed to update password", ape);
             setReferrerOnPage();
             return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index dd3f1a8..52e1e65 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -13,6 +13,7 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelReadOnlyException;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.SocialLinkModel;
@@ -118,6 +119,8 @@ public class UsersResource {
             return Response.noContent().build();
         } catch (ModelDuplicateException e) {
             return Flows.errors().exists("User exists with same username or email");
+        } catch (ModelReadOnlyException re) {
+            return Flows.errors().exists("User is read only!");
         }
     }
 
@@ -779,7 +782,11 @@ public class UsersResource {
         }
 
         UserCredentialModel cred = RepresentationToModel.convertCredential(pass);
-        session.users().updateCredential(realm, user, cred);
+        try {
+            session.users().updateCredential(realm, user, cred);
+        } catch (ModelReadOnlyException mre) {
+            throw new BadRequestException("Can't reset password as account is read only");
+        }
         if (pass.isTemporary()) user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
     }
 
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 9b48590..09a07e6 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
@@ -14,6 +14,7 @@ import org.keycloak.federation.ldap.LDAPFederationProvider;
 import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
 import org.keycloak.federation.ldap.LDAPUtils;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelReadOnlyException;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserCredentialValueModel;
@@ -252,26 +253,26 @@ public class FederationProvidersIntegrationTest {
             try {
                 user.setEmail("error@error.com");
                 Assert.fail("should fail");
-            } catch (Exception e) {
+            } catch (ModelReadOnlyException e) {
 
             }
             try {
                 user.setLastName("Berk");
                 Assert.fail("should fail");
-            } catch (Exception e) {
+            } catch (ModelReadOnlyException e) {
 
             }
             try {
                 user.setFirstName("Bilbo");
                 Assert.fail("should fail");
-            } catch (Exception e) {
+            } catch (ModelReadOnlyException e) {
 
             }
             try {
                 UserCredentialModel cred = UserCredentialModel.password("poop");
                 user.updateCredential(cred);
                 Assert.fail("should fail");
-            } catch (Exception e) {
+            } catch (ModelReadOnlyException e) {
 
             }
         } finally {