diff --git a/docs/documentation/upgrading/topics/keycloak/changes-22_0_2.adoc b/docs/documentation/upgrading/topics/keycloak/changes-22_0_2.adoc index f162e847a3..827997d702 100644 --- a/docs/documentation/upgrading/topics/keycloak/changes-22_0_2.adoc +++ b/docs/documentation/upgrading/topics/keycloak/changes-22_0_2.adoc @@ -10,4 +10,10 @@ The old LinkedIn way based on OAuth seems to be completely removed from the link ``` kc.[sh|bat] start --features linkedin-oauth ... +``` + += A new parameter --spi-user-profile-declarative-user-profile-max-email-local-part-length is added to set max email local part length taking backwards compatibility into consideration. The default value is 64. + +``` +kc.[sh|bat] start --spi-user-profile-declarative-user-profile-max-email-local-part-length=100 ... ``` \ No newline at end of file diff --git a/server-spi-private/src/main/java/org/keycloak/utils/EmailValidationUtil.java b/server-spi-private/src/main/java/org/keycloak/utils/EmailValidationUtil.java index c77c6416c7..c868b8ac3c 100644 --- a/server-spi-private/src/main/java/org/keycloak/utils/EmailValidationUtil.java +++ b/server-spi-private/src/main/java/org/keycloak/utils/EmailValidationUtil.java @@ -3,12 +3,11 @@ package org.keycloak.utils; import java.net.IDN; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.keycloak.Config; import static java.util.regex.Pattern.CASE_INSENSITIVE; public class EmailValidationUtil { - private static final int MAX_LOCAL_PART_LENGTH = 64; - private static final String LOCAL_PART_ATOM = "[a-z0-9!#$%&'*+/=?^_`{|}~\u0080-\uFFFF-]"; private static final String LOCAL_PART_INSIDE_QUOTES_ATOM = "(?:[a-z0-9!#$%&'*.(),<>\\[\\]:; @+/=?^_`{|}~\u0080-\uFFFF-]|\\\\\\\\|\\\\\\\")"; /** @@ -29,6 +28,8 @@ public class EmailValidationUtil { */ private static final Pattern EMAIL_DOMAIN_PATTERN = Pattern.compile(DOMAIN + "|\\[" + IP_DOMAIN + "\\]|" + "\\[IPv6:" + IP_V6_DOMAIN + "\\]", CASE_INSENSITIVE); + public static final String MAX_EMAIL_LOCAL_PART_LENGTH = "max-email-local-part-length"; + public static boolean isValidEmail(String value) { if ( value == null || value.length() == 0 ) { @@ -56,7 +57,8 @@ public class EmailValidationUtil { } private static boolean isValidEmailLocalPart(String localPart) { - if ( localPart.length() > MAX_LOCAL_PART_LENGTH ) { + + if ( localPart.length() > Config.scope("user-profile-declarative-user-profile").getInt(MAX_EMAIL_LOCAL_PART_LENGTH,64) ) { return false; } Matcher matcher = LOCAL_PART_PATTERN.matcher( localPart ); diff --git a/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java b/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java index 7d5dad5c34..11540446f7 100644 --- a/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java +++ b/services/src/main/java/org/keycloak/userprofile/AbstractUserProfileProvider.java @@ -71,6 +71,7 @@ public abstract class AbstractUserProfileProvider public static final String CONFIG_ADMIN_READ_ONLY_ATTRIBUTES = "admin-read-only-attributes"; public static final String CONFIG_READ_ONLY_ATTRIBUTES = "read-only-attributes"; + public static final String MAX_EMAIL_LOCAL_PART_LENGTH = "max-email-local-part-length"; private static boolean editUsernameCondition(AttributeContext c) { KeycloakSession session = c.getSession(); @@ -441,6 +442,12 @@ public abstract class AbstractUserProfileProvider .helpText("Array of regular expressions to identify fields that should be treated read-only so administrators can't change them.") .add() + .property() + .name(MAX_EMAIL_LOCAL_PART_LENGTH) + .type(ProviderConfigProperty.STRING_TYPE) + .helpText("To set user profile max email local part length") + .add() + .build(); } diff --git a/services/src/test/java/org/keycloak/test/ValidationTest.java b/services/src/test/java/org/keycloak/test/ValidationTest.java index 4b2ed0d2e0..63fa3f692a 100644 --- a/services/src/test/java/org/keycloak/test/ValidationTest.java +++ b/services/src/test/java/org/keycloak/test/ValidationTest.java @@ -41,6 +41,8 @@ public class ValidationTest { Assert.assertFalse(Validation.isEmailValid("abc@foo.")); Assert.assertFalse(Validation.isEmailValid("abc@foo..bar")); Assert.assertTrue(Validation.isEmailValid("diegø@foo.com")); + Assert.assertTrue(Validation.isEmailValid("qwertyuiopasdfghjklz@foo.com")); + Assert.assertFalse(Validation.isEmailValid("qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyqwertyu@foo.com")); } @Test diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/keycloak.conf b/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/keycloak.conf index 193faf05a0..596b3efbf6 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/keycloak.conf +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/src/main/content/conf/keycloak.conf @@ -32,6 +32,7 @@ spi-truststore-file-password=secret spi-user-profile-provider=declarative-user-profile spi-user-profile-declarative-user-profile-read-only-attributes=deniedFoo,deniedBar*,deniedSome/thing,deniedsome*thing spi-user-profile-declarative-user-profile-admin-read-only-attributes=deniedSomeAdmin +spi-user-profile-declarative-user-profile-max-email-local-part-length=100 #password-blacklists path spi-password-policy-password-blacklist-blacklists-path=${kc.home.dir:}/dependency/password-blacklists