Handle exceptions and UI errors when user is read only. Some sync issues
This commit is contained in:
parent
75bc633a33
commit
fd9d2ba4d5
8 changed files with 70 additions and 27 deletions
|
@ -311,8 +311,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
|
||||||
currentUser.setLastName(picketlinkUser.getLastName());
|
currentUser.setLastName(picketlinkUser.getLastName());
|
||||||
logger.infof("Updated user from LDAP: " + currentUser.getUsername());
|
logger.infof("Updated user from LDAP: " + currentUser.getUsername());
|
||||||
} else {
|
} else {
|
||||||
// TODO: We have local user of same username like LDAP user, but not linked. What to do? Delete him and import again?
|
logger.warnf("User '%s' is not updated during sync as he is not linked to federation provider '%s'", username, fedModel.getDisplayName());
|
||||||
throw new IllegalStateException("User " + username + " has invalid LDAP ID or doesn't have federation link");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.keycloak.federation.ldap;
|
package org.keycloak.federation.ldap;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.keycloak.models.ModelReadOnlyException;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.UserModelDelegate;
|
import org.keycloak.models.utils.UserModelDelegate;
|
||||||
|
@ -10,7 +10,6 @@ import org.keycloak.models.utils.UserModelDelegate;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements UserModel {
|
public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements UserModel {
|
||||||
private static final Logger logger = Logger.getLogger(ReadonlyLDAPUserModelDelegate.class);
|
|
||||||
|
|
||||||
protected LDAPFederationProvider provider;
|
protected LDAPFederationProvider provider;
|
||||||
|
|
||||||
|
@ -21,30 +20,30 @@ public class ReadonlyLDAPUserModelDelegate extends UserModelDelegate implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
throw new IllegalStateException("Federated storage is not writable");
|
throw new ModelReadOnlyException("Federated storage is not writable");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLastName(String lastName) {
|
public void setLastName(String lastName) {
|
||||||
throw new IllegalStateException("Federated storage is not writable");
|
throw new ModelReadOnlyException("Federated storage is not writable");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFirstName(String first) {
|
public void setFirstName(String first) {
|
||||||
throw new IllegalStateException("Federated storage is not writable");
|
throw new ModelReadOnlyException("Federated storage is not writable");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateCredential(UserCredentialModel cred) {
|
public void updateCredential(UserCredentialModel cred) {
|
||||||
if (provider.getSupportedCredentialTypes(delegate).contains(cred.getType())) {
|
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);
|
delegate.updateCredential(cred);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setEmail(String email) {
|
public void setEmail(String email) {
|
||||||
throw new IllegalStateException("Federated storage is not writable");
|
throw new ModelReadOnlyException("Federated storage is not writable");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ missingTotp=Please specify authenticator code
|
||||||
invalidPasswordExisting=Invalid existing password
|
invalidPasswordExisting=Invalid existing password
|
||||||
invalidPasswordConfirm=Password confirmation doesn't match
|
invalidPasswordConfirm=Password confirmation doesn't match
|
||||||
invalidTotp=Invalid authenticator code
|
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.
|
successTotp=Google authenticator configured.
|
||||||
successTotpRemoved=Google authenticator removed.
|
successTotpRemoved=Google authenticator removed.
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,10 @@ public class Messages {
|
||||||
|
|
||||||
public static final String INVALID_USER = "invalidUser";
|
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_EMAIL = "missingEmail";
|
||||||
|
|
||||||
public static final String MISSING_FIRST_NAME = "missingFirstName";
|
public static final String MISSING_FIRST_NAME = "missingFirstName";
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.ModelReadOnlyException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.SocialLinkModel;
|
import org.keycloak.models.SocialLinkModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
@ -342,23 +343,28 @@ public class AccountService {
|
||||||
return account.setError(error).createResponse(AccountPages.ACCOUNT);
|
return account.setError(error).createResponse(AccountPages.ACCOUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.setFirstName(formData.getFirst("firstName"));
|
try {
|
||||||
user.setLastName(formData.getFirst("lastName"));
|
user.setFirstName(formData.getFirst("firstName"));
|
||||||
|
user.setLastName(formData.getFirst("lastName"));
|
||||||
|
|
||||||
String email = formData.getFirst("email");
|
String email = formData.getFirst("email");
|
||||||
String oldEmail = user.getEmail();
|
String oldEmail = user.getEmail();
|
||||||
boolean emailChanged = oldEmail != null ? !oldEmail.equals(email) : email != null;
|
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) {
|
if (emailChanged) {
|
||||||
user.setEmailVerified(false);
|
user.setEmailVerified(false);
|
||||||
audit.clone().event(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, oldEmail).detail(Details.UPDATED_EMAIL, email).success();
|
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")
|
@Path("totp-remove")
|
||||||
|
@ -510,7 +516,10 @@ public class AccountService {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
session.users().updateCredential(realm, user, UserCredentialModel.password(passwordNew));
|
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);
|
logger.error("Failed to update password", ape);
|
||||||
setReferrerOnPage();
|
setReferrerOnPage();
|
||||||
return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD);
|
return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD);
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
|
import org.keycloak.models.ModelReadOnlyException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.SocialLinkModel;
|
import org.keycloak.models.SocialLinkModel;
|
||||||
|
@ -118,6 +119,8 @@ public class UsersResource {
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
return Flows.errors().exists("User exists with same username or email");
|
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);
|
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);
|
if (pass.isTemporary()) user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.keycloak.federation.ldap.LDAPFederationProvider;
|
||||||
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
|
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
|
||||||
import org.keycloak.federation.ldap.LDAPUtils;
|
import org.keycloak.federation.ldap.LDAPUtils;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.ModelReadOnlyException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
@ -252,26 +253,26 @@ public class FederationProvidersIntegrationTest {
|
||||||
try {
|
try {
|
||||||
user.setEmail("error@error.com");
|
user.setEmail("error@error.com");
|
||||||
Assert.fail("should fail");
|
Assert.fail("should fail");
|
||||||
} catch (Exception e) {
|
} catch (ModelReadOnlyException e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
user.setLastName("Berk");
|
user.setLastName("Berk");
|
||||||
Assert.fail("should fail");
|
Assert.fail("should fail");
|
||||||
} catch (Exception e) {
|
} catch (ModelReadOnlyException e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
user.setFirstName("Bilbo");
|
user.setFirstName("Bilbo");
|
||||||
Assert.fail("should fail");
|
Assert.fail("should fail");
|
||||||
} catch (Exception e) {
|
} catch (ModelReadOnlyException e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
UserCredentialModel cred = UserCredentialModel.password("poop");
|
UserCredentialModel cred = UserCredentialModel.password("poop");
|
||||||
user.updateCredential(cred);
|
user.updateCredential(cred);
|
||||||
Assert.fail("should fail");
|
Assert.fail("should fail");
|
||||||
} catch (Exception e) {
|
} catch (ModelReadOnlyException e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
Loading…
Reference in a new issue