From 23aa490c721221618876b0632ec885a120fe87d4 Mon Sep 17 00:00:00 2001 From: mposolda Date: Mon, 14 Mar 2016 13:35:30 +0100 Subject: [PATCH] KEYCLOAK-2634 Better error reporting if password update failed due to MSAD password policy --- .../idm/store/ldap/LDAPIdentityStore.java | 4 +++ .../msad/MSADUserAccountControlMapper.java | 29 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) 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 index b8adbdd4ef..9a9b929839 100644 --- 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 @@ -220,6 +220,8 @@ public class LDAPIdentityStore implements IdentityStore { 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); } @@ -240,6 +242,8 @@ public class LDAPIdentityStore implements IdentityStore { 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); } 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 index 88f12fbd1e..033b3f40f8 100644 --- 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 @@ -30,6 +30,7 @@ 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.models.LDAPConstants; +import org.keycloak.models.ModelException; import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserFederationMapperModel; @@ -48,6 +49,7 @@ public class MSADUserAccountControlMapper extends AbstractLDAPFederationMapper { 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); @@ -94,7 +96,7 @@ public class MSADUserAccountControlMapper extends AbstractLDAPFederationMapper { } protected boolean processAuthErrorCode(String errorCode, UserModel user) { - logger.debugf("MSAD Error code is '%s' after failed LDAP login of user", errorCode, user.getUsername()); + 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")) { @@ -105,6 +107,8 @@ public class MSADUserAccountControlMapper extends AbstractLDAPFederationMapper { // 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()); } } @@ -112,6 +116,22 @@ public class MSADUserAccountControlMapper extends AbstractLDAPFederationMapper { } + protected ModelException processFailedPasswordUpdateException(ModelException 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; + } + + public class MSADUserModelDelegate extends UserModelDelegate { private final LDAPObject ldapUser; @@ -156,7 +176,12 @@ public class MSADUserAccountControlMapper extends AbstractLDAPFederationMapper { @Override public void updateCredential(UserCredentialModel cred) { // Update LDAP password first - super.updateCredential(cred); + try { + super.updateCredential(cred); + } catch (ModelException me) { + me = processFailedPasswordUpdateException(me); + throw me; + } if (ldapProvider.getEditMode() == UserFederationProvider.EditMode.WRITABLE && cred.getType().equals(UserCredentialModel.PASSWORD)) { logger.debugf("Going to update userAccountControl for ldap user '%s' after successful password update", ldapUser.getDn().toString());