Allow managing the required settigs for the email attribute
Closes #15026
This commit is contained in:
parent
782d145cef
commit
857b02be63
15 changed files with 190 additions and 48 deletions
|
@ -20,7 +20,6 @@
|
|||
package org.keycloak.userprofile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -47,7 +46,7 @@ public final class AttributeMetadata {
|
|||
private final Predicate<AttributeContext> selector;
|
||||
private final List<Predicate<AttributeContext>> writeAllowed = new ArrayList<>();
|
||||
/** Predicate to decide if attribute is required, it is handled as required if predicate is null */
|
||||
private final Predicate<AttributeContext> required;
|
||||
private Predicate<AttributeContext> required;
|
||||
private final List<Predicate<AttributeContext>> readAllowed = new ArrayList<>();
|
||||
private List<AttributeValidatorMetadata> validators;
|
||||
private Map<String, Object> annotations;
|
||||
|
@ -170,7 +169,7 @@ public final class AttributeMetadata {
|
|||
return validators;
|
||||
}
|
||||
|
||||
public AttributeMetadata addValidator(List<AttributeValidatorMetadata> validators) {
|
||||
public AttributeMetadata addValidators(List<AttributeValidatorMetadata> validators) {
|
||||
if (this.validators == null) {
|
||||
this.validators = new ArrayList<>();
|
||||
}
|
||||
|
@ -202,7 +201,7 @@ public final class AttributeMetadata {
|
|||
// we clone validators list to allow adding or removing validators. Validators
|
||||
// itself are not cloned as we do not expect them to be reconfigured.
|
||||
if (validators != null) {
|
||||
cloned.addValidator(validators);
|
||||
cloned.addValidators(validators);
|
||||
}
|
||||
//we clone annotations map to allow adding to or removing from it
|
||||
if(annotations != null) {
|
||||
|
@ -247,4 +246,14 @@ public final class AttributeMetadata {
|
|||
public int hashCode() {
|
||||
return attributeName.hashCode();
|
||||
}
|
||||
|
||||
public AttributeMetadata setRequired(Predicate<AttributeContext> required) {
|
||||
this.required = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AttributeMetadata setValidators(List<AttributeValidatorMetadata> validators) {
|
||||
this.validators = validators;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,19 +61,23 @@ public final class UserProfileMetadata implements Cloneable {
|
|||
}
|
||||
|
||||
public AttributeMetadata addAttribute(String name, int guiOrder, Predicate<AttributeContext> writeAllowed, Predicate<AttributeContext> readAllowed, AttributeValidatorMetadata... validator) {
|
||||
return addAttribute(new AttributeMetadata(name, guiOrder, ALWAYS_TRUE, writeAllowed, ALWAYS_TRUE, readAllowed).addValidator(Arrays.asList(validator)));
|
||||
return addAttribute(new AttributeMetadata(name, guiOrder, ALWAYS_TRUE, writeAllowed, ALWAYS_TRUE, readAllowed).addValidators(Arrays.asList(validator)));
|
||||
}
|
||||
|
||||
public AttributeMetadata addAttribute(String name, int guiOrder, Predicate<AttributeContext> writeAllowed, List<AttributeValidatorMetadata> validators) {
|
||||
return addAttribute(new AttributeMetadata(name, guiOrder, ALWAYS_TRUE, writeAllowed, ALWAYS_TRUE, ALWAYS_TRUE).addValidator(validators));
|
||||
return addAttribute(new AttributeMetadata(name, guiOrder, ALWAYS_TRUE, writeAllowed, ALWAYS_TRUE, ALWAYS_TRUE).addValidators(validators));
|
||||
}
|
||||
|
||||
public AttributeMetadata addAttribute(String name, int guiOrder, Predicate<AttributeContext> writeAllowed, Predicate<AttributeContext> required, List<AttributeValidatorMetadata> validators) {
|
||||
return addAttribute(new AttributeMetadata(name, guiOrder, ALWAYS_TRUE, writeAllowed, required, ALWAYS_TRUE).addValidators(validators));
|
||||
}
|
||||
|
||||
public AttributeMetadata addAttribute(String name, int guiOrder, List<AttributeValidatorMetadata> validators) {
|
||||
return addAttribute(new AttributeMetadata(name, guiOrder).addValidator(validators));
|
||||
return addAttribute(new AttributeMetadata(name, guiOrder).addValidators(validators));
|
||||
}
|
||||
|
||||
public AttributeMetadata addAttribute(String name, int guiOrder, List<AttributeValidatorMetadata> validator, Predicate<AttributeContext> selector, Predicate<AttributeContext> writeAllowed, Predicate<AttributeContext> required, Predicate<AttributeContext> readAllowed) {
|
||||
return addAttribute(new AttributeMetadata(name, guiOrder, selector, writeAllowed, required, readAllowed).addValidator(validator));
|
||||
return addAttribute(new AttributeMetadata(name, guiOrder, selector, writeAllowed, required, readAllowed).addValidators(validator));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -298,7 +298,7 @@ public abstract class AbstractUserProfileProvider<U extends UserProfileProvider>
|
|||
private UserProfileMetadata createRegistrationUserCreationProfile() {
|
||||
UserProfileMetadata metadata = new UserProfileMetadata(REGISTRATION_USER_CREATION);
|
||||
|
||||
metadata.addAttribute(UserModel.USERNAME, -2, new AttributeValidatorMetadata(RegistrationEmailAsUsernameUsernameValueValidator.ID), new AttributeValidatorMetadata(RegistrationUsernameExistsValidator.ID));
|
||||
metadata.addAttribute(UserModel.USERNAME, -2, new AttributeValidatorMetadata(RegistrationEmailAsUsernameUsernameValueValidator.ID), new AttributeValidatorMetadata(RegistrationUsernameExistsValidator.ID), new AttributeValidatorMetadata(UsernameHasValueValidator.ID));
|
||||
|
||||
metadata.addAttribute(UserModel.EMAIL, -1, new AttributeValidatorMetadata(RegistrationEmailAsUsernameEmailValueValidator.ID));
|
||||
|
||||
|
@ -366,6 +366,11 @@ public abstract class AbstractUserProfileProvider<U extends UserProfileProvider>
|
|||
private UserProfileMetadata createUserResourceValidation(Config.Scope config) {
|
||||
Pattern p = getRegexPatternString(config.getArray("admin-read-only-attributes"));
|
||||
UserProfileMetadata metadata = new UserProfileMetadata(USER_API);
|
||||
|
||||
|
||||
metadata.addAttribute(UserModel.USERNAME, -2, new AttributeValidatorMetadata(UsernameHasValueValidator.ID));
|
||||
metadata.addAttribute(UserModel.EMAIL, -1, new AttributeValidatorMetadata(EmailValidator.ID, ValidatorConfig.builder().config(EmailValidator.IGNORE_EMPTY_VALUE, true).build()));
|
||||
|
||||
List<AttributeValidatorMetadata> readonlyValidators = new ArrayList<>();
|
||||
|
||||
if (p != null) {
|
||||
|
|
|
@ -61,7 +61,6 @@ import org.keycloak.userprofile.validator.BlankAttributeValidator;
|
|||
import org.keycloak.userprofile.validator.ImmutableAttributeValidator;
|
||||
import org.keycloak.validate.AbstractSimpleValidator;
|
||||
import org.keycloak.validate.ValidatorConfig;
|
||||
import org.keycloak.validate.validators.EmailValidator;
|
||||
|
||||
/**
|
||||
* {@link UserProfileProvider} loading configuration from the changeable JSON file stored in component config. Parsed
|
||||
|
@ -295,7 +294,7 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
}
|
||||
|
||||
Predicate<AttributeContext> required = AttributeMetadata.ALWAYS_FALSE;
|
||||
if (rc != null && !isUsernameOrEmailAttribute(attributeName)) {
|
||||
if (rc != null) {
|
||||
if (rc.isAlways() || UPConfigUtils.isRoleForContext(context, rc.getRoles())) {
|
||||
required = AttributeMetadata.ALWAYS_TRUE;
|
||||
} else if (UPConfigUtils.canBeAuthFlowContext(context) && rc.getScopes() != null && !rc.getScopes().isEmpty()) {
|
||||
|
@ -327,7 +326,7 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
|
||||
Predicate<AttributeContext> selector = AttributeMetadata.ALWAYS_TRUE;
|
||||
UPAttributeSelector sc = attrConfig.getSelector();
|
||||
if (sc != null && !isUsernameOrEmailAttribute(attributeName) && UPConfigUtils.canBeAuthFlowContext(context) && sc.getScopes() != null && !sc.getScopes().isEmpty()) {
|
||||
if (sc != null && !isBuiltInAttribute(attributeName) && UPConfigUtils.canBeAuthFlowContext(context) && sc.getScopes() != null && !sc.getScopes().isEmpty()) {
|
||||
// for contexts executed from auth flow and with configured scopes selector
|
||||
// we have to create correct predicate
|
||||
selector = (c) -> requestedScopePredicate(c, sc.getScopes());
|
||||
|
@ -337,48 +336,46 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
String attributeGroup = attrConfig.getGroup();
|
||||
AttributeGroupMetadata groupMetadata = toAttributeGroupMeta(groupsByName.get(attributeGroup));
|
||||
|
||||
if (isUsernameOrEmailAttribute(attributeName)) {
|
||||
guiOrder++;
|
||||
|
||||
if (isBuiltInAttribute(attributeName)) {
|
||||
// make sure username and email are writable if permissions are not set
|
||||
if (permissions == null || permissions.isEmpty()) {
|
||||
writeAllowed = AttributeMetadata.ALWAYS_TRUE;
|
||||
readAllowed = AttributeMetadata.ALWAYS_TRUE;
|
||||
}
|
||||
|
||||
List<AttributeMetadata> atts = decoratedMetadata.getAttribute(attributeName);
|
||||
if (UserModel.USERNAME.equals(attributeName)) {
|
||||
required = AttributeMetadata.ALWAYS_TRUE;
|
||||
}
|
||||
|
||||
// Add ImmutableAttributeValidator to ensure that attributes that are configured
|
||||
// as read-only are marked as such.
|
||||
// Skip this for username in realms with username = email to allow change of email
|
||||
// address on initial login with profile via idp
|
||||
if (!realm.isRegistrationEmailAsUsername() || !UserModel.USERNAME.equals(attributeName)) {
|
||||
if (!realm.isRegistrationEmailAsUsername() && UserModel.EMAIL.equals(attributeName)) {
|
||||
validators.add(new AttributeValidatorMetadata(ImmutableAttributeValidator.ID));
|
||||
}
|
||||
|
||||
if (atts.isEmpty()) {
|
||||
// attribute metadata doesn't exist so we have to add it. We keep it optional as Abstract base
|
||||
// doesn't require it.
|
||||
decoratedMetadata.addAttribute(attributeName, guiOrder++, writeAllowed, validators)
|
||||
.addAnnotations(annotations)
|
||||
.setAttributeDisplayName(attrConfig.getDisplayName())
|
||||
.setAttributeGroupMetadata(groupMetadata);
|
||||
} else {
|
||||
final int localGuiOrder = guiOrder++;
|
||||
Predicate<AttributeContext> readAllowedFinal = readAllowed;
|
||||
Predicate<AttributeContext> writeAllowedFinal = writeAllowed;
|
||||
List<AttributeMetadata> existingMetadata = decoratedMetadata.getAttribute(attributeName);
|
||||
|
||||
// add configured validators and annotations to existing attribute metadata
|
||||
atts.stream().forEach(c -> c.addValidator(validators)
|
||||
.addAnnotations(annotations)
|
||||
.setAttributeDisplayName(attrConfig.getDisplayName())
|
||||
.setGuiOrder(localGuiOrder)
|
||||
.setAttributeGroupMetadata(groupMetadata)
|
||||
.addReadCondition(readAllowedFinal)
|
||||
.addWriteCondition(writeAllowedFinal));
|
||||
if (existingMetadata.isEmpty()) {
|
||||
throw new IllegalStateException("Attribute " + attributeName + " not defined in the context.");
|
||||
}
|
||||
|
||||
for (AttributeMetadata metadata : existingMetadata) {
|
||||
metadata.addAnnotations(annotations)
|
||||
.setAttributeDisplayName(attrConfig.getDisplayName())
|
||||
.setGuiOrder(guiOrder)
|
||||
.setAttributeGroupMetadata(groupMetadata)
|
||||
.addReadCondition(readAllowed)
|
||||
.addWriteCondition(writeAllowed)
|
||||
.addValidators(validators)
|
||||
.setRequired(required);
|
||||
}
|
||||
} else {
|
||||
// always add validation for immutable/read-only attributes
|
||||
validators.add(new AttributeValidatorMetadata(ImmutableAttributeValidator.ID));
|
||||
decoratedMetadata.addAttribute(attributeName, guiOrder++, validators, selector, writeAllowed, required, readAllowed)
|
||||
decoratedMetadata.addAttribute(attributeName, guiOrder, validators, selector, writeAllowed, required, readAllowed)
|
||||
.addAnnotations(annotations)
|
||||
.setAttributeDisplayName(attrConfig.getDisplayName())
|
||||
.setAttributeGroupMetadata(groupMetadata);
|
||||
|
@ -400,7 +397,7 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
return new AttributeGroupMetadata(group.getName(), group.getDisplayHeader(), group.getDisplayDescription(), group.getAnnotations());
|
||||
}
|
||||
|
||||
private boolean isUsernameOrEmailAttribute(String attributeName) {
|
||||
private boolean isBuiltInAttribute(String attributeName) {
|
||||
return UserModel.USERNAME.equals(attributeName) || UserModel.EMAIL.equals(attributeName);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.userprofile.config;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* Configuration of the User Profile for one realm.
|
||||
|
@ -70,6 +71,16 @@ public class UPConfig {
|
|||
return this;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public UPAttribute getAttribute(String name) {
|
||||
for (UPAttribute attribute : getAttributes()) {
|
||||
if (attribute.getName().equals(name)) {
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UPConfig [attributes=" + attributes + ", groups=" + groups + "]";
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.keycloak.userprofile.validator;
|
|||
import java.util.List;
|
||||
|
||||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.userprofile.AttributeContext;
|
||||
import org.keycloak.userprofile.UserProfileAttributeValidationContext;
|
||||
import org.keycloak.validate.SimpleValidator;
|
||||
import org.keycloak.validate.ValidationContext;
|
||||
import org.keycloak.validate.ValidationError;
|
||||
|
@ -56,6 +58,12 @@ public class BlankAttributeValidator implements SimpleValidator {
|
|||
return context;
|
||||
}
|
||||
|
||||
AttributeContext attributeContext = UserProfileAttributeValidationContext.from(context).getAttributeContext();
|
||||
|
||||
if (!attributeContext.getMetadata().isRequired(attributeContext)) {
|
||||
return context;
|
||||
}
|
||||
|
||||
String value = values.isEmpty() ? null: values.get(0);
|
||||
|
||||
if ((failOnNull || value != null) && Validation.isBlank(value)) {
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
{
|
||||
"name": "username",
|
||||
"displayName": "${username}",
|
||||
"permissions": {
|
||||
"view": ["admin", "user"],
|
||||
"edit": ["admin", "user"]
|
||||
},
|
||||
"validations": {
|
||||
"length": { "min": 3, "max": 255 },
|
||||
"username-prohibited-characters": {}
|
||||
|
@ -11,6 +15,11 @@
|
|||
{
|
||||
"name": "email",
|
||||
"displayName": "${email}",
|
||||
"required": {"roles" : ["user"]},
|
||||
"permissions": {
|
||||
"view": ["admin", "user"],
|
||||
"edit": ["admin", "user"]
|
||||
},
|
||||
"validations": {
|
||||
"email" : {},
|
||||
"length": { "max": 255 }
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.actions;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Assert;
|
||||
|
@ -258,7 +262,10 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
|
|||
Assert.assertEquals("New last", updateProfilePage.getLastName());
|
||||
Assert.assertEquals("", updateProfilePage.getEmail());
|
||||
|
||||
Assert.assertEquals("Please specify email.", updateProfilePage.getInputErrors().getEmailError());
|
||||
assertThat(updateProfilePage.getInputErrors().getEmailError(), anyOf(
|
||||
containsString("Please specify email"),
|
||||
containsString("Please specify this field")
|
||||
));
|
||||
|
||||
events.assertEmpty();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
*/
|
||||
package org.keycloak.testsuite.actions;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -226,7 +229,10 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
|||
Assert.assertEquals("New last", updateProfilePage.getLastName());
|
||||
Assert.assertEquals("", updateProfilePage.getEmail());
|
||||
|
||||
Assert.assertEquals("Please specify email.", updateProfilePage.getInputErrors().getEmailError());
|
||||
assertThat(updateProfilePage.getInputErrors().getEmailError(), anyOf(
|
||||
containsString("Please specify email"),
|
||||
containsString("Please specify this field")
|
||||
));
|
||||
|
||||
events.assertEmpty();
|
||||
}
|
||||
|
|
|
@ -227,7 +227,7 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
user.setUsername(PARENT3_USERNAME);
|
||||
user.setFirstName("first name");
|
||||
user.setLastName("last name");
|
||||
user.setEmail("email");
|
||||
user.setEmail("email@keycloak.org");
|
||||
user.setEnabled(true);
|
||||
createUserAndResetPasswordWithAdminClient(realm, user, "password");
|
||||
}
|
||||
|
@ -749,7 +749,7 @@ public class BrokerLinkAndTokenExchangeTest extends AbstractServletsAdapterTest
|
|||
Assert.assertEquals(PARENT3_USERNAME, token.getPreferredUsername());
|
||||
Assert.assertEquals("first name", token.getGivenName());
|
||||
Assert.assertEquals("last name", token.getFamilyName());
|
||||
Assert.assertEquals("email", token.getEmail());
|
||||
Assert.assertEquals("email@keycloak.org", token.getEmail());
|
||||
|
||||
// cleanup remove the user
|
||||
childRealm.users().get(token.getSubject()).remove();
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.keycloak.testsuite.util.OAuthClient;
|
|||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
@ -515,7 +516,9 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
|
|||
assertTrue(registerPage.isCurrent());
|
||||
assertEquals("Invalid password: must not be equal to the username.", registerPage.getInputPasswordErrors().getPasswordError());
|
||||
|
||||
adminClient.realm("test").users().create(UserBuilder.create().username("registerUserNotUsername").build());
|
||||
try (Response response = adminClient.realm("test").users().create(UserBuilder.create().username("registerUserNotUsername").build())) {
|
||||
assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
|
||||
}
|
||||
|
||||
registerPage.register("firstName", "lastName", "registerUserNotUsername@email", "registerUserNotUsername", "registerUserNotUsername", "registerUserNotUsername");
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.keycloak.testsuite.forms;
|
|||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
|
||||
|
@ -59,7 +61,7 @@ public class RegisterWithUserProfileTest extends RegisterTest {
|
|||
private static ClientRepresentation client_scope_optional;
|
||||
|
||||
public static String UP_CONFIG_BASIC_ATTRIBUTES = "{\"name\": \"username\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + ", \"required\": {}},";
|
||||
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + ", \"required\": {\"roles\" : [\"user\"]}},";
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
|
@ -602,6 +604,64 @@ public class RegisterWithUserProfileTest extends RegisterTest {
|
|||
assertEquals(null, user.firstAttribute(ATTRIBUTE_DEPARTMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmailAsOptional() {
|
||||
|
||||
setUserProfileConfiguration("{\"attributes\": ["
|
||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + "}"
|
||||
+ "]}");
|
||||
|
||||
loginPage.open();
|
||||
loginPage.clickRegister();
|
||||
registerPage.assertCurrent();
|
||||
|
||||
registerPage.register("firstName", "lastName", null, "registerWithoutEmail", "password", "password");
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmailRequired() {
|
||||
|
||||
setUserProfileConfiguration("{\"attributes\": ["
|
||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + ", \"required\": {}}"
|
||||
+ "]}");
|
||||
|
||||
loginPage.open();
|
||||
loginPage.clickRegister();
|
||||
registerPage.assertCurrent();
|
||||
|
||||
registerPage.register("firstName", "lastName", null, "registerWithoutEmail", "password", "password");
|
||||
registerPage.assertCurrent();
|
||||
assertThat(registerPage.getInputAccountErrors().getEmailError(), anyOf(
|
||||
containsString("Please specify email"),
|
||||
containsString("Please specify this field")
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmailRequiredForUser() {
|
||||
|
||||
setUserProfileConfiguration("{\"attributes\": ["
|
||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + ", \"required\": {\"roles\" : [\"user\"]}}"
|
||||
+ "]}");
|
||||
|
||||
loginPage.open();
|
||||
loginPage.clickRegister();
|
||||
registerPage.assertCurrent();
|
||||
|
||||
registerPage.register("firstName", "lastName", null, "registerWithoutEmail", "password", "password");
|
||||
assertThat(registerPage.getInputAccountErrors().getEmailError(), anyOf(
|
||||
containsString("Please specify email"),
|
||||
containsString("Please specify this field")
|
||||
));
|
||||
}
|
||||
|
||||
private void assertUserRegistered(String userId, String username, String email, String firstName, String lastName) {
|
||||
events.expectLogin().detail("username", username.toLowerCase()).user(userId).assertEvent();
|
||||
|
|
|
@ -184,7 +184,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
|||
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testAttributeValidation);
|
||||
}
|
||||
|
||||
private static void failValidationWhenEmptyAttributes(KeycloakSession session) {
|
||||
private static void failValidationWhenEmptyAttributes(KeycloakSession session) throws IOException {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
||||
provider.setConfiguration(null);
|
||||
|
@ -227,6 +227,14 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
|||
realm.setRegistrationEmailAsUsername(false);
|
||||
}
|
||||
|
||||
UPConfig config = JsonSerialization.readValue(provider.getConfiguration(), UPConfig.class);
|
||||
|
||||
UPAttribute email = config.getAttribute("email");
|
||||
|
||||
email.setRequired(null);
|
||||
|
||||
provider.setConfiguration(JsonSerialization.writeValueAsString(config));
|
||||
|
||||
attributes.clear();
|
||||
attributes.put(UserModel.USERNAME, "profile-user");
|
||||
attributes.put(UserModel.FIRST_NAME, "Joe");
|
||||
|
@ -438,6 +446,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
|||
String userName = org.keycloak.models.utils.KeycloakModelUtils.generateId();
|
||||
|
||||
attributes.put(UserModel.USERNAME, userName);
|
||||
attributes.put(UserModel.EMAIL, "user@keycloak.org");
|
||||
attributes.put(UserModel.FIRST_NAME, "Joe");
|
||||
attributes.put(UserModel.LAST_NAME, "Doe");
|
||||
attributes.put("address", "fixed-address");
|
||||
|
@ -457,6 +466,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
|||
Map<String, String> attributesUpdatedOldValues = new HashMap<>();
|
||||
attributesUpdatedOldValues.put(UserModel.FIRST_NAME, "Joe");
|
||||
attributesUpdatedOldValues.put(UserModel.LAST_NAME, "Doe");
|
||||
attributesUpdatedOldValues.put(UserModel.EMAIL, "user@keycloak.org");
|
||||
|
||||
profile.update((attributeName, userModel, oldValue) -> {
|
||||
assertTrue(attributesUpdated.add(attributeName));
|
||||
|
@ -856,6 +866,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
|||
provider.setConfiguration(null);
|
||||
|
||||
attributes.put(UserModel.USERNAME, "user");
|
||||
attributes.put(UserModel.EMAIL, "user@keycloak.org");
|
||||
attributes.put(UserModel.FIRST_NAME, "Joe");
|
||||
attributes.put(UserModel.LAST_NAME, "Doe");
|
||||
|
||||
|
|
|
@ -1634,7 +1634,19 @@ module.controller('RealmUserProfileCtrl', function($scope, Realm, realm, clientS
|
|||
return attributeName != "username" && attributeName != "email";
|
||||
};
|
||||
|
||||
$scope.guiOrderUp = function(index) {
|
||||
$scope.showRequiredSettings = function(attributeName) {
|
||||
if (attributeName == "username") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attributeName == "email" && realm.registrationEmailAsUsername) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
$scope.guiOrderUp = function(index) {
|
||||
$scope.moveAttribute(index, index - 1);
|
||||
};
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@
|
|||
<input type="hidden" ui-select2="selectorByScopeSelect" id="selectorByScopeSelect" data-ng-model="selectorByScope" data-placeholder="Select a scope..." multiple/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="isNotUsernameOrEmail(currentAttribute.name)">
|
||||
<div class="form-group" data-ng-show="showRequiredSettings(currentAttribute.name)">
|
||||
<label class="col-md-2 control-label" for="isRequired">{{:: 'user.profile.attribute.required' | translate}}</label>
|
||||
<kc-tooltip>{{:: 'user.profile.attribute.required.tooltip' | translate}}</kc-tooltip>
|
||||
<div class="col-md-6">
|
||||
|
@ -160,14 +160,14 @@
|
|||
on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="isRequired">
|
||||
<div class="form-group" data-ng-show="isRequired && showRequiredSettings(currentAttribute.name)">
|
||||
<label class="col-md-2 control-label" for="isRequiredRoles">{{:: 'user.profile.attribute.required.roles' | translate}}</label>
|
||||
<kc-tooltip>{{:: 'user.profile.attribute.required.roles.tooltip' | translate}}</kc-tooltip>
|
||||
<div class="col-md-6">
|
||||
<input type="hidden" ui-select2="isRequiredRoles" id="isRequiredRoles" data-ng-model="requiredRoles" data-placeholder="Select a role..." multiple/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="isRequired">
|
||||
<div class="form-group" data-ng-show="isRequired && showRequiredSettings(currentAttribute.name)">
|
||||
<label class="col-md-2 control-label" for="isRequiredScopes">{{:: 'user.profile.attribute.required.scopes' | translate}}</label>
|
||||
<kc-tooltip>{{:: 'user.profile.attribute.required.scopes.tooltip' | translate}}</kc-tooltip>
|
||||
<div class="col-md-6">
|
||||
|
|
Loading…
Reference in a new issue