From 8c53c5a90e7861ad4c73cfa2084f084b974a24fb Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Tue, 23 May 2017 14:39:20 +0200 Subject: [PATCH] KEYCLOAK-4888 Change default hashing provider for realm --- .../hash/Pbkdf2PasswordHashProvider.java | 8 +- .../Pbkdf2PasswordHashProviderFactory.java | 4 +- ...kdf2Sha256PasswordHashProviderFactory.java | 4 +- ...kdf2Sha512PasswordHashProviderFactory.java | 4 +- .../migration/MigrationModelManager.java | 4 +- .../migration/migrators/MigrateTo3_2_0.java | 45 +++ ...erationsPasswordPolicyProviderFactory.java | 2 +- .../org/keycloak/models/PasswordPolicy.java | 166 ++++++++--- .../services/managers/RealmManager.java | 2 - .../keycloak/testsuite/util/LogChecker.java | 2 +- .../testsuite/admin/realm/RealmTest.java | 2 +- .../testsuite/forms/PasswordHashingTest.java | 32 ++- .../testsuite/migration/MigrationTest.java | 9 +- .../testsuite/policy/PasswordPolicyTest.java | 266 ++++++++++++++++++ .../testsuite/adduser/AddUserTest.java | 4 +- .../testsuite/model/PasswordPolicyTest.java | 188 ------------- 16 files changed, 490 insertions(+), 252 deletions(-) create mode 100644 server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java create mode 100755 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/policy/PasswordPolicyTest.java delete mode 100755 testsuite/integration/src/test/java/org/keycloak/testsuite/model/PasswordPolicyTest.java diff --git a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProvider.java b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProvider.java index 6c170e1ed7..6a6c1ff3eb 100644 --- a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProvider.java @@ -37,12 +37,14 @@ public class Pbkdf2PasswordHashProvider implements PasswordHashProvider { private final String providerId; private final String pbkdf2Algorithm; + private int defaultIterations; public static final int DERIVED_KEY_SIZE = 512; - public Pbkdf2PasswordHashProvider(String providerId, String pbkdf2Algorithm) { + public Pbkdf2PasswordHashProvider(String providerId, String pbkdf2Algorithm, int defaultIterations) { this.providerId = providerId; this.pbkdf2Algorithm = pbkdf2Algorithm; + this.defaultIterations = defaultIterations; } @Override @@ -52,6 +54,10 @@ public class Pbkdf2PasswordHashProvider implements PasswordHashProvider { @Override public void encode(String rawPassword, int iterations, CredentialModel credential) { + if (iterations == -1) { + iterations = defaultIterations; + } + byte[] salt = getSalt(); String encodedPassword = encode(rawPassword, iterations, salt); diff --git a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProviderFactory.java index ecd917d25d..44e0e12d76 100644 --- a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2PasswordHashProviderFactory.java @@ -30,9 +30,11 @@ public class Pbkdf2PasswordHashProviderFactory implements PasswordHashProviderFa public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1"; + public static final int DEFAULT_ITERATIONS = 20000; + @Override public PasswordHashProvider create(KeycloakSession session) { - return new Pbkdf2PasswordHashProvider(ID, PBKDF2_ALGORITHM); + return new Pbkdf2PasswordHashProvider(ID, PBKDF2_ALGORITHM, 20000); } @Override diff --git a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2Sha256PasswordHashProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2Sha256PasswordHashProviderFactory.java index c6453d1cc3..040879f4c2 100644 --- a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2Sha256PasswordHashProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2Sha256PasswordHashProviderFactory.java @@ -15,9 +15,11 @@ public class Pbkdf2Sha256PasswordHashProviderFactory implements PasswordHashProv public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256"; + public static final int DEFAULT_ITERATIONS = 27500; + @Override public PasswordHashProvider create(KeycloakSession session) { - return new Pbkdf2PasswordHashProvider(ID, PBKDF2_ALGORITHM); + return new Pbkdf2PasswordHashProvider(ID, PBKDF2_ALGORITHM, DEFAULT_ITERATIONS); } @Override diff --git a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2Sha512PasswordHashProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2Sha512PasswordHashProviderFactory.java index 5f838a1923..2e0700dca4 100644 --- a/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2Sha512PasswordHashProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/credential/hash/Pbkdf2Sha512PasswordHashProviderFactory.java @@ -15,9 +15,11 @@ public class Pbkdf2Sha512PasswordHashProviderFactory implements PasswordHashProv public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA512"; + public static final int DEFAULT_ITERATIONS = 30000; + @Override public PasswordHashProvider create(KeycloakSession session) { - return new Pbkdf2PasswordHashProvider(ID, PBKDF2_ALGORITHM); + return new Pbkdf2PasswordHashProvider(ID, PBKDF2_ALGORITHM, DEFAULT_ITERATIONS); } @Override diff --git a/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java index 1a36d7a853..4b620bc561 100755 --- a/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java +++ b/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java @@ -34,6 +34,7 @@ import org.keycloak.migration.migrators.MigrateTo2_3_0; import org.keycloak.migration.migrators.MigrateTo2_5_0; import org.keycloak.migration.migrators.MigrateTo3_0_0; import org.keycloak.migration.migrators.MigrateTo3_1_0; +import org.keycloak.migration.migrators.MigrateTo3_2_0; import org.keycloak.migration.migrators.Migration; import org.keycloak.models.KeycloakSession; @@ -60,7 +61,8 @@ public class MigrationModelManager { new MigrateTo2_3_0(), new MigrateTo2_5_0(), new MigrateTo3_0_0(), - new MigrateTo3_1_0() + new MigrateTo3_1_0(), + new MigrateTo3_2_0() }; public static void migrate(KeycloakSession session) { diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java new file mode 100644 index 0000000000..85f2296f85 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.migration.migrators; + + +import org.keycloak.migration.ModelVersion; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.PasswordPolicy; +import org.keycloak.models.RealmModel; + +public class MigrateTo3_2_0 implements Migration { + + public static final ModelVersion VERSION = new ModelVersion("3.1.0"); + + @Override + public void migrate(KeycloakSession session) { + for (RealmModel realm : session.realms().getRealms()) { + PasswordPolicy.Builder builder = realm.getPasswordPolicy().toBuilder(); + if (!builder.contains(PasswordPolicy.HASH_ALGORITHM_ID) && "20000".equals(builder.get(PasswordPolicy.HASH_ITERATIONS_ID))) { + realm.setPasswordPolicy(builder.remove(PasswordPolicy.HASH_ITERATIONS_ID).build(session)); + } + } + } + + @Override + public ModelVersion getVersion() { + return VERSION; + } + +} diff --git a/server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java index 695ab282ea..be4dd454ed 100644 --- a/server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java @@ -60,7 +60,7 @@ public class HashIterationsPasswordPolicyProviderFactory implements PasswordPoli @Override public Object parseConfig(String value) { - return value != null ? Integer.parseInt(value) : PasswordPolicy.HASH_ITERATIONS_DEFAULT; + return parseInteger(value, -1); } @Override diff --git a/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java b/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java index f3367af365..10d59d9b16 100755 --- a/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java +++ b/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java @@ -22,6 +22,7 @@ import org.keycloak.policy.PasswordPolicyProvider; import java.io.Serializable; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -32,62 +33,33 @@ public class PasswordPolicy implements Serializable { public static final String HASH_ALGORITHM_ID = "hashAlgorithm"; - public static final String HASH_ALGORITHM_DEFAULT = "pbkdf2"; + public static final String HASH_ALGORITHM_DEFAULT = "pbkdf2-sha256"; public static final String HASH_ITERATIONS_ID = "hashIterations"; - public static final int HASH_ITERATIONS_DEFAULT = 20000; + public static final int HASH_ITERATIONS_DEFAULT = 27500; public static final String PASSWORD_HISTORY_ID = "passwordHistory"; public static final String FORCE_EXPIRED_ID = "forceExpiredPasswordChange"; - private String policyString; private Map policyConfig; + private Builder builder; public static PasswordPolicy empty() { return new PasswordPolicy(null, new HashMap<>()); } - public static PasswordPolicy parse(KeycloakSession session, String policyString) { - Map policyConfig = new HashMap<>(); - - if (policyString != null && !policyString.trim().isEmpty()) { - for (String policy : policyString.split(" and ")) { - policy = policy.trim(); - - String key; - String config = null; - - int i = policy.indexOf('('); - if (i == -1) { - key = policy.trim(); - } else { - key = policy.substring(0, i).trim(); - config = policy.substring(i + 1, policy.length() - 1); - } - - PasswordPolicyProvider provider = session.getProvider(PasswordPolicyProvider.class, key); - if (provider == null) { - throw new PasswordPolicyConfigException("Password policy not found"); - } - - Object o; - try { - o = provider.parseConfig(config); - } catch (PasswordPolicyConfigException e) { - throw new ModelException("Invalid config for " + key + ": " + e.getMessage()); - } - - policyConfig.put(key, o); - } - } - - return new PasswordPolicy(policyString, policyConfig); + public static Builder build() { + return new Builder(); } - private PasswordPolicy(String policyString, Map policyConfig) { - this.policyString = policyString; + public static PasswordPolicy parse(KeycloakSession session, String policyString) { + return new Builder(policyString).build(session); + } + + private PasswordPolicy(Builder builder, Map policyConfig) { + this.builder = builder; this.policyConfig = policyConfig; } @@ -111,7 +83,7 @@ public class PasswordPolicy implements Serializable { if (policyConfig.containsKey(HASH_ITERATIONS_ID)) { return getPolicyConfig(HASH_ITERATIONS_ID); } else { - return HASH_ITERATIONS_DEFAULT; + return -1; } } @@ -133,7 +105,117 @@ public class PasswordPolicy implements Serializable { @Override public String toString() { - return policyString; + return builder.asString(); + } + + public Builder toBuilder() { + return builder.clone(); + } + + public static class Builder { + + private LinkedHashMap map; + + private Builder() { + this.map = new LinkedHashMap<>(); + } + + private Builder(LinkedHashMap map) { + this.map = map; + } + + private Builder(String policyString) { + map = new LinkedHashMap<>(); + + if (policyString != null && !policyString.trim().isEmpty()) { + for (String policy : policyString.split(" and ")) { + policy = policy.trim(); + + String key; + String config = null; + + int i = policy.indexOf('('); + if (i == -1) { + key = policy.trim(); + } else { + key = policy.substring(0, i).trim(); + config = policy.substring(i + 1, policy.length() - 1); + } + + map.put(key, config); + } + } + } + + public boolean contains(String key) { + return map.containsKey(key); + } + + public String get(String key) { + return map.get(key); + } + + public Builder put(String key, String value) { + map.put(key, value); + return this; + } + + public Builder remove(String key) { + map.remove(key); + return this; + } + + public PasswordPolicy build(KeycloakSession session) { + Map config = new HashMap<>(); + for (Map.Entry e : map.entrySet()) { + + PasswordPolicyProvider provider = session.getProvider(PasswordPolicyProvider.class, e.getKey()); + if (provider == null) { + throw new PasswordPolicyConfigException("Password policy not found"); + } + + Object o; + try { + o = provider.parseConfig(e.getValue()); + } catch (PasswordPolicyConfigException ex) { + throw new ModelException("Invalid config for " + e.getKey() + ": " + ex.getMessage()); + } + + config.put(e.getKey(), o); + } + return new PasswordPolicy(this, config); + } + + public String asString() { + if (map.isEmpty()) { + return null; + } + + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Map.Entry e : map.entrySet()) { + if (first) { + first = false; + } else { + sb.append(" and "); + } + + sb.append(e.getKey()); + + String c = e.getValue(); + if (c != null && !c.trim().isEmpty()) { + sb.append("("); + sb.append(c); + sb.append(")"); + } + } + return sb.toString(); + } + + public Builder clone() { + return new Builder((LinkedHashMap) map.clone()); + } + } } diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index e94ff3c00f..a83d80deb9 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -226,8 +226,6 @@ public class RealmManager { realm.setLoginWithEmailAllowed(true); realm.setEventsListeners(Collections.singleton("jboss-logging")); - - realm.setPasswordPolicy(PasswordPolicy.parse(session, "hashIterations(20000)")); } public boolean removeRealm(RealmModel realm) { diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LogChecker.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LogChecker.java index e968c7d5cf..dd5d6d888a 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LogChecker.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LogChecker.java @@ -31,7 +31,7 @@ public class LogChecker { private static final Logger log = Logger.getLogger(LogChecker.class); - private static final String[] IGNORED = new String[] { ".*Jetty ALPN support not found.*" }; + private static final String[] IGNORED = new String[] { ".*Jetty ALPN support not found.*", ".*org.keycloak.events.*" }; public static void checkServerLog(File logFile) throws IOException { log.info(String.format("Checking server log: '%s'", logFile.getAbsolutePath())); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java index ba0b7c9759..7f811c6dc6 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java @@ -173,7 +173,7 @@ public class RealmTest extends AbstractAdminTest { adminClient.realms().create(rep); - assertEquals("hashIterations(20000)", adminClient.realm("new-realm").toRepresentation().getPasswordPolicy()); + assertEquals(null, adminClient.realm("new-realm").toRepresentation().getPasswordPolicy()); adminClient.realms().realm("new-realm").remove(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/PasswordHashingTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/PasswordHashingTest.java index 0784f01b45..6763e7d20d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/PasswordHashingTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/PasswordHashingTest.java @@ -103,34 +103,38 @@ public class PasswordHashingTest extends AbstractTestRealmKeycloakTest { @Test public void testPasswordRehashedOnAlgorithmChanged() throws Exception { + setPasswordPolicy("hashAlgorithm(" + Pbkdf2Sha256PasswordHashProviderFactory.ID + ") and hashIterations(1)"); + String username = "testPasswordRehashedOnAlgorithmChanged"; createUser(username); CredentialModel credential = fetchCredentials(username); - assertEquals(Pbkdf2PasswordHashProviderFactory.ID, credential.getAlgorithm()); + assertEquals(Pbkdf2Sha256PasswordHashProviderFactory.ID, credential.getAlgorithm()); - assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA1", 20000); + assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA256", 1); - setPasswordPolicy("hashAlgorithm(" + Pbkdf2Sha256PasswordHashProviderFactory.ID + ")"); + setPasswordPolicy("hashAlgorithm(" + Pbkdf2PasswordHashProviderFactory.ID + ") and hashIterations(1)"); loginPage.open(); loginPage.login(username, "password"); credential = fetchCredentials(username); - assertEquals(Pbkdf2Sha256PasswordHashProviderFactory.ID, credential.getAlgorithm()); - assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA256", 20000); + assertEquals(Pbkdf2PasswordHashProviderFactory.ID, credential.getAlgorithm()); + assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA1", 1); } @Test public void testPasswordRehashedOnIterationsChanged() throws Exception { + setPasswordPolicy("hashIterations(10000)"); + String username = "testPasswordRehashedOnIterationsChanged"; createUser(username); CredentialModel credential = fetchCredentials(username); - assertEquals(20000, credential.getHashIterations()); + assertEquals(10000, credential.getHashIterations()); setPasswordPolicy("hashIterations(1)"); @@ -140,7 +144,7 @@ public class PasswordHashingTest extends AbstractTestRealmKeycloakTest { credential = fetchCredentials(username); assertEquals(1, credential.getHashIterations()); - assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA1", 1); + assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA256", 1); } @Test @@ -153,6 +157,16 @@ public class PasswordHashingTest extends AbstractTestRealmKeycloakTest { assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA1", 20000); } + @Test + public void testDefault() throws Exception { + setPasswordPolicy(""); + String username = "testDefault"; + createUser(username); + + CredentialModel credential = fetchCredentials(username); + assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA256", 27500); + } + @Test public void testPbkdf2Sha256() throws Exception { setPasswordPolicy("hashAlgorithm(" + Pbkdf2Sha256PasswordHashProviderFactory.ID + ")"); @@ -160,7 +174,7 @@ public class PasswordHashingTest extends AbstractTestRealmKeycloakTest { createUser(username); CredentialModel credential = fetchCredentials(username); - assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA256", 20000); + assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA256", 27500); } @Test @@ -170,7 +184,7 @@ public class PasswordHashingTest extends AbstractTestRealmKeycloakTest { createUser(username); CredentialModel credential = fetchCredentials(username); - assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA512", 20000); + assertEncoded(credential, "password", credential.getSalt(), "PBKDF2WithHmacSHA512", 30000); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java index 04a758ed2d..a769687f0d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java @@ -59,6 +59,7 @@ import org.keycloak.testsuite.runonserver.RunHelpers; import org.keycloak.testsuite.runonserver.RunOnServerDeployment; import org.keycloak.testsuite.util.OAuthClient; +import static org.junit.Assert.assertNull; import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT; import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS; import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID; @@ -132,6 +133,7 @@ public class MigrationTest extends AbstractKeycloakTest { testMigrationTo2_5_0(); testMigrationTo2_5_1(); testMigrationTo3_0_0(); + testMigrationTo3_2_0(); } @Test @@ -210,7 +212,12 @@ public class MigrationTest extends AbstractKeycloakTest { private void testMigrationTo3_0_0() { testRoleManageAccountLinks(masterRealm, migrationRealm); } - + + private void testMigrationTo3_2_0() { + assertNull(masterRealm.toRepresentation().getPasswordPolicy()); + assertNull(migrationRealm.toRepresentation().getPasswordPolicy()); + } + private void testRoleManageAccountLinks(RealmResource... realms) { log.info("testing role manage account links"); for (RealmResource realm : realms) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/policy/PasswordPolicyTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/policy/PasswordPolicyTest.java new file mode 100755 index 0000000000..4b7b41951d --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/policy/PasswordPolicyTest.java @@ -0,0 +1,266 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.policy; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.models.ModelException; +import org.keycloak.models.PasswordPolicy; +import org.keycloak.models.RealmModel; +import org.keycloak.policy.PasswordPolicyManagerProvider; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.runonserver.RunOnServerDeployment; +import org.keycloak.testsuite.util.RealmBuilder; + +import java.util.List; +import java.util.regex.PatternSyntaxException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author Stian Thorgersen + */ +public class PasswordPolicyTest extends AbstractKeycloakTest { + + @Deployment + public static WebArchive deploy() { + return RunOnServerDeployment.create(); + } + + @Test + public void testLength() { + testingClient.server("passwordPolicy").run(session -> { + RealmModel realmModel = session.getContext().getRealm(); + PasswordPolicyManagerProvider policyManager = session.getProvider(PasswordPolicyManagerProvider.class); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "length")); + + Assert.assertEquals("invalidPasswordMinLengthMessage", policyManager.validate("jdoe", "1234567").getMessage()); + Assert.assertArrayEquals(new Object[]{8}, policyManager.validate("jdoe", "1234567").getParameters()); + assertNull(policyManager.validate("jdoe", "12345678")); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "length(4)")); + + Assert.assertEquals("invalidPasswordMinLengthMessage", policyManager.validate("jdoe", "123").getMessage()); + Assert.assertArrayEquals(new Object[]{4}, policyManager.validate("jdoe", "123").getParameters()); + assertNull(policyManager.validate("jdoe", "1234")); + }); + } + + @Test + public void testDigits() { + testingClient.server("passwordPolicy").run(session -> { + RealmModel realmModel = session.getContext().getRealm(); + PasswordPolicyManagerProvider policyManager = session.getProvider(PasswordPolicyManagerProvider.class); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "digits")); + Assert.assertEquals("invalidPasswordMinDigitsMessage", policyManager.validate("jdoe", "abcd").getMessage()); + Assert.assertArrayEquals(new Object[]{1}, policyManager.validate("jdoe", "abcd").getParameters()); + assertNull(policyManager.validate("jdoe", "abcd1")); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "digits(2)")); + Assert.assertEquals("invalidPasswordMinDigitsMessage", policyManager.validate("jdoe", "abcd1").getMessage()); + Assert.assertArrayEquals(new Object[]{2}, policyManager.validate("jdoe", "abcd1").getParameters()); + assertNull(policyManager.validate("jdoe", "abcd12")); + }); + } + + @Test + public void testLowerCase() { + testingClient.server("passwordPolicy").run(session -> { + RealmModel realmModel = session.getContext().getRealm(); + PasswordPolicyManagerProvider policyManager = session.getProvider(PasswordPolicyManagerProvider.class); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "lowerCase")); + Assert.assertEquals("invalidPasswordMinLowerCaseCharsMessage", policyManager.validate("jdoe", "ABCD1234").getMessage()); + Assert.assertArrayEquals(new Object[]{1}, policyManager.validate("jdoe", "ABCD1234").getParameters()); + assertNull(policyManager.validate("jdoe", "ABcD1234")); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "lowerCase(2)")); + Assert.assertEquals("invalidPasswordMinLowerCaseCharsMessage", policyManager.validate("jdoe", "ABcD1234").getMessage()); + Assert.assertArrayEquals(new Object[]{2}, policyManager.validate("jdoe", "ABcD1234").getParameters()); + assertNull(policyManager.validate("jdoe", "aBcD1234")); + }); + } + + @Test + public void testUpperCase() { + testingClient.server("passwordPolicy").run(session -> { + RealmModel realmModel = session.getContext().getRealm(); + PasswordPolicyManagerProvider policyManager = session.getProvider(PasswordPolicyManagerProvider.class); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "upperCase")); + Assert.assertEquals("invalidPasswordMinUpperCaseCharsMessage", policyManager.validate("jdoe", "abcd1234").getMessage()); + Assert.assertArrayEquals(new Object[]{1}, policyManager.validate("jdoe", "abcd1234").getParameters()); + assertNull(policyManager.validate("jdoe", "abCd1234")); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "upperCase(2)")); + Assert.assertEquals("invalidPasswordMinUpperCaseCharsMessage", policyManager.validate("jdoe", "abCd1234").getMessage()); + Assert.assertArrayEquals(new Object[]{2}, policyManager.validate("jdoe", "abCd1234").getParameters()); + assertNull(policyManager.validate("jdoe", "AbCd1234")); + }); + } + + @Test + public void testSpecialChars() { + testingClient.server("passwordPolicy").run(session -> { + RealmModel realmModel = session.getContext().getRealm(); + PasswordPolicyManagerProvider policyManager = session.getProvider(PasswordPolicyManagerProvider.class); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "specialChars")); + Assert.assertEquals("invalidPasswordMinSpecialCharsMessage", policyManager.validate("jdoe", "abcd1234").getMessage()); + Assert.assertArrayEquals(new Object[]{1}, policyManager.validate("jdoe", "abcd1234").getParameters()); + assertNull(policyManager.validate("jdoe", "ab&d1234")); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "specialChars(2)")); + Assert.assertEquals("invalidPasswordMinSpecialCharsMessage", policyManager.validate("jdoe", "ab&d1234").getMessage()); + Assert.assertArrayEquals(new Object[]{2}, policyManager.validate("jdoe", "ab&d1234").getParameters()); + assertNull(policyManager.validate("jdoe", "ab&d-234")); + }); + } + + @Test + public void testNotUsername() { + testingClient.server("passwordPolicy").run(session -> { + RealmModel realmModel = session.getContext().getRealm(); + PasswordPolicyManagerProvider policyManager = session.getProvider(PasswordPolicyManagerProvider.class); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "notUsername")); + Assert.assertEquals("invalidPasswordNotUsernameMessage", policyManager.validate("jdoe", "jdoe").getMessage()); + assertNull(policyManager.validate("jdoe", "ab&d1234")); + }); + } + + @Test + public void testInvalidPolicyName() { + testingClient.server("passwordPolicy").run(session -> { + RealmModel realmModel = session.getContext().getRealm(); + PasswordPolicyManagerProvider policyManager = session.getProvider(PasswordPolicyManagerProvider.class); + + try { + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "noSuchPolicy")); + Assert.fail("Expected exception"); + } catch (ModelException e) { + assertEquals("Password policy not found", e.getMessage()); + } + }); + } + + @Test + public void testRegexPatterns() { + testingClient.server("passwordPolicy").run(session -> { + RealmModel realmModel = session.getContext().getRealm(); + PasswordPolicyManagerProvider policyManager = session.getProvider(PasswordPolicyManagerProvider.class); + + PasswordPolicy policy = null; + try { + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern")); + fail("Expected NullPointerException: Regex Pattern cannot be null."); + } catch (ModelException e) { + assertEquals("Invalid config for regexPattern: Config required", e.getMessage()); + } + + try { + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(*)")); + fail("Expected PatternSyntaxException: Regex Pattern cannot be null."); + } catch (ModelException e) { + assertEquals("Invalid config for regexPattern: Not a valid regular expression", e.getMessage()); + } + + try { + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(*,**)")); + fail("Expected PatternSyntaxException: Regex Pattern cannot be null."); + } catch (ModelException e) { + assertEquals("Invalid config for regexPattern: Not a valid regular expression", e.getMessage()); + } + + //Fails to match one of the regex pattern + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(jdoe) and regexPattern(j*d)")); + Assert.assertEquals("invalidPasswordRegexPatternMessage", policyManager.validate("jdoe", "jdoe").getMessage()); + + ////Fails to match all of the regex patterns + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(j*p) and regexPattern(j*d) and regexPattern(adoe)")); + Assert.assertEquals("invalidPasswordRegexPatternMessage", policyManager.validate("jdoe", "jdoe").getMessage()); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern([a-z][a-z][a-z][a-z][0-9])")); + Assert.assertEquals("invalidPasswordRegexPatternMessage", policyManager.validate("jdoe", "jdoe").getMessage()); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(jdoe)")); + assertNull(policyManager.validate("jdoe", "jdoe")); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern([a-z][a-z][a-z][a-z][0-9])")); + assertNull(policyManager.validate("jdoe", "jdoe0")); + }); + } + + @Test + public void testComplex() { + testingClient.server("passwordPolicy").run(session -> { + RealmModel realmModel = session.getContext().getRealm(); + PasswordPolicyManagerProvider policyManager = session.getProvider(PasswordPolicyManagerProvider.class); + + realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "length(8) and digits(2) and lowerCase(2) and upperCase(2) and specialChars(2) and notUsername()")); + Assert.assertNotNull(policyManager.validate("jdoe", "12aaBB&")); + Assert.assertNotNull(policyManager.validate("jdoe", "aaaaBB&-")); + Assert.assertNotNull(policyManager.validate("jdoe", "12AABB&-")); + Assert.assertNotNull(policyManager.validate("jdoe", "12aabb&-")); + Assert.assertNotNull(policyManager.validate("jdoe", "12aaBBcc")); + Assert.assertNotNull(policyManager.validate("12aaBB&-", "12aaBB&-")); + + assertNull(policyManager.validate("jdoe", "12aaBB&-")); + }); + } + + @Test + public void testBuilder() { + testingClient.server("passwordPolicy").run(session -> { + PasswordPolicy.Builder builder = PasswordPolicy.parse(session, "hashIterations(20000)").toBuilder(); + assertFalse(builder.contains(PasswordPolicy.HASH_ALGORITHM_ID)); + assertTrue("20000".equals(builder.get(PasswordPolicy.HASH_ITERATIONS_ID))); + + builder.remove(PasswordPolicy.HASH_ITERATIONS_ID); + + assertNull(builder.asString()); + + builder = PasswordPolicy.parse(session, "hashIterations(20000) and hashAlgorithm(pbkdf2)").toBuilder(); + assertTrue(builder.contains(PasswordPolicy.HASH_ALGORITHM_ID)); + + builder = PasswordPolicy.parse(session, "hashIterations(20000) and length(100)").toBuilder(); + builder.remove(PasswordPolicy.HASH_ITERATIONS_ID); + assertEquals("length(100)", builder.asString()); + + builder = PasswordPolicy.parse(session, "digits(10) and hashIterations(20000) and length(100)").toBuilder(); + builder.remove(PasswordPolicy.HASH_ITERATIONS_ID); + assertEquals("digits(10) and length(100)", builder.asString()); + }); + } + + @Override + public void addTestRealms(List testRealms) { + testRealms.add(RealmBuilder.create().name("passwordPolicy").build()); + } + +} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adduser/AddUserTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adduser/AddUserTest.java index fb23bcd5c5..8dff78966d 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adduser/AddUserTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adduser/AddUserTest.java @@ -26,7 +26,7 @@ import org.junit.rules.TemporaryFolder; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.UserResource; -import org.keycloak.credential.hash.Pbkdf2PasswordHashProviderFactory; +import org.keycloak.credential.hash.Pbkdf2Sha256PasswordHashProviderFactory; import org.keycloak.models.Constants; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; @@ -85,7 +85,7 @@ public class AddUserTest { CredentialRepresentation credentials = user.getCredentials().get(0); - assertEquals(Pbkdf2PasswordHashProviderFactory.ID, credentials.getAlgorithm()); + assertEquals(Pbkdf2Sha256PasswordHashProviderFactory.ID, credentials.getAlgorithm()); assertEquals(new Integer(100000), credentials.getHashIterations()); KeycloakServer server = new KeycloakServer(); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/PasswordPolicyTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/PasswordPolicyTest.java deleted file mode 100755 index 93ee3683f1..0000000000 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/PasswordPolicyTest.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.testsuite.model; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.keycloak.models.ModelException; -import org.keycloak.models.PasswordPolicy; -import org.keycloak.models.RealmModel; -import org.keycloak.policy.PasswordPolicyManagerProvider; - -import java.util.regex.PatternSyntaxException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -/** - * @author Stian Thorgersen - */ -public class PasswordPolicyTest extends AbstractModelTest { - - private RealmModel realmModel; - private PasswordPolicyManagerProvider policyManager; - - @Before - public void before() throws Exception { - super.before(); - realmModel = realmManager.createRealm("JUGGLER"); - session.getContext().setRealm(realmModel); - policyManager = session.getProvider(PasswordPolicyManagerProvider.class); - } - - @Test - public void testLength() { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "length")); - - Assert.assertEquals("invalidPasswordMinLengthMessage", policyManager.validate("jdoe", "1234567").getMessage()); - Assert.assertArrayEquals(new Object[]{8}, policyManager.validate("jdoe", "1234567").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "12345678")); - - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "length(4)")); - - Assert.assertEquals("invalidPasswordMinLengthMessage", policyManager.validate("jdoe", "123").getMessage()); - Assert.assertArrayEquals(new Object[]{4}, policyManager.validate("jdoe", "123").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "1234")); - } - - @Test - public void testDigits() { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "digits")); - Assert.assertEquals("invalidPasswordMinDigitsMessage", policyManager.validate("jdoe", "abcd").getMessage()); - Assert.assertArrayEquals(new Object[]{1}, policyManager.validate("jdoe", "abcd").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "abcd1")); - - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "digits(2)")); - Assert.assertEquals("invalidPasswordMinDigitsMessage", policyManager.validate("jdoe", "abcd1").getMessage()); - Assert.assertArrayEquals(new Object[]{2}, policyManager.validate("jdoe", "abcd1").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "abcd12")); - } - - @Test - public void testLowerCase() { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "lowerCase")); - Assert.assertEquals("invalidPasswordMinLowerCaseCharsMessage", policyManager.validate("jdoe", "ABCD1234").getMessage()); - Assert.assertArrayEquals(new Object[]{1}, policyManager.validate("jdoe", "ABCD1234").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "ABcD1234")); - - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "lowerCase(2)")); - Assert.assertEquals("invalidPasswordMinLowerCaseCharsMessage", policyManager.validate("jdoe", "ABcD1234").getMessage()); - Assert.assertArrayEquals(new Object[]{2}, policyManager.validate("jdoe", "ABcD1234").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "aBcD1234")); - } - - @Test - public void testUpperCase() { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "upperCase")); - Assert.assertEquals("invalidPasswordMinUpperCaseCharsMessage", policyManager.validate("jdoe", "abcd1234").getMessage()); - Assert.assertArrayEquals(new Object[]{1}, policyManager.validate("jdoe", "abcd1234").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "abCd1234")); - - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "upperCase(2)")); - Assert.assertEquals("invalidPasswordMinUpperCaseCharsMessage", policyManager.validate("jdoe", "abCd1234").getMessage()); - Assert.assertArrayEquals(new Object[]{2}, policyManager.validate("jdoe", "abCd1234").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "AbCd1234")); - } - - @Test - public void testSpecialChars() { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "specialChars")); - Assert.assertEquals("invalidPasswordMinSpecialCharsMessage", policyManager.validate("jdoe", "abcd1234").getMessage()); - Assert.assertArrayEquals(new Object[]{1}, policyManager.validate("jdoe", "abcd1234").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "ab&d1234")); - - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "specialChars(2)")); - Assert.assertEquals("invalidPasswordMinSpecialCharsMessage", policyManager.validate("jdoe", "ab&d1234").getMessage()); - Assert.assertArrayEquals(new Object[]{2}, policyManager.validate("jdoe", "ab&d1234").getParameters()); - Assert.assertNull(policyManager.validate("jdoe", "ab&d-234")); - } - - @Test - public void testNotUsername() { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "notUsername")); - Assert.assertEquals("invalidPasswordNotUsernameMessage", policyManager.validate("jdoe", "jdoe").getMessage()); - Assert.assertNull(policyManager.validate("jdoe", "ab&d1234")); - } - - @Test - public void testInvalidPolicyName() { - try { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "noSuchPolicy")); - Assert.fail("Expected exception"); - } catch (ModelException e) { - assertEquals("Password policy not found", e.getMessage()); - } - } - - @Test - public void testRegexPatterns() { - PasswordPolicy policy = null; - try { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern")); - fail("Expected NullPointerException: Regex Pattern cannot be null."); - } catch (ModelException e) { - assertEquals("Invalid config for regexPattern: Config required", e.getMessage()); - } - - try { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(*)")); - fail("Expected PatternSyntaxException: Regex Pattern cannot be null."); - } catch (ModelException e) { - assertEquals("Invalid config for regexPattern: Not a valid regular expression", e.getMessage()); - } - - try { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(*,**)")); - fail("Expected PatternSyntaxException: Regex Pattern cannot be null."); - } catch (ModelException e) { - assertEquals("Invalid config for regexPattern: Not a valid regular expression", e.getMessage()); - } - - //Fails to match one of the regex pattern - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(jdoe) and regexPattern(j*d)")); - Assert.assertEquals("invalidPasswordRegexPatternMessage", policyManager.validate("jdoe", "jdoe").getMessage()); - - ////Fails to match all of the regex patterns - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(j*p) and regexPattern(j*d) and regexPattern(adoe)")); - Assert.assertEquals("invalidPasswordRegexPatternMessage", policyManager.validate("jdoe", "jdoe").getMessage()); - - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern([a-z][a-z][a-z][a-z][0-9])")); - Assert.assertEquals("invalidPasswordRegexPatternMessage", policyManager.validate("jdoe", "jdoe").getMessage()); - - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern(jdoe)")); - Assert.assertNull(policyManager.validate("jdoe", "jdoe")); - - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "regexPattern([a-z][a-z][a-z][a-z][0-9])")); - Assert.assertNull(policyManager.validate("jdoe", "jdoe0")); - } - - @Test - public void testComplex() { - realmModel.setPasswordPolicy(PasswordPolicy.parse(session, "length(8) and digits(2) and lowerCase(2) and upperCase(2) and specialChars(2) and notUsername()")); - Assert.assertNotNull(policyManager.validate("jdoe", "12aaBB&")); - Assert.assertNotNull(policyManager.validate("jdoe", "aaaaBB&-")); - Assert.assertNotNull(policyManager.validate("jdoe", "12AABB&-")); - Assert.assertNotNull(policyManager.validate("jdoe", "12aabb&-")); - Assert.assertNotNull(policyManager.validate("jdoe", "12aaBBcc")); - Assert.assertNotNull(policyManager.validate("12aaBB&-", "12aaBB&-")); - - Assert.assertNull(policyManager.validate("jdoe", "12aaBB&-")); - } - -}