Allow permission configuration for username and email in user profile.
Enhanced Account API to respect access to these attributes. Resolves #12599
This commit is contained in:
parent
e47bbba7ef
commit
62790b8ce0
8 changed files with 234 additions and 20 deletions
|
@ -45,10 +45,10 @@ public final class AttributeMetadata {
|
||||||
private String attributeDisplayName;
|
private String attributeDisplayName;
|
||||||
private AttributeGroupMetadata attributeGroupMetadata;
|
private AttributeGroupMetadata attributeGroupMetadata;
|
||||||
private final Predicate<AttributeContext> selector;
|
private final Predicate<AttributeContext> selector;
|
||||||
private final Predicate<AttributeContext> writeAllowed;
|
private final List<Predicate<AttributeContext>> writeAllowed = new ArrayList<>();
|
||||||
/** Predicate to decide if attribute is required, it is handled as required if predicate is null */
|
/** Predicate to decide if attribute is required, it is handled as required if predicate is null */
|
||||||
private final Predicate<AttributeContext> required;
|
private final Predicate<AttributeContext> required;
|
||||||
private final Predicate<AttributeContext> readAllowed;
|
private final List<Predicate<AttributeContext>> readAllowed = new ArrayList<>();
|
||||||
private List<AttributeValidatorMetadata> validators;
|
private List<AttributeValidatorMetadata> validators;
|
||||||
private Map<String, Object> annotations;
|
private Map<String, Object> annotations;
|
||||||
private int guiOrder;
|
private int guiOrder;
|
||||||
|
@ -93,11 +93,22 @@ public final class AttributeMetadata {
|
||||||
Predicate<AttributeContext> required,
|
Predicate<AttributeContext> required,
|
||||||
Predicate<AttributeContext> readAllowed) {
|
Predicate<AttributeContext> readAllowed) {
|
||||||
this.attributeName = attributeName;
|
this.attributeName = attributeName;
|
||||||
this.selector = selector;
|
|
||||||
this.writeAllowed = writeAllowed;
|
|
||||||
this.required = required;
|
|
||||||
this.readAllowed = readAllowed;
|
|
||||||
this.guiOrder = guiOrder;
|
this.guiOrder = guiOrder;
|
||||||
|
this.selector = selector;
|
||||||
|
addWriteCondition(writeAllowed);
|
||||||
|
this.required = required;
|
||||||
|
addReadCondition(readAllowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeMetadata(String attributeName, int guiOrder, Predicate<AttributeContext> selector, List<Predicate<AttributeContext>> writeAllowed,
|
||||||
|
Predicate<AttributeContext> required,
|
||||||
|
List<Predicate<AttributeContext>> readAllowed) {
|
||||||
|
this.attributeName = attributeName;
|
||||||
|
this.guiOrder = guiOrder;
|
||||||
|
this.selector = selector;
|
||||||
|
this.writeAllowed.addAll(writeAllowed);
|
||||||
|
this.required = required;
|
||||||
|
this.readAllowed.addAll(readAllowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -121,16 +132,29 @@ public final class AttributeMetadata {
|
||||||
return selector.test(context);
|
return selector.test(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean allConditionsMet(List<Predicate<AttributeContext>> predicates, AttributeContext context) {
|
||||||
|
return predicates.stream().allMatch(p -> p.test(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttributeMetadata addReadCondition(Predicate<AttributeContext> readAllowed) {
|
||||||
|
this.readAllowed.add(readAllowed);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttributeMetadata addWriteCondition(Predicate<AttributeContext> writeAllowed) {
|
||||||
|
this.writeAllowed.add(writeAllowed);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
public boolean isReadOnly(AttributeContext context) {
|
public boolean isReadOnly(AttributeContext context) {
|
||||||
return !writeAllowed.test(context);
|
return !canEdit(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canView(AttributeContext context) {
|
public boolean canView(AttributeContext context) {
|
||||||
return readAllowed.test(context);
|
return allConditionsMet(readAllowed, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canEdit(AttributeContext context) {
|
public boolean canEdit(AttributeContext context) {
|
||||||
return writeAllowed.test(context);
|
return allConditionsMet(writeAllowed, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -277,6 +277,7 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, UPGroup> groupsByName = asHashMap(parsedConfig.getGroups());
|
Map<String, UPGroup> groupsByName = asHashMap(parsedConfig.getGroups());
|
||||||
|
RealmModel realm = session.getContext().getRealm();
|
||||||
int guiOrder = 0;
|
int guiOrder = 0;
|
||||||
|
|
||||||
for (UPAttribute attrConfig : parsedConfig.getAttributes()) {
|
for (UPAttribute attrConfig : parsedConfig.getAttributes()) {
|
||||||
|
@ -343,10 +344,19 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
||||||
// make sure username and email are writable if permissions are not set
|
// make sure username and email are writable if permissions are not set
|
||||||
if (permissions == null || permissions.isEmpty()) {
|
if (permissions == null || permissions.isEmpty()) {
|
||||||
writeAllowed = AttributeMetadata.ALWAYS_TRUE;
|
writeAllowed = AttributeMetadata.ALWAYS_TRUE;
|
||||||
|
readAllowed = AttributeMetadata.ALWAYS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AttributeMetadata> atts = decoratedMetadata.getAttribute(attributeName);
|
List<AttributeMetadata> atts = decoratedMetadata.getAttribute(attributeName);
|
||||||
|
|
||||||
|
// 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)) {
|
||||||
|
validators.add(new AttributeValidatorMetadata(ImmutableAttributeValidator.ID));
|
||||||
|
}
|
||||||
|
|
||||||
if (atts.isEmpty()) {
|
if (atts.isEmpty()) {
|
||||||
// attribute metadata doesn't exist so we have to add it. We keep it optional as Abstract base
|
// attribute metadata doesn't exist so we have to add it. We keep it optional as Abstract base
|
||||||
// doesn't require it.
|
// doesn't require it.
|
||||||
|
@ -356,12 +366,17 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
||||||
.setAttributeGroupMetadata(groupMetadata);
|
.setAttributeGroupMetadata(groupMetadata);
|
||||||
} else {
|
} else {
|
||||||
final int localGuiOrder = guiOrder++;
|
final int localGuiOrder = guiOrder++;
|
||||||
// only add configured validators and annotations if attribute metadata exist
|
Predicate<AttributeContext> readAllowedFinal = readAllowed;
|
||||||
|
Predicate<AttributeContext> writeAllowedFinal = writeAllowed;
|
||||||
|
|
||||||
|
// add configured validators and annotations to existing attribute metadata
|
||||||
atts.stream().forEach(c -> c.addValidator(validators)
|
atts.stream().forEach(c -> c.addValidator(validators)
|
||||||
.addAnnotations(annotations)
|
.addAnnotations(annotations)
|
||||||
.setAttributeDisplayName(attrConfig.getDisplayName())
|
.setAttributeDisplayName(attrConfig.getDisplayName())
|
||||||
.setGuiOrder(localGuiOrder)
|
.setGuiOrder(localGuiOrder)
|
||||||
.setAttributeGroupMetadata(groupMetadata));
|
.setAttributeGroupMetadata(groupMetadata)
|
||||||
|
.addReadCondition(readAllowedFinal)
|
||||||
|
.addWriteCondition(writeAllowedFinal));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// always add validation for immutable/read-only attributes
|
// always add validation for immutable/read-only attributes
|
||||||
|
|
|
@ -132,6 +132,14 @@ public class VerifyProfilePage extends AbstractPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUsernameEnabled() {
|
||||||
|
try {
|
||||||
|
return driver.findElement(By.id("username")).isEnabled();
|
||||||
|
} catch (NoSuchElementException nse) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDepartmentPresent() {
|
public boolean isDepartmentPresent() {
|
||||||
try {
|
try {
|
||||||
isDepartmentEnabled();
|
isDepartmentEnabled();
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String UP_CONFIG_FOR_METADATA = "{\"attributes\": ["
|
private final static String UP_CONFIG_FOR_METADATA = "{\"attributes\": ["
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {\"scopes\":[\"profile\"]}, \"displayName\": \"${profile.firstName}\", \"validations\": {\"length\": { \"max\": 255 }}},"
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {\"scopes\":[\"profile\"]}, \"displayName\": \"${profile.firstName}\", \"validations\": {\"length\": { \"max\": 255 }}},"
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}, \"displayName\": \"Last name\", \"annotations\": {\"formHintKey\" : \"userEmailFormFieldHint\", \"anotherKey\" : 10, \"yetAnotherKey\" : \"some value\"}},"
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}, \"displayName\": \"Last name\", \"annotations\": {\"formHintKey\" : \"userEmailFormFieldHint\", \"anotherKey\" : 10, \"yetAnotherKey\" : \"some value\"}},"
|
||||||
+ "{\"name\": \"attr_with_scope_selector\"," + PERMISSIONS_ALL + ", \"selector\": {\"scopes\": [\"profile\"]}},"
|
+ "{\"name\": \"attr_with_scope_selector\"," + PERMISSIONS_ALL + ", \"selector\": {\"scopes\": [\"profile\"]}},"
|
||||||
|
@ -78,20 +78,26 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
||||||
+ "{\"name\": \"attr_no_permission\"," + PERMISSIONS_ADMIN_ONLY + "}"
|
+ "{\"name\": \"attr_no_permission\"," + PERMISSIONS_ADMIN_ONLY + "}"
|
||||||
+ "]}";
|
+ "]}";
|
||||||
|
|
||||||
private static String UP_CONFIG_NO_ACCESS_TO_NAME_FIELDS = "{\"attributes\": ["
|
private final static String UP_CONFIG_NO_ACCESS_TO_NAME_FIELDS = "{\"attributes\": ["
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ADMIN_ONLY + ", \"required\": {}, \"displayName\": \"${profile.firstName}\", \"validations\": {\"length\": { \"max\": 255 }}},"
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ADMIN_ONLY + ", \"required\": {}, \"displayName\": \"${profile.firstName}\", \"validations\": {\"length\": { \"max\": 255 }}},"
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ADMIN_ONLY + ", \"required\": {}, \"displayName\": \"Last name\", \"annotations\": {\"formHintKey\" : \"userEmailFormFieldHint\", \"anotherKey\" : 10, \"yetAnotherKey\" : \"some value\"}},"
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ADMIN_ONLY + ", \"required\": {}, \"displayName\": \"Last name\", \"annotations\": {\"formHintKey\" : \"userEmailFormFieldHint\", \"anotherKey\" : 10, \"yetAnotherKey\" : \"some value\"}},"
|
||||||
+ "{\"name\": \"attr_readonly\"," + PERMISSIONS_ADMIN_EDITABLE + "},"
|
+ "{\"name\": \"attr_readonly\"," + PERMISSIONS_ADMIN_EDITABLE + "},"
|
||||||
+ "{\"name\": \"attr_no_permission\"," + PERMISSIONS_ADMIN_ONLY + "}"
|
+ "{\"name\": \"attr_no_permission\"," + PERMISSIONS_ADMIN_ONLY + "}"
|
||||||
+ "]}";
|
+ "]}";
|
||||||
|
|
||||||
private static String UP_CONFIG_RO_ACCESS_TO_NAME_FIELDS = "{\"attributes\": ["
|
private final static String UP_CONFIG_RO_ACCESS_TO_NAME_FIELDS = "{\"attributes\": ["
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ADMIN_EDITABLE + ", \"required\": {}, \"displayName\": \"${profile.firstName}\", \"validations\": {\"length\": { \"max\": 255 }}},"
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ADMIN_EDITABLE + ", \"required\": {}, \"displayName\": \"${profile.firstName}\", \"validations\": {\"length\": { \"max\": 255 }}},"
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ADMIN_EDITABLE + ", \"required\": {}, \"displayName\": \"Last name\", \"annotations\": {\"formHintKey\" : \"userEmailFormFieldHint\", \"anotherKey\" : 10, \"yetAnotherKey\" : \"some value\"}},"
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ADMIN_EDITABLE + ", \"required\": {}, \"displayName\": \"Last name\", \"annotations\": {\"formHintKey\" : \"userEmailFormFieldHint\", \"anotherKey\" : 10, \"yetAnotherKey\" : \"some value\"}},"
|
||||||
+ "{\"name\": \"attr_readonly\"," + PERMISSIONS_ADMIN_EDITABLE + "},"
|
+ "{\"name\": \"attr_readonly\"," + PERMISSIONS_ADMIN_EDITABLE + "},"
|
||||||
+ "{\"name\": \"attr_no_permission\"," + PERMISSIONS_ADMIN_ONLY + "}"
|
+ "{\"name\": \"attr_no_permission\"," + PERMISSIONS_ADMIN_ONLY + "}"
|
||||||
+ "]}";
|
+ "]}";
|
||||||
|
|
||||||
|
private final static String UP_CONFIG_RO_USERNAME_AND_EMAIL = "{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"email\"," + PERMISSIONS_ADMIN_EDITABLE + ", \"required\": {}, \"displayName\": \"${email}\", \"annotations\": {\"formHintKey\" : \"userEmailFormFieldHint\", \"anotherKey\" : 10, \"yetAnotherKey\" : \"some value\"}},"
|
||||||
|
+ "{\"name\": \"attr_readonly\"," + PERMISSIONS_ADMIN_EDITABLE + "},"
|
||||||
|
+ "{\"name\": \"attr_no_permission\"," + PERMISSIONS_ADMIN_ONLY + "}"
|
||||||
|
+ "]}";
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Override
|
@Override
|
||||||
|
@ -187,6 +193,31 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUserProfileMetadata_RoAccessToUsernameAndEmail() throws IOException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
RealmRepresentation realmRep = adminClient.realm("test").toRepresentation();
|
||||||
|
realmRep.setEditUsernameAllowed(false);
|
||||||
|
adminClient.realm("test").update(realmRep);
|
||||||
|
|
||||||
|
setUserProfileConfiguration(UP_CONFIG_RO_USERNAME_AND_EMAIL);
|
||||||
|
|
||||||
|
UserRepresentation user = getUser();
|
||||||
|
assertNotNull(user.getUserProfileMetadata());
|
||||||
|
|
||||||
|
assertUserProfileAttributeMetadata(user, "username", "${username}", true, true);
|
||||||
|
assertUserProfileAttributeMetadata(user, "email", "${email}", true, true);
|
||||||
|
|
||||||
|
assertUserProfileAttributeMetadata(user, "attr_readonly", "attr_readonly", false, true);
|
||||||
|
assertNull(getUserProfileAttributeMetadata(user, "attr_no_permission"));
|
||||||
|
} finally {
|
||||||
|
RealmRepresentation realmRep = testRealm().toRepresentation();
|
||||||
|
realmRep.setEditUsernameAllowed(true);
|
||||||
|
testRealm().update(realmRep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -532,6 +532,63 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
assertEquals("Last", user.getLastName());
|
assertEquals("Last", user.getLastName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAdminOnlyAttributeNotVisibleToUser() {
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"department\"," + PERMISSIONS_ADMIN_ONLY + "},"
|
||||||
|
+ "{\"name\": \"requiredAttrToTriggerVerifyPage\"," + PERMISSIONS_ALL + ", \"required\": {}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.login("login-test6", "password");
|
||||||
|
|
||||||
|
verifyProfilePage.assertCurrent();
|
||||||
|
Assert.assertEquals("ExistingLast", verifyProfilePage.getLastName());
|
||||||
|
Assert.assertFalse("Admin-only attribute should not be visible for user", verifyProfilePage.isDepartmentPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUsernameReadOnlyInProfile() {
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"username\"," + PERMISSIONS_ADMIN_EDITABLE + "},"
|
||||||
|
+ "{\"name\": \"requiredAttrToTriggerVerifyPage\"," + PERMISSIONS_ALL + ", \"required\": {}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.login("login-test6", "password");
|
||||||
|
|
||||||
|
verifyProfilePage.assertCurrent();
|
||||||
|
Assert.assertEquals("ExistingLast", verifyProfilePage.getLastName());
|
||||||
|
|
||||||
|
Assert.assertFalse("username should not be editable by user", verifyProfilePage.isUsernameEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUsernameReadNotVisibleInProfile() {
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"username\"," + PERMISSIONS_ADMIN_ONLY + "},"
|
||||||
|
+ "{\"name\": \"requiredAttrToTriggerVerifyPage\"," + PERMISSIONS_ALL + ", \"required\": {}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
loginPage.open();
|
||||||
|
loginPage.login("login-test6", "password");
|
||||||
|
|
||||||
|
verifyProfilePage.assertCurrent();
|
||||||
|
Assert.assertEquals("ExistingLast", verifyProfilePage.getLastName());
|
||||||
|
|
||||||
|
Assert.assertFalse("username should not be shown to user", verifyProfilePage.isUsernamePresent());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAttributeNotVisible() {
|
public void testAttributeNotVisible() {
|
||||||
|
|
||||||
|
|
|
@ -535,6 +535,85 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
assertTrue(profile.getAttributes().isReadOnly("department"));
|
assertTrue(profile.getAttributes().isReadOnly("department"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadonlyEmailCannotBeUpdated() {
|
||||||
|
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testReadonlyEmailCannotBeUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testReadonlyEmailCannotBeUpdated(KeycloakSession session) {
|
||||||
|
Map<String, Object> attributes = new HashMap<>();
|
||||||
|
|
||||||
|
attributes.put(UserModel.USERNAME, org.keycloak.models.utils.KeycloakModelUtils.generateId());
|
||||||
|
attributes.put(UserModel.EMAIL, "readonly@foo.bar");
|
||||||
|
|
||||||
|
UserProfileProvider provider = getDynamicUserProfileProvider(session);
|
||||||
|
|
||||||
|
// configure email r/o for user
|
||||||
|
provider.setConfiguration("{\"attributes\": [{\"name\": \"email\", \"permissions\": {\"edit\": [ \"admin\"]}}]}");
|
||||||
|
|
||||||
|
UserProfile profile = provider.create(UserProfileContext.ACCOUNT, attributes);
|
||||||
|
UserModel user = profile.create();
|
||||||
|
|
||||||
|
assertThat(profile.getAttributes().nameSet(),
|
||||||
|
containsInAnyOrder(UserModel.USERNAME, UserModel.EMAIL));
|
||||||
|
|
||||||
|
profile = provider.create(UserProfileContext.USER_API, attributes, user);
|
||||||
|
|
||||||
|
Set<String> attributesUpdated = new HashSet<>();
|
||||||
|
|
||||||
|
profile.update((attributeName, userModel, oldValue) -> assertTrue(attributesUpdated.add(attributeName)));
|
||||||
|
|
||||||
|
attributes.put(UserModel.EMAIL, "cannot-change@foo.bar");
|
||||||
|
|
||||||
|
profile = provider.create(UserProfileContext.ACCOUNT, attributes, user);
|
||||||
|
|
||||||
|
try {
|
||||||
|
profile.update();
|
||||||
|
fail("Should fail since email is read only");
|
||||||
|
} catch (ValidationException ve) {
|
||||||
|
assertTrue(ve.isAttributeOnError("email"));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("E-Mail address shouldn't be changed", "readonly@foo.bar", user.getEmail());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateEmail() {
|
||||||
|
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testUpdateEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testUpdateEmail(KeycloakSession session) {
|
||||||
|
Map<String, Object> attributes = new HashMap<>();
|
||||||
|
|
||||||
|
attributes.put(UserModel.USERNAME, org.keycloak.models.utils.KeycloakModelUtils.generateId());
|
||||||
|
attributes.put(UserModel.EMAIL, "canchange@foo.bar");
|
||||||
|
|
||||||
|
UserProfileProvider provider = getDynamicUserProfileProvider(session);
|
||||||
|
|
||||||
|
// configure email r/w for user
|
||||||
|
provider.setConfiguration("{\"attributes\": [{\"name\": \"email\", \"permissions\": {\"edit\": [ \"user\", \"admin\"]}}]}");
|
||||||
|
|
||||||
|
UserProfile profile = provider.create(UserProfileContext.ACCOUNT, attributes);
|
||||||
|
UserModel user = profile.create();
|
||||||
|
|
||||||
|
assertThat(profile.getAttributes().nameSet(),
|
||||||
|
containsInAnyOrder(UserModel.USERNAME, UserModel.EMAIL));
|
||||||
|
|
||||||
|
profile = provider.create(UserProfileContext.USER_API, attributes, user);
|
||||||
|
|
||||||
|
Set<String> attributesUpdated = new HashSet<>();
|
||||||
|
|
||||||
|
profile.update((attributeName, userModel, oldValue) -> assertTrue(attributesUpdated.add(attributeName)));
|
||||||
|
|
||||||
|
attributes.put("email", "changed@foo.bar");
|
||||||
|
|
||||||
|
profile = provider.create(UserProfileContext.ACCOUNT, attributes, user);
|
||||||
|
|
||||||
|
profile.update();
|
||||||
|
|
||||||
|
assertEquals("E-Mail address should have been changed!", "changed@foo.bar", user.getEmail());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDoNotUpdateUndefinedAttributes() {
|
public void testDoNotUpdateUndefinedAttributes() {
|
||||||
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testDoNotUpdateUndefinedAttributes);
|
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testDoNotUpdateUndefinedAttributes);
|
||||||
|
|
|
@ -174,7 +174,7 @@
|
||||||
<input type="hidden" ui-select2="isRequiredScopes" id="isRequiredScopes" data-ng-model="requiredScopes" data-placeholder="Select a scope..." multiple/>
|
<input type="hidden" ui-select2="isRequiredScopes" id="isRequiredScopes" data-ng-model="requiredScopes" data-placeholder="Select a scope..." multiple/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<fieldset class="border-top" data-ng-show="isNotUsernameOrEmail(currentAttribute.name)">
|
<fieldset class="border-top">
|
||||||
<legend collapsed><span class="text">{{:: 'user.profile.attribute.permission' | translate}}</span></legend>
|
<legend collapsed><span class="text">{{:: 'user.profile.attribute.permission' | translate}}</span></legend>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="canUserView">{{:: 'user.profile.attribute.canUserView' | translate}}</label>
|
<label class="col-md-2 control-label" for="canUserView">{{:: 'user.profile.attribute.canUserView' | translate}}</label>
|
||||||
|
|
|
@ -180,7 +180,7 @@ export class AccountPage extends React.Component<AccountPageProps, AccountPageSt
|
||||||
onSubmit={(event) => this.handleSubmit(event)}
|
onSubmit={(event) => this.handleSubmit(event)}
|
||||||
className="personal-info-form"
|
className="personal-info-form"
|
||||||
>
|
>
|
||||||
{!this.isRegistrationEmailAsUsername && (
|
{!this.isRegistrationEmailAsUsername && fields.username != undefined && (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={Msg.localize("username")}
|
label={Msg.localize("username")}
|
||||||
fieldId="user-name"
|
fieldId="user-name"
|
||||||
|
|
Loading…
Reference in a new issue