KEYCLOAK-4889
Improve error messages for password policies
This commit is contained in:
parent
804766827c
commit
097a2267f5
15 changed files with 56 additions and 20 deletions
|
@ -91,7 +91,7 @@ public class HashAlgorithmPasswordPolicyProviderFactory implements PasswordPolic
|
||||||
String providerId = value != null && value.length() > 0 ? value : PasswordPolicy.HASH_ALGORITHM_DEFAULT;
|
String providerId = value != null && value.length() > 0 ? value : PasswordPolicy.HASH_ALGORITHM_DEFAULT;
|
||||||
PasswordHashProvider provider = session.getProvider(PasswordHashProvider.class, providerId);
|
PasswordHashProvider provider = session.getProvider(PasswordHashProvider.class, providerId);
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
throw new ModelException("Password hashing provider not found");
|
throw new PasswordPolicyConfigException("Password hashing provider not found");
|
||||||
}
|
}
|
||||||
return providerId;
|
return providerId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class HistoryPasswordPolicyProvider implements PasswordPolicyProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object parseConfig(String value) {
|
public Object parseConfig(String value) {
|
||||||
return value != null ? Integer.parseInt(value) : HistoryPasswordPolicyProviderFactory.DEFAULT_VALUE;
|
return parseInteger(value, HistoryPasswordPolicyProviderFactory.DEFAULT_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class LengthPasswordPolicyProvider implements PasswordPolicyProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object parseConfig(String value) {
|
public Object parseConfig(String value) {
|
||||||
return value != null ? Integer.parseInt(value) : 8;
|
return parseInteger(value, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class LowerCasePasswordPolicyProvider implements PasswordPolicyProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object parseConfig(String value) {
|
public Object parseConfig(String value) {
|
||||||
return value != null ? Integer.parseInt(value) : 1;
|
return parseInteger(value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.regex.PatternSyntaxException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -55,9 +56,13 @@ public class RegexPatternsPasswordPolicyProvider implements PasswordPolicyProvid
|
||||||
@Override
|
@Override
|
||||||
public Object parseConfig(String value) {
|
public Object parseConfig(String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new IllegalArgumentException("Config required");
|
throw new PasswordPolicyConfigException("Config required");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Pattern.compile(value);
|
||||||
|
} catch (PatternSyntaxException e) {
|
||||||
|
throw new PasswordPolicyConfigException("Not a valid regular expression");
|
||||||
}
|
}
|
||||||
return Pattern.compile(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class SpecialCharsPasswordPolicyProvider implements PasswordPolicyProvide
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object parseConfig(String value) {
|
public Object parseConfig(String value) {
|
||||||
return value != null ? Integer.parseInt(value) : 1;
|
return parseInteger(value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class UpperCasePasswordPolicyProvider implements PasswordPolicyProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object parseConfig(String value) {
|
public Object parseConfig(String value) {
|
||||||
return value != null ? Integer.parseInt(value) : 1;
|
return parseInteger(value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.models;
|
package org.keycloak.models;
|
||||||
|
|
||||||
|
import org.keycloak.policy.PasswordPolicyConfigException;
|
||||||
import org.keycloak.policy.PasswordPolicyProvider;
|
import org.keycloak.policy.PasswordPolicyProvider;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -68,10 +69,17 @@ public class PasswordPolicy implements Serializable {
|
||||||
|
|
||||||
PasswordPolicyProvider provider = session.getProvider(PasswordPolicyProvider.class, key);
|
PasswordPolicyProvider provider = session.getProvider(PasswordPolicyProvider.class, key);
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
throw new IllegalArgumentException("Unsupported policy");
|
throw new PasswordPolicyConfigException("Password policy not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
policyConfig.put(key, provider.parseConfig(config));
|
Object o;
|
||||||
|
try {
|
||||||
|
o = provider.parseConfig(config);
|
||||||
|
} catch (PasswordPolicyConfigException e) {
|
||||||
|
throw new ModelException("Invalid config for " + key + ": " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
policyConfig.put(key, o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.keycloak.policy;
|
||||||
|
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by st on 23/05/17.
|
||||||
|
*/
|
||||||
|
public class PasswordPolicyConfigException extends ModelException {
|
||||||
|
|
||||||
|
public PasswordPolicyConfigException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -33,4 +33,12 @@ public interface PasswordPolicyProvider extends Provider {
|
||||||
PolicyError validate(String user, String password);
|
PolicyError validate(String user, String password);
|
||||||
Object parseConfig(String value);
|
Object parseConfig(String value);
|
||||||
|
|
||||||
|
default Integer parseInteger(String value, Integer defaultValue) {
|
||||||
|
try {
|
||||||
|
return value != null ? Integer.parseInt(value) : defaultValue;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new PasswordPolicyConfigException("Not a valid number");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,8 +325,6 @@ public class RealmAdminResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (PatternSyntaxException e) {
|
|
||||||
return ErrorResponse.error("Specified regex pattern(s) is invalid.", Response.Status.BAD_REQUEST);
|
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
return ErrorResponse.exists("Realm with same name exists");
|
return ErrorResponse.exists("Realm with same name exists");
|
||||||
} catch (ModelException e) {
|
} catch (ModelException e) {
|
||||||
|
|
|
@ -97,7 +97,7 @@ public class PasswordHashingTest extends AbstractTestRealmKeycloakTest {
|
||||||
fail("Expected error");
|
fail("Expected error");
|
||||||
} catch (BadRequestException e) {
|
} catch (BadRequestException e) {
|
||||||
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||||
assertEquals("Password hashing provider not found", error.getErrorMessage());
|
assertEquals("Invalid config for hashAlgorithm: Password hashing provider not found", error.getErrorMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,14 @@ package org.keycloak.testsuite.model;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
import org.keycloak.models.PasswordPolicy;
|
import org.keycloak.models.PasswordPolicy;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.policy.PasswordPolicyManagerProvider;
|
import org.keycloak.policy.PasswordPolicyManagerProvider;
|
||||||
|
|
||||||
import java.util.regex.PatternSyntaxException;
|
import java.util.regex.PatternSyntaxException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,7 +125,8 @@ public class PasswordPolicyTest extends AbstractModelTest {
|
||||||
try {
|
try {
|
||||||
realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "noSuchPolicy"));
|
realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "noSuchPolicy"));
|
||||||
Assert.fail("Expected exception");
|
Assert.fail("Expected exception");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (ModelException e) {
|
||||||
|
assertEquals("Password policy not found", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,22 +136,22 @@ public class PasswordPolicyTest extends AbstractModelTest {
|
||||||
try {
|
try {
|
||||||
realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern"));
|
realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern"));
|
||||||
fail("Expected NullPointerException: Regex Pattern cannot be null.");
|
fail("Expected NullPointerException: Regex Pattern cannot be null.");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (ModelException e) {
|
||||||
// Expected NPE as regex pattern is null.
|
assertEquals("Invalid config for regexPattern: Config required", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(*)"));
|
realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(*)"));
|
||||||
fail("Expected PatternSyntaxException: Regex Pattern cannot be null.");
|
fail("Expected PatternSyntaxException: Regex Pattern cannot be null.");
|
||||||
} catch (PatternSyntaxException e) {
|
} catch (ModelException e) {
|
||||||
// Expected PSE as regex pattern(or any of its token) is not quantifiable.
|
assertEquals("Invalid config for regexPattern: Not a valid regular expression", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(*,**)"));
|
realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(*,**)"));
|
||||||
fail("Expected PatternSyntaxException: Regex Pattern cannot be null.");
|
fail("Expected PatternSyntaxException: Regex Pattern cannot be null.");
|
||||||
} catch (PatternSyntaxException e) {
|
} catch (ModelException e) {
|
||||||
// Expected PSE as regex pattern(or any of its token) is not quantifiable.
|
assertEquals("Invalid config for regexPattern: Not a valid regular expression", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
//Fails to match one of the regex pattern
|
//Fails to match one of the regex pattern
|
||||||
|
|
Loading…
Reference in a new issue