diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
index b3b0edfcee..53bcf1f1fa 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
@@ -1042,7 +1042,8 @@ module.factory('PasswordPolicy', function() {
digits: "Minimal number (integer type) of digits in password. Default value is 1.",
lowerCase: "Minimal number (integer type) of lowercase characters in password. Default value is 1.",
upperCase: "Minimal number (integer type) of uppercase characters in password. Default value is 1.",
- specialChars: "Minimal number (integer type) of special characters in password. Default value is 1."
+ specialChars: "Minimal number (integer type) of special characters in password. Default value is 1.",
+ notUsername: "Block passwords that are equal to the username"
}
p.allPolicies = [
@@ -1051,7 +1052,8 @@ module.factory('PasswordPolicy', function() {
{ name: 'digits', value: 1 },
{ name: 'lowerCase', value: 1 },
{ name: 'upperCase', value: 1 },
- { name: 'specialChars', value: 1 }
+ { name: 'specialChars', value: 1 },
+ { name: 'notUsername', value: 1 }
];
p.parse = function(policyString) {
@@ -1068,9 +1070,9 @@ module.factory('PasswordPolicy', function() {
var re = /(\w+)\(*(\d*)\)*/;
var policyEntry = re.exec(policyToken);
-
- policies.push({ name: policyEntry[1], value: parseInt(policyEntry[2]) });
-
+ if (null !== policyEntry) {
+ policies.push({ name: policyEntry[1], value: parseInt(policyEntry[2]) });
+ }
}
return policies;
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-credentials.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-credentials.html
index 6dff85cba7..608a2b622f 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-credentials.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-credentials.html
@@ -46,7 +46,9 @@
-
+
|
diff --git a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
index dc82bcc5f5..3651e9d3f4 100755
--- a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
+++ b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
@@ -52,6 +52,8 @@ public class PasswordPolicy {
list.add(new UpperCase(args));
} else if (name.equals(SpecialChars.NAME)) {
list.add(new SpecialChars(args));
+ } else if (name.equals(NotUsername.NAME)) {
+ list.add(new NotUsername(args));
} else if (name.equals(HashIterations.NAME)) {
list.add(new HashIterations(args));
}
@@ -74,9 +76,9 @@ public class PasswordPolicy {
return -1;
}
- public String validate(String password) {
+ public String validate(String username, String password) {
for (Policy p : policies) {
- String error = p.validate(password);
+ String error = p.validate(username, password);
if (error != null) {
return error;
}
@@ -85,7 +87,7 @@ public class PasswordPolicy {
}
private static interface Policy {
- public String validate(String password);
+ public String validate(String username, String password);
}
private static class HashIterations implements Policy {
@@ -97,11 +99,23 @@ public class PasswordPolicy {
}
@Override
- public String validate(String password) {
+ public String validate(String username, String password) {
return null;
}
}
+ private static class NotUsername implements Policy {
+ private static final String NAME = "notUsername";
+
+ public NotUsername(String[] args) {
+ }
+
+ @Override
+ public String validate(String username, String password) {
+ return username.equals(password) ? "Invalid password: must not be equal to the username" : null;
+ }
+ }
+
private static class Length implements Policy {
private static final String NAME = "length";
private int min;
@@ -111,7 +125,7 @@ public class PasswordPolicy {
}
@Override
- public String validate(String password) {
+ public String validate(String username, String password) {
return password.length() < min ? "Invalid password: minimum length " + min : null;
}
}
@@ -125,7 +139,7 @@ public class PasswordPolicy {
}
@Override
- public String validate(String password) {
+ public String validate(String username, String password) {
int count = 0;
for (char c : password.toCharArray()) {
if (Character.isDigit(c)) {
@@ -145,7 +159,7 @@ public class PasswordPolicy {
}
@Override
- public String validate(String password) {
+ public String validate(String username, String password) {
int count = 0;
for (char c : password.toCharArray()) {
if (Character.isLowerCase(c)) {
@@ -165,7 +179,7 @@ public class PasswordPolicy {
}
@Override
- public String validate(String password) {
+ public String validate(String username, String password) {
int count = 0;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) {
@@ -185,7 +199,7 @@ public class PasswordPolicy {
}
@Override
- public String validate(String password) {
+ public String validate(String username, String password) {
int count = 0;
for (char c : password.toCharArray()) {
if (!Character.isLetterOrDigit(c)) {
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 fe96e5af16..a360f958a4 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -323,7 +323,7 @@ public class UserFederationManager implements UserProvider {
public void updateCredential(RealmModel realm, UserModel user, UserCredentialModel credential) {
if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
if (realm.getPasswordPolicy() != null) {
- String error = realm.getPasswordPolicy().validate(credential.getValue());
+ String error = realm.getPasswordPolicy().validate(user.getUsername(), credential.getValue());
if (error != null) throw new ModelException(error);
}
}
diff --git a/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java b/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
index 3481498884..7242c554df 100644
--- a/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
+++ b/model/api/src/test/java/org/keycloak/models/PasswordPolicyTest.java
@@ -11,68 +11,76 @@ public class PasswordPolicyTest {
@Test
public void testLength() {
PasswordPolicy policy = new PasswordPolicy("length");
- Assert.assertEquals("Invalid password: minimum length 8", policy.validate("1234567"));
- Assert.assertNull(policy.validate("12345678"));
+ Assert.assertEquals("Invalid password: minimum length 8", policy.validate("jdoe", "1234567"));
+ Assert.assertNull(policy.validate("jdoe", "12345678"));
policy = new PasswordPolicy("length(4)");
- Assert.assertEquals("Invalid password: minimum length 4", policy.validate("123"));
- Assert.assertNull(policy.validate("1234"));
+ Assert.assertEquals("Invalid password: minimum length 4", policy.validate("jdoe", "123"));
+ Assert.assertNull(policy.validate("jdoe", "1234"));
}
@Test
public void testDigits() {
PasswordPolicy policy = new PasswordPolicy("digits");
- Assert.assertEquals("Invalid password: must contain at least 1 numerical digits", policy.validate("abcd"));
- Assert.assertNull(policy.validate("abcd1"));
+ Assert.assertEquals("Invalid password: must contain at least 1 numerical digits", policy.validate("jdoe", "abcd"));
+ Assert.assertNull(policy.validate("jdoe", "abcd1"));
policy = new PasswordPolicy("digits(2)");
- Assert.assertEquals("Invalid password: must contain at least 2 numerical digits", policy.validate("abcd1"));
- Assert.assertNull(policy.validate("abcd12"));
+ Assert.assertEquals("Invalid password: must contain at least 2 numerical digits", policy.validate("jdoe", "abcd1"));
+ Assert.assertNull(policy.validate("jdoe", "abcd12"));
}
@Test
public void testLowerCase() {
PasswordPolicy policy = new PasswordPolicy("lowerCase");
- Assert.assertEquals("Invalid password: must contain at least 1 lower case characters", policy.validate("ABCD1234"));
- Assert.assertNull(policy.validate("ABcD1234"));
+ Assert.assertEquals("Invalid password: must contain at least 1 lower case characters", policy.validate("jdoe", "ABCD1234"));
+ Assert.assertNull(policy.validate("jdoe", "ABcD1234"));
policy = new PasswordPolicy("lowerCase(2)");
- Assert.assertEquals("Invalid password: must contain at least 2 lower case characters", policy.validate("ABcD1234"));
- Assert.assertNull(policy.validate("aBcD1234"));
+ Assert.assertEquals("Invalid password: must contain at least 2 lower case characters", policy.validate("jdoe", "ABcD1234"));
+ Assert.assertNull(policy.validate("jdoe", "aBcD1234"));
}
@Test
public void testUpperCase() {
PasswordPolicy policy = new PasswordPolicy("upperCase");
- Assert.assertEquals("Invalid password: must contain at least 1 upper case characters", policy.validate("abcd1234"));
- Assert.assertNull(policy.validate("abCd1234"));
+ Assert.assertEquals("Invalid password: must contain at least 1 upper case characters", policy.validate("jdoe", "abcd1234"));
+ Assert.assertNull(policy.validate("jdoe", "abCd1234"));
policy = new PasswordPolicy("upperCase(2)");
- Assert.assertEquals("Invalid password: must contain at least 2 upper case characters", policy.validate("abCd1234"));
- Assert.assertNull(policy.validate("AbCd1234"));
+ Assert.assertEquals("Invalid password: must contain at least 2 upper case characters", policy.validate("jdoe", "abCd1234"));
+ Assert.assertNull(policy.validate("jdoe", "AbCd1234"));
}
@Test
public void testSpecialChars() {
PasswordPolicy policy = new PasswordPolicy("specialChars");
- Assert.assertEquals("Invalid password: must contain at least 1 special characters", policy.validate("abcd1234"));
- Assert.assertNull(policy.validate("ab&d1234"));
+ Assert.assertEquals("Invalid password: must contain at least 1 special characters", policy.validate("jdoe", "abcd1234"));
+ Assert.assertNull(policy.validate("jdoe", "ab&d1234"));
policy = new PasswordPolicy("specialChars(2)");
- Assert.assertEquals("Invalid password: must contain at least 2 special characters", policy.validate("ab&d1234"));
- Assert.assertNull(policy.validate("ab&d-234"));
+ Assert.assertEquals("Invalid password: must contain at least 2 special characters", policy.validate("jdoe", "ab&d1234"));
+ Assert.assertNull(policy.validate("jdoe", "ab&d-234"));
+ }
+
+ @Test
+ public void testNotUsername() {
+ PasswordPolicy policy = new PasswordPolicy("notUsername");
+ Assert.assertEquals("Invalid password: must not be equal to the username", policy.validate("jdoe", "jdoe"));
+ Assert.assertNull(policy.validate("jdoe", "ab&d1234"));
}
@Test
public void testComplex() {
- PasswordPolicy policy = new PasswordPolicy("length(8) and digits(2) and lowerCase(2) and upperCase(2) and specialChars(2)");
- Assert.assertNotNull(policy.validate("12aaBB&"));
- Assert.assertNotNull(policy.validate("aaaaBB&-"));
- Assert.assertNotNull(policy.validate("12AABB&-"));
- Assert.assertNotNull(policy.validate("12aabb&-"));
- Assert.assertNotNull(policy.validate("12aaBBcc"));
+ PasswordPolicy policy = new PasswordPolicy("length(8) and digits(2) and lowerCase(2) and upperCase(2) and specialChars(2) and notUsername()");
+ Assert.assertNotNull(policy.validate("jdoe", "12aaBB&"));
+ Assert.assertNotNull(policy.validate("jdoe", "aaaaBB&-"));
+ Assert.assertNotNull(policy.validate("jdoe", "12AABB&-"));
+ Assert.assertNotNull(policy.validate("jdoe", "12aabb&-"));
+ Assert.assertNotNull(policy.validate("jdoe", "12aaBBcc"));
+ Assert.assertNotNull(policy.validate("12aaBB&-", "12aaBB&-"));
- Assert.assertNull(policy.validate("12aaBB&-"));
+ Assert.assertNull(policy.validate("jdoe", "12aaBB&-"));
}
}
diff --git a/services/src/main/java/org/keycloak/services/validation/Validation.java b/services/src/main/java/org/keycloak/services/validation/Validation.java
index 254cee1e8c..d306dc050d 100755
--- a/services/src/main/java/org/keycloak/services/validation/Validation.java
+++ b/services/src/main/java/org/keycloak/services/validation/Validation.java
@@ -49,7 +49,7 @@ public class Validation {
}
public static String validatePassword(MultivaluedMap formData, PasswordPolicy policy) {
- return policy.validate(formData.getFirst("password"));
+ return policy.validate(formData.getFirst("username"), formData.getFirst("password"));
}
public static String validateUpdateProfileForm(MultivaluedMap formData) {
|