Password policy for not having username in the password
closes #27643 Signed-off-by: Gilvan Filho <gfilho@redhat.com>
This commit is contained in:
parent
b9a7152a29
commit
757c524cc5
11 changed files with 169 additions and 0 deletions
|
@ -19,6 +19,10 @@ The following methods for setting custom cookies have been removed:
|
||||||
* `HttpCookie` - replaced by `NewCookie.Builder`
|
* `HttpCookie` - replaced by `NewCookie.Builder`
|
||||||
* `HttpResponse.setCookieIfAbsent(HttpCookie cookie)` - replaced by `HttpResponse.setCookieIfAbsent(NewCookie cookie)`
|
* `HttpResponse.setCookieIfAbsent(HttpCookie cookie)` - replaced by `HttpResponse.setCookieIfAbsent(NewCookie cookie)`
|
||||||
|
|
||||||
|
= Password policy for check if password contains Username
|
||||||
|
|
||||||
|
Keycloak supports a new password policy that allows you to deny user passwords which contains the user username.
|
||||||
|
|
||||||
= Searching by user attribute no longer case insensitive
|
= Searching by user attribute no longer case insensitive
|
||||||
|
|
||||||
When searching for users by user attribute, {project_name} no longer searches for user attribute names forcing lower case comparisons. The goal of this change was to speed up searches by using {project_name}'s native index on the user attribute table. If your database collation is case-insensitive, your search results will stay the same. If your database collation is case-sensitive, you might see less search results than before.
|
When searching for users by user attribute, {project_name} no longer searches for user attribute names forcing lower case comparisons. The goal of this change was to speed up searches by using {project_name}'s native index on the user attribute table. If your database collation is case-insensitive, your search results will stay the same. If your database collation is case-sensitive, you might see less search results than before.
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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;
|
||||||
|
|
||||||
|
public class NotContainsUsernamePasswordPolicyProvider implements PasswordPolicyProvider {
|
||||||
|
|
||||||
|
private static final String ERROR_MESSAGE = "invalidPasswordNotContainsUsernameMessage";
|
||||||
|
|
||||||
|
private KeycloakContext context;
|
||||||
|
|
||||||
|
public NotContainsUsernamePasswordPolicyProvider(KeycloakContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PolicyError validate(String username, String password) {
|
||||||
|
if (username == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return username.contains(password) ? new PolicyError(ERROR_MESSAGE) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PolicyError validate(RealmModel realm, UserModel user, String password) {
|
||||||
|
return validate(user.getUsername(), password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object parseConfig(String value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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;
|
||||||
|
|
||||||
|
public class NotContainsUsernamePasswordPolicyProviderFactory implements PasswordPolicyProviderFactory {
|
||||||
|
|
||||||
|
public static final String ID = "notContainsUsername";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PasswordPolicyProvider create(KeycloakSession session) {
|
||||||
|
return new NotContainsUsernamePasswordPolicyProvider(session.getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayName() {
|
||||||
|
return "Not Contains Username";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConfigType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultConfigValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMultiplSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ org.keycloak.policy.LengthPasswordPolicyProviderFactory
|
||||||
org.keycloak.policy.LowerCasePasswordPolicyProviderFactory
|
org.keycloak.policy.LowerCasePasswordPolicyProviderFactory
|
||||||
org.keycloak.policy.MaximumLengthPasswordPolicyProviderFactory
|
org.keycloak.policy.MaximumLengthPasswordPolicyProviderFactory
|
||||||
org.keycloak.policy.NotUsernamePasswordPolicyProviderFactory
|
org.keycloak.policy.NotUsernamePasswordPolicyProviderFactory
|
||||||
|
org.keycloak.policy.NotContainsUsernamePasswordPolicyProviderFactory
|
||||||
org.keycloak.policy.RegexPatternsPasswordPolicyProviderFactory
|
org.keycloak.policy.RegexPatternsPasswordPolicyProviderFactory
|
||||||
org.keycloak.policy.SpecialCharsPasswordPolicyProviderFactory
|
org.keycloak.policy.SpecialCharsPasswordPolicyProviderFactory
|
||||||
org.keycloak.policy.UpperCasePasswordPolicyProviderFactory
|
org.keycloak.policy.UpperCasePasswordPolicyProviderFactory
|
||||||
|
|
|
@ -597,6 +597,38 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KEYCLOAK-27643
|
||||||
|
@Test
|
||||||
|
public void registerUserNotContainsUsernamePasswordPolicy() throws IOException {
|
||||||
|
try (RealmAttributeUpdater rau = getRealmAttributeUpdater().setPasswordPolicy("notContainsUsername").update()) {
|
||||||
|
loginPage.open();
|
||||||
|
|
||||||
|
assertTrue(loginPage.isCurrent());
|
||||||
|
|
||||||
|
loginPage.clickRegister();
|
||||||
|
registerPage.assertCurrent();
|
||||||
|
|
||||||
|
registerPage.register("firstName", "lastName", "registerUserNotContainsUsername@email", "registerUserNotContainsUsername", "registerUserNotContainsUsername", "registerUserNotContainsUsername");
|
||||||
|
|
||||||
|
assertTrue(registerPage.isCurrent());
|
||||||
|
assertEquals("Invalid password: Can not contains the username.", registerPage.getInputPasswordErrors().getPasswordError());
|
||||||
|
|
||||||
|
try (Response response = adminClient.realm("test").users().create(UserBuilder.create().username("registerUserNotContainsUsername").build())) {
|
||||||
|
assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPage.register("firstName", "lastName", "registerUserNotContainsUsername@email", "registerUserNotContainsUsername", "registerUserNotContainsUsername", "registerUserNotContainsUsername");
|
||||||
|
|
||||||
|
assertTrue(registerPage.isCurrent());
|
||||||
|
assertEquals("Username already exists.", registerPage.getInputAccountErrors().getUsernameError());
|
||||||
|
|
||||||
|
registerPage.register("firstName", "lastName", "registerUserNotContainsUsername@email", null, "password", "password");
|
||||||
|
|
||||||
|
assertTrue(registerPage.isCurrent());
|
||||||
|
assertEquals("Please specify username.", registerPage.getInputAccountErrors().getUsernameError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// KEYCLOAK-12729
|
// KEYCLOAK-12729
|
||||||
@Test
|
@Test
|
||||||
public void registerUserNotEmailPasswordPolicy() throws IOException {
|
public void registerUserNotEmailPasswordPolicy() throws IOException {
|
||||||
|
|
|
@ -214,6 +214,7 @@ invalidPasswordMinDigitsMessage=Senha inválida\: deve conter pelo menos {0} nú
|
||||||
invalidPasswordMinUpperCaseCharsMessage=Senha inválida\: deve conter pelo menos {0} letra(s) maiúscula(s).
|
invalidPasswordMinUpperCaseCharsMessage=Senha inválida\: deve conter pelo menos {0} letra(s) maiúscula(s).
|
||||||
invalidPasswordMinSpecialCharsMessage=Senha inválida\: deve conter pelo menos {0} caractere(s) especial(is).
|
invalidPasswordMinSpecialCharsMessage=Senha inválida\: deve conter pelo menos {0} caractere(s) especial(is).
|
||||||
invalidPasswordNotUsernameMessage=Senha inválida\: não pode ser igual ao nome de usuário.
|
invalidPasswordNotUsernameMessage=Senha inválida\: não pode ser igual ao nome de usuário.
|
||||||
|
invalidPasswordNotContainsUsernameMessage=Senha inválida\: não pode conter o nome de usuário.
|
||||||
invalidPasswordNotEmailMessage=Senha inválida: não pode ser igual ao endereço de e-mail.
|
invalidPasswordNotEmailMessage=Senha inválida: não pode ser igual ao endereço de e-mail.
|
||||||
invalidPasswordRegexPatternMessage=Senha inválida\: não corresponde ao(s) padrão(ões) da expressão regular.
|
invalidPasswordRegexPatternMessage=Senha inválida\: não corresponde ao(s) padrão(ões) da expressão regular.
|
||||||
invalidPasswordHistoryMessage=Senha inválida\: não pode ser igual a qualquer uma da(s) última(s) {0} senha(s).
|
invalidPasswordHistoryMessage=Senha inválida\: não pode ser igual a qualquer uma da(s) última(s) {0} senha(s).
|
||||||
|
|
|
@ -4,6 +4,7 @@ invalidPasswordMinDigitsMessage=Senha inválida: deve conter ao menos {0} digito
|
||||||
invalidPasswordMinUpperCaseCharsMessage=Senha inválida: deve conter ao menos {0} caracteres maiúsculos.
|
invalidPasswordMinUpperCaseCharsMessage=Senha inválida: deve conter ao menos {0} caracteres maiúsculos.
|
||||||
invalidPasswordMinSpecialCharsMessage=Senha inválida: deve conter ao menos {0} caracteres especiais.
|
invalidPasswordMinSpecialCharsMessage=Senha inválida: deve conter ao menos {0} caracteres especiais.
|
||||||
invalidPasswordNotUsernameMessage=Senha inválida: não deve ser igual ao nome de usuário.
|
invalidPasswordNotUsernameMessage=Senha inválida: não deve ser igual ao nome de usuário.
|
||||||
|
invalidPasswordNotContainsUsernameMessage=Senha inválida\: não pode conter o nome de usuário.
|
||||||
invalidPasswordRegexPatternMessage=Senha inválida: falha ao passar por padrões.
|
invalidPasswordRegexPatternMessage=Senha inválida: falha ao passar por padrões.
|
||||||
invalidPasswordHistoryMessage=Senha inválida: não deve ser igual às últimas {0} senhas.
|
invalidPasswordHistoryMessage=Senha inválida: não deve ser igual às últimas {0} senhas.
|
||||||
|
|
||||||
|
|
|
@ -241,6 +241,7 @@ invalidPasswordMinLowerCaseCharsMessage=Senha inválida\: deve conter pelo menos
|
||||||
invalidPasswordMinUpperCaseCharsMessage=Senha inválida\: deve conter pelo menos {0} letra(s) maiúscula(s).
|
invalidPasswordMinUpperCaseCharsMessage=Senha inválida\: deve conter pelo menos {0} letra(s) maiúscula(s).
|
||||||
invalidPasswordMinSpecialCharsMessage=Senha inválida\: deve conter pelo menos {0} caractere(s) especial(is).
|
invalidPasswordMinSpecialCharsMessage=Senha inválida\: deve conter pelo menos {0} caractere(s) especial(is).
|
||||||
invalidPasswordNotUsernameMessage=Senha inválida\: não pode ser igual ao nome de usuário
|
invalidPasswordNotUsernameMessage=Senha inválida\: não pode ser igual ao nome de usuário
|
||||||
|
invalidPasswordNotContainsUsernameMessage=Senha inválida\: não pode conter o nome de usuário.
|
||||||
invalidPasswordNotEmailMessage=Senha inválida: não pode ser igual ao endereço de e-mail.
|
invalidPasswordNotEmailMessage=Senha inválida: não pode ser igual ao endereço de e-mail.
|
||||||
invalidPasswordRegexPatternMessage=Senha inválida\: não corresponde ao(s) padrão(ões) de expressão regular.
|
invalidPasswordRegexPatternMessage=Senha inválida\: não corresponde ao(s) padrão(ões) de expressão regular.
|
||||||
invalidPasswordHistoryMessage=Senha inválida\: não pode ser igual a qualquer uma da(s) última(s) {0} senha(s).
|
invalidPasswordHistoryMessage=Senha inválida\: não pode ser igual a qualquer uma da(s) última(s) {0} senha(s).
|
||||||
|
|
|
@ -230,6 +230,7 @@ invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} nume
|
||||||
invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
|
invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
|
||||||
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
|
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
|
||||||
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
|
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
|
||||||
|
invalidPasswordNotContainsUsernameMessage=Invalid password: Can not contains the username.
|
||||||
invalidPasswordNotEmailMessage=Invalid password: must not be equal to the email.
|
invalidPasswordNotEmailMessage=Invalid password: must not be equal to the email.
|
||||||
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
|
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
|
||||||
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
|
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
|
||||||
|
|
|
@ -5,6 +5,7 @@ invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} nume
|
||||||
invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
|
invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
|
||||||
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
|
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
|
||||||
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
|
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
|
||||||
|
invalidPasswordNotContainsUsernameMessage=Invalid password: Can not contains the username.
|
||||||
invalidPasswordNotEmailMessage=Invalid password: must not be equal to the email.
|
invalidPasswordNotEmailMessage=Invalid password: must not be equal to the email.
|
||||||
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
|
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
|
||||||
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
|
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
|
||||||
|
|
|
@ -307,6 +307,7 @@ invalidPasswordMinLowerCaseCharsMessage=Invalid password: must contain at least
|
||||||
invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
|
invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
|
||||||
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
|
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
|
||||||
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
|
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
|
||||||
|
invalidPasswordNotContainsUsernameMessage=Invalid password: Can not contains the username.
|
||||||
invalidPasswordNotEmailMessage=Invalid password: must not be equal to the email.
|
invalidPasswordNotEmailMessage=Invalid password: must not be equal to the email.
|
||||||
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
|
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
|
||||||
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
|
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
|
||||||
|
|
Loading…
Reference in a new issue