From 0f967b7acb0fc8c0f122961423d024b612da059b Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Sun, 24 May 2020 01:43:01 +0200 Subject: [PATCH] KEYCLOAK-12729 Add password policy not-email Added test cases and initial translations --- .../NotEmailPasswordPolicyProvider.java | 60 +++++++++++++++ ...NotEmailPasswordPolicyProviderFactory.java | 73 +++++++++++++++++++ ...cloak.policy.PasswordPolicyProviderFactory | 1 + .../account/AccountFormServiceTest.java | 18 +++++ .../testsuite/forms/RegisterTest.java | 22 ++++++ .../account/messages/messages_de.properties | 1 + .../admin/messages/messages_de.properties | 1 + .../login/messages/messages_de.properties | 1 + .../account/messages/messages_en.properties | 1 + .../admin/messages/messages_en.properties | 1 + .../login/messages/messages_en.properties | 1 + 11 files changed, 180 insertions(+) create mode 100644 server-spi-private/src/main/java/org/keycloak/policy/NotEmailPasswordPolicyProvider.java create mode 100644 server-spi-private/src/main/java/org/keycloak/policy/NotEmailPasswordPolicyProviderFactory.java diff --git a/server-spi-private/src/main/java/org/keycloak/policy/NotEmailPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/NotEmailPasswordPolicyProvider.java new file mode 100644 index 0000000000..9080e7364a --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/policy/NotEmailPasswordPolicyProvider.java @@ -0,0 +1,60 @@ +/* + * Copyright 2020 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.policy; + +import org.keycloak.models.KeycloakContext; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; + +/** + * @author Thomas Darimont + */ +public class NotEmailPasswordPolicyProvider implements PasswordPolicyProvider { + + private static final String ERROR_MESSAGE = "invalidPasswordNotEmailMessage"; + + private KeycloakContext context; + + public NotEmailPasswordPolicyProvider(KeycloakContext context) { + this.context = context; + } + + @Override + public PolicyError validate(String email, String password) { + if (email == null) { + return null; + } + return email.equals(password) ? new PolicyError(ERROR_MESSAGE) : null; + } + + @Override + public PolicyError validate(RealmModel realm, UserModel user, String password) { + return validate(user.getEmail(), password); + } + + @Override + public Object parseConfig(String value) { + return null; + } + + @Override + public void close() { + // NOOP + } + +} diff --git a/server-spi-private/src/main/java/org/keycloak/policy/NotEmailPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/NotEmailPasswordPolicyProviderFactory.java new file mode 100644 index 0000000000..125b8cb5ed --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/policy/NotEmailPasswordPolicyProviderFactory.java @@ -0,0 +1,73 @@ +/* + * Copyright 2020 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.policy; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; + +/** + * @author Thomas Darimont + */ +public class NotEmailPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory { + + public static final String ID = "notEmail"; + + @Override + public String getId() { + return ID; + } + + @Override + public PasswordPolicyProvider create(KeycloakSession session) { + return new NotEmailPasswordPolicyProvider(session.getContext()); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public String getDisplayName() { + return "Not Email"; + } + + @Override + public String getConfigType() { + return null; + } + + @Override + public String getDefaultConfigValue() { + return null; + } + + @Override + public boolean isMultiplSupported() { + return false; + } + + @Override + public void close() { + } + +} diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory index ac72ac5693..7e887ecdf1 100644 --- a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory +++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory @@ -27,3 +27,4 @@ org.keycloak.policy.RegexPatternsPasswordPolicyProviderFactory org.keycloak.policy.SpecialCharsPasswordPolicyProviderFactory org.keycloak.policy.UpperCasePasswordPolicyProviderFactory org.keycloak.policy.BlacklistPasswordPolicyProviderFactory +org.keycloak.policy.NotEmailPasswordPolicyProviderFactory diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java index 2b08f03dae..36ea003999 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java @@ -395,6 +395,7 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest { events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent(); } + // KEYCLOAK-12729 @Test public void changePasswordWithNotUsernamePolicy() { setPasswordPolicy("notUsername(1)"); @@ -413,6 +414,23 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest { events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent(); } + @Test + public void changePasswordWithNotEmailPolicy() { + setPasswordPolicy("notEmail(1)"); + + changePasswordPage.open(); + loginPage.login("test-user@localhost", "password"); + events.expectLogin().client("account").detail(Details.REDIRECT_URI, getAccountRedirectUrl() + "?path=password").assertEvent(); + + changePasswordPage.changePassword("password", "test-user@localhost", "test-user@localhost"); + Assert.assertEquals("Invalid password: must not be equal to the email.", profilePage.getError()); + events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent(); + + changePasswordPage.changePassword("password", "newPassword", "newPassword"); + Assert.assertEquals("Your password has been updated.", profilePage.getSuccess()); + events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent(); + } + @Test public void changePasswordWithRegexPatternsPolicy() { setPasswordPolicy("regexPattern(^[A-Z]+#[a-z]{8}$)"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java index 79a30231cf..2793c93437 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java @@ -491,6 +491,28 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest { assertEquals("Please specify username.", registerPage.getError()); } + // KEYCLOAK-12729 + @Test + public void registerUserNotEmailPasswordPolicy() { + + RealmRepresentation notEmailRealm = RealmBuilder.create().passwordPolicy("notEmail").build(); + notEmailRealm.setRegistrationEmailAsUsername(true); + + adminClient.realm("test").update(notEmailRealm); + + loginPage.open(); + + assertTrue(loginPage.isCurrent()); + + loginPage.clickRegister(); + registerPage.assertCurrent(); + + registerPage.registerWithEmailAsUsername("firstName", "lastName", "registerUserNotEmail@email", "registerUserNotEmail@email", "registerUserNotEmail@email"); + + assertTrue(registerPage.isCurrent()); + assertEquals("Invalid password: must not be equal to the email.", registerPage.getError()); + } + protected UserRepresentation getUser(String userId) { return testRealm().users().get(userId).toRepresentation(); } diff --git a/themes/src/main/resources-community/theme/base/account/messages/messages_de.properties b/themes/src/main/resources-community/theme/base/account/messages/messages_de.properties index 0c8b087d45..a1c57e7541 100644 --- a/themes/src/main/resources-community/theme/base/account/messages/messages_de.properties +++ b/themes/src/main/resources-community/theme/base/account/messages/messages_de.properties @@ -163,6 +163,7 @@ invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort: Es muss mindestens {0} invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort: Es muss mindestens {0} Gro\u00DFbuchstaben beinhalten. invalidPasswordMinSpecialCharsMessage=Ung\u00FCltiges Passwort: Es muss mindestens {0} Sonderzeichen beinhalten. invalidPasswordNotUsernameMessage=Ung\u00FCltiges Passwort: Es darf nicht gleich sein wie der Benutzername. +invalidPasswordNotEmailMessage=Ung\u00FCltiges Passwort: darf nicht identisch mit der E-Mail-Adresse sein. invalidPasswordRegexPatternMessage=Ung\u00FCltiges Passwort: Es entspricht nicht dem Regex-Muster. invalidPasswordHistoryMessage=Ung\u00FCltiges Passwort: Es darf nicht einem der letzten {0} Passw\u00F6rter entsprechen. invalidPasswordBlacklistedMessage=Ung\u00FCltiges Passwort: Das Passwort steht auf der Blocklist (schwarzen Liste). diff --git a/themes/src/main/resources-community/theme/base/admin/messages/messages_de.properties b/themes/src/main/resources-community/theme/base/admin/messages/messages_de.properties index 26a5a1e252..3902300eae 100644 --- a/themes/src/main/resources-community/theme/base/admin/messages/messages_de.properties +++ b/themes/src/main/resources-community/theme/base/admin/messages/messages_de.properties @@ -4,6 +4,7 @@ invalidPasswordMinDigitsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Zi invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Gro\u00DFbuchstaben beinhalten. invalidPasswordMinSpecialCharsMessage=Ung\u00FCltiges Passwort: muss mindestens {0} Sonderzeichen beinhalten. invalidPasswordNotUsernameMessage=Ung\u00FCltiges Passwort: darf nicht identisch mit dem Benutzernamen sein. +invalidPasswordNotEmailMessage=Ung\u00FCltiges Passwort: darf nicht identisch mit der E-Mail-Adresse sein. invalidPasswordRegexPatternMessage=Ung\u00FCltiges Passwort: stimmt nicht mit Regex-Muster \u00FCberein. invalidPasswordHistoryMessage=Ung\u00FCltiges Passwort: darf nicht identisch mit einem der letzten {0} Passw\u00F6rter sein. invalidPasswordBlacklistedMessage=Ung\u00FCltiges Passwort: Passwort ist zu bekannt und auf der schwarzen Liste. diff --git a/themes/src/main/resources-community/theme/base/login/messages/messages_de.properties b/themes/src/main/resources-community/theme/base/login/messages/messages_de.properties index 288f9db01c..fd1c08e5d9 100755 --- a/themes/src/main/resources-community/theme/base/login/messages/messages_de.properties +++ b/themes/src/main/resources-community/theme/base/login/messages/messages_de.properties @@ -193,6 +193,7 @@ invalidPasswordMinLowerCaseCharsMessage=Ung\u00FCltiges Passwort\: Es muss minde invalidPasswordMinUpperCaseCharsMessage=Ung\u00FCltiges Passwort: Es muss mindestens {0} Gro\u00DFbuchstaben beinhalten. invalidPasswordMinSpecialCharsMessage=Ung\u00FCltiges Passwort: Es muss mindestens {0} Sonderzeichen beinhalten. invalidPasswordNotUsernameMessage=Ung\u00FCltiges Passwort: Es darf nicht gleich sein wie der Benutzername. +invalidPasswordNotEmailMessage=Ung\u00FCltiges Passwort: darf nicht identisch mit der E-Mail-Adresse sein. invalidPasswordRegexPatternMessage=Ung\u00FCltiges Passwort: Es entspricht nicht dem Regex-Muster. invalidPasswordHistoryMessage=Ung\u00FCltiges Passwort: Es darf nicht einem der letzten {0} Passw\u00F6rter entsprechen. invalidPasswordGenericMessage=Ung\u00FCltiges Passwort: Es verletzt die Passwort-Richtlinien. diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties index 7a95787f65..60b9520fca 100755 --- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties @@ -200,6 +200,7 @@ invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} nume invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters. invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters. invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username. +invalidPasswordNotEmailMessage=Invalid password: must not be equal to the email. invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s). invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords. invalidPasswordBlacklistedMessage=Invalid password: password is blacklisted. diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties index 6a870247dd..1a416a40d3 100644 --- a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties @@ -4,6 +4,7 @@ invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} nume invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters. invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters. invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username. +invalidPasswordNotEmailMessage=Invalid password: must not be equal to the email. invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s). invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords. invalidPasswordBlacklistedMessage=Invalid password: password is blacklisted. diff --git a/themes/src/main/resources/theme/base/login/messages/messages_en.properties b/themes/src/main/resources/theme/base/login/messages/messages_en.properties index ab8f775259..ffbd14c8ae 100755 --- a/themes/src/main/resources/theme/base/login/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/login/messages/messages_en.properties @@ -228,6 +228,7 @@ invalidPasswordMinLowerCaseCharsMessage=Invalid password: must contain at least invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters. invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters. invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username. +invalidPasswordNotEmailMessage=Invalid password: must not be equal to the email. invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s). invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords. invalidPasswordGenericMessage=Invalid password: new password doesn''t match password policies.