Align the format of the annotation kc.scim.schema.attribute to how SCIM defines custom schema attributes

Closes #48632

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2026-05-07 10:32:00 -03:00 committed by GitHub
parent 33edd62a78
commit 493145b4ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 105 additions and 123 deletions

View file

@ -25,13 +25,13 @@ const SCIM_CORE_ATTRIBUTES = [
];
const SCIM_ENTERPRISE_ATTRIBUTES = [
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.employeeNumber",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.costCenter",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.organization",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.division",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.department",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.manager.value",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.manager.displayName",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:costCenter",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:division",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.value",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.displayName",
];
type ScimAttributeGroup = {

View file

@ -38,25 +38,24 @@ public class Attribute<M extends Model, R extends ResourceTypeRepresentation> {
return null;
}
return name.substring(0, schemaSeparator) + ":" + getResourceType(name);
return name.substring(0, schemaSeparator);
}
public static String getResourceType(String name) {
requireNonNull(name, "name is required");
int schemaSeparator = name.lastIndexOf(':');
String schema = getSchema(name);
if (schemaSeparator == -1) {
if (schema == null) {
return null;
}
String resourceType = name.substring(schemaSeparator + 1);
int fieldSeparator = resourceType.indexOf('.');
int resourceTypeSeparator = schema.lastIndexOf(':');
if (fieldSeparator == -1) {
if (resourceTypeSeparator == -1) {
return null;
}
return resourceType.substring(0, fieldSeparator);
return schema.substring(resourceTypeSeparator + 1);
}
public static String getSimpleName(String name) {

View file

@ -155,7 +155,9 @@ public class SchemaResourceTypeProvider implements ScimResourceTypeProvider<Sche
rep.setAttributes(List.copyOf(topLevelAttributes.values()));
if (!modelSchema.isInternal()) {
List<Attribute> attributes = rep.getAttributes();
if (!modelSchema.isInternal() && !attributes.isEmpty()) {
schemas.put(modelSchema.getId(), rep);
}
}

View file

@ -11,8 +11,12 @@
package org.keycloak.tests.scim.tck;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import org.keycloak.representations.userprofile.config.UPAttribute;
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
import org.keycloak.representations.userprofile.config.UPConfig;
import org.keycloak.scim.client.ScimClient;
import org.keycloak.testframework.annotations.InjectAdminEvents;
@ -20,6 +24,7 @@ import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.events.AdminEvents;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.scim.client.annotations.InjectScimClient;
import org.keycloak.userprofile.config.UPConfigUtils;
import static org.keycloak.scim.model.user.AbstractUserModelSchema.ANNOTATION_SCIM_SCHEMA_ATTRIBUTE;
import static org.keycloak.scim.resource.Scim.ENTERPRISE_USER_SCHEMA;
@ -37,22 +42,42 @@ public abstract class AbstractScimTest {
protected void addEnterpriseUserUserProfileAttributes() {
UPConfig configuration = realm.admin().users().userProfile().getConfiguration();
UPConfig originalConfig = configuration.clone();
realm.cleanup().add(realm -> realm.users().userProfile().update(originalConfig));
configuration.addOrReplaceAttribute(new UPAttribute("department", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".department")));
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ":department")));
configuration.addOrReplaceAttribute(new UPAttribute("division", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".division")));
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ":division")));
configuration.addOrReplaceAttribute(new UPAttribute("costCenter", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".costCenter")));
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ":costCenter")));
configuration.addOrReplaceAttribute(new UPAttribute("employeeNumber", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".employeeNumber")));
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ":employeeNumber")));
configuration.addOrReplaceAttribute(new UPAttribute("organization", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".organization")));
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ":organization")));
configuration.addOrReplaceAttribute(new UPAttribute("manager", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".manager.value")));
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ":manager.value")));
configuration.addOrReplaceAttribute(new UPAttribute("managerName", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".manager.displayName")));
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ":manager.displayName")));
realm.admin().users().userProfile().update(configuration);
}
protected UPAttribute addOrReplaceUPAttribute(String name) {
return addOrReplaceUPAttribute(null, name);
}
protected UPAttribute addOrReplaceUPAttribute(String customSchema, String name) {
UPConfig upConfig = realm.admin().users().userProfile().getConfiguration();
UPAttribute upAttribute = new UPAttribute("scim." + name, Map.of(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, Optional.ofNullable(customSchema).map(new Function<String, String>() {
@Override
public String apply(String s) {
return s + ":";
}
}).orElse("") + name));
upAttribute.setPermissions(new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN)));
upConfig.addOrReplaceAttribute(upAttribute);
realm.admin().users().userProfile().update(upConfig);
adminEvents.clear();
return upAttribute;
}
}

View file

@ -4,7 +4,6 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.keycloak.models.UserModel;
@ -24,7 +23,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.keycloak.scim.model.user.AbstractUserModelSchema.ANNOTATION_SCIM_SCHEMA_ATTRIBUTE;
import static org.keycloak.scim.resource.Scim.ENTERPRISE_USER_SCHEMA;
import static org.hamcrest.MatcherAssert.assertThat;
@ -617,24 +615,7 @@ public class FilterTest extends AbstractScimTest {
@Test
public void testSearchEnterpriseUsers() {
UPConfig configuration = realm.admin().users().userProfile().getConfiguration();
// update user profile configuration
configuration.addOrReplaceAttribute(new UPAttribute("department", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".department")));
configuration.addOrReplaceAttribute(new UPAttribute("division", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".division")));
configuration.addOrReplaceAttribute(new UPAttribute("costCenter", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".costCenter")));
configuration.addOrReplaceAttribute(new UPAttribute("employeeNumber", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".employeeNumber")));
configuration.addOrReplaceAttribute(new UPAttribute("organization", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".organization")));
configuration.addOrReplaceAttribute(new UPAttribute("manager", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".manager.value")));
configuration.addOrReplaceAttribute(new UPAttribute("managerName", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".manager.displayName")));
realm.admin().users().userProfile().update(configuration);
addEnterpriseUserUserProfileAttributes();
User user1 = createEnterpriseUser("user1", "Engineering", "E1234", "Bruce Wayne");
User user2 = createEnterpriseUser("user2", "QE", "E7763", "Lucius Fox");

View file

@ -1,23 +1,16 @@
package org.keycloak.tests.scim.tck;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.keycloak.representations.userprofile.config.UPAttribute;
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
import org.keycloak.representations.userprofile.config.UPConfig;
import org.keycloak.scim.protocol.response.ListResponse;
import org.keycloak.scim.resource.Scim;
import org.keycloak.scim.resource.schema.Schema;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.userprofile.config.UPConfigUtils;
import org.junit.jupiter.api.Test;
import static org.keycloak.scim.model.user.AbstractUserModelSchema.ANNOTATION_SCIM_SCHEMA_ATTRIBUTE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -29,6 +22,7 @@ public class SchemaTest extends AbstractScimTest {
@Test
public void testGetAllSchemas() {
addEnterpriseUserUserProfileAttributes();
String customSchema = "urn:my:params:scim:schemas:extension:custom:1.0:User";
addOrReplaceUPAttribute(customSchema, "myattribute");
@ -50,6 +44,40 @@ public class SchemaTest extends AbstractScimTest {
assertTrue(schemaIds.contains(customSchema));
}
@Test
public void testSchemasWithNoAttributesNotReturned() {
// Extension schemas with no user profile attributes mapped should not be returned
// (e.g., EnterpriseUser schema when no UP attributes have the kc.scim.schema.attribute annotation)
ListResponse<Schema> response = client.schemas().getAll();
assertNotNull(response);
assertNotNull(response.getResources());
List<String> schemaIds = response.getResources().stream()
.map(Schema::getId)
.toList();
// Core schemas are always present regardless of attributes
assertTrue(schemaIds.contains(Scim.USER_CORE_SCHEMA));
assertTrue(schemaIds.contains(Scim.GROUP_CORE_SCHEMA));
// EnterpriseUser schema should not be listed when no UP attributes are mapped to it
assertFalse(schemaIds.contains(Scim.ENTERPRISE_USER_SCHEMA),
"Schemas with no attributes should not be returned from the /Schemas endpoint");
// Only core schemas should be returned
assertEquals(2, response.getTotalResults());
// After adding enterprise user attributes, the schema should appear
addEnterpriseUserUserProfileAttributes();
response = client.schemas().getAll();
schemaIds = response.getResources().stream()
.map(Schema::getId)
.toList();
assertTrue(schemaIds.contains(Scim.ENTERPRISE_USER_SCHEMA),
"Schema should be returned once attributes are mapped to it");
}
@Test
public void testGetUserCoreSchema() {
Schema schema = client.schemas().get(Scim.USER_CORE_SCHEMA);
@ -212,6 +240,7 @@ public class SchemaTest extends AbstractScimTest {
assertEquals(1, nameCount, "name attribute should appear exactly once");
// Verify EnterpriseUser manager appears only once
addEnterpriseUserUserProfileAttributes();
Schema enterpriseSchema = client.schemas().get(Scim.ENTERPRISE_USER_SCHEMA);
List<String> enterpriseNames = enterpriseSchema.getAttributes().stream()
.map(Schema.Attribute::getName)
@ -247,12 +276,4 @@ public class SchemaTest extends AbstractScimTest {
assertEquals(multiValued, subAttribute.getMultiValued(), subAttribute.getName() + " sub-attribute multiValued");
assertEquals(mutability, subAttribute.getMutability(), subAttribute.getName() + " sub-attribute mutability");
}
private void addOrReplaceUPAttribute(String customSchema, String name) {
UPConfig upConfig = realm.admin().users().userProfile().getConfiguration();
UPAttribute upAttribute = new UPAttribute("scim" + name, Map.of(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, customSchema + "." + name));
upAttribute.setPermissions(new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN)));
upConfig.addOrReplaceAttribute(upAttribute);
realm.admin().users().userProfile().update(upConfig);
}
}

View file

@ -394,15 +394,9 @@ public class UserTest extends AbstractScimTest {
@Test
public void testPatchAdd() {
User expected = client.users().create(createUser());
UPConfig configuration = realm.admin().users().userProfile().getConfiguration();
configuration.addOrReplaceAttribute(new UPAttribute("middleName", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, "name.middleName")));
configuration.addOrReplaceAttribute(new UPAttribute("honorificPrefix", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, "name.honorificPrefix")));
configuration.addOrReplaceAttribute(new UPAttribute("honorificSuffix", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, "name.honorificSuffix")));
realm.admin().users().userProfile().update(configuration);
adminEvents.clear();
addOrReplaceUPAttribute("name.middleName");
addOrReplaceUPAttribute("name.honorificPrefix");
addOrReplaceUPAttribute("name.honorificSuffix");
// patch multiple attributes in a single request
client.users().patch(expected.getId(), PatchRequest.create()
@ -499,9 +493,7 @@ public class UserTest extends AbstractScimTest {
assertRootAttributes(actual, expected);
// patch an attribute from an extension schema
configuration.addOrReplaceAttribute(new UPAttribute("employeeNumber", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".employeeNumber")));
realm.admin().users().userProfile().update(configuration);
addOrReplaceUPAttribute(ENTERPRISE_USER_SCHEMA, "employeeNumber");
assertNull(actual.getEnterpriseUser());
client.users().patch(expected.getId(), PatchRequest.create()
.add(ENTERPRISE_USER_SCHEMA + ":" + "employeeNumber", "1234")
@ -537,9 +529,7 @@ public class UserTest extends AbstractScimTest {
assertEquals("321", actual.getEnterpriseUser().getEmployeeNumber());
assertEquals("Amanda", actual.getFirstName());
configuration.addOrReplaceAttribute(new UPAttribute("managerId", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".manager.value")));
realm.admin().users().userProfile().update(configuration);
addOrReplaceUPAttribute(ENTERPRISE_USER_SCHEMA, "manager.value");
// patch a sub attribute of a complex attribute using a direct path
client.users().patch(expected.getId(), PatchRequest.create()
.add("{\"name.givenName\": \"Alice\", \"" + ENTERPRISE_USER_SCHEMA + ":manager\": \"321\"}}")
@ -563,14 +553,9 @@ public class UserTest extends AbstractScimTest {
@Test
public void testPatchReplace() {
User expected = client.users().create(createUser());
UPConfig configuration = realm.admin().users().userProfile().getConfiguration();
configuration.addOrReplaceAttribute(new UPAttribute("middleName", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, "name.middleName")));
configuration.addOrReplaceAttribute(new UPAttribute("honorificPrefix", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, "name.honorificPrefix")));
configuration.addOrReplaceAttribute(new UPAttribute("honorificSuffix", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, "name.honorificSuffix")));
realm.admin().users().userProfile().update(configuration);
addOrReplaceUPAttribute("name.middleName");
addOrReplaceUPAttribute("name.honorificPrefix");
addOrReplaceUPAttribute("name.honorificSuffix");
// patch multiple attributes in a single request
client.users().patch(expected.getId(), PatchRequest.create()
@ -654,9 +639,7 @@ public class UserTest extends AbstractScimTest {
assertRootAttributes(actual, expected);
// patch an attribute from an extension schema
configuration.addOrReplaceAttribute(new UPAttribute("employeeNumber", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".employeeNumber")));
realm.admin().users().userProfile().update(configuration);
addOrReplaceUPAttribute(ENTERPRISE_USER_SCHEMA, "employeeNumber");
assertNull(actual.getEnterpriseUser());
client.users().patch(expected.getId(), PatchRequest.create()
.replace(ENTERPRISE_USER_SCHEMA + ":" + "employeeNumber", "1234")
@ -689,14 +672,9 @@ public class UserTest extends AbstractScimTest {
@Test
public void testPatchRemove() {
User expected = client.users().create(createUser());
UPConfig configuration = realm.admin().users().userProfile().getConfiguration();
configuration.addOrReplaceAttribute(new UPAttribute("middleName", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, "name.middleName")));
configuration.addOrReplaceAttribute(new UPAttribute("honorificPrefix", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, "name.honorificPrefix")));
configuration.addOrReplaceAttribute(new UPAttribute("honorificSuffix", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, "name.honorificSuffix")));
realm.admin().users().userProfile().update(configuration);
addOrReplaceUPAttribute("name.middleName");
addOrReplaceUPAttribute("name.honorificPrefix");
addOrReplaceUPAttribute("name.honorificSuffix");
// patch multiple attributes in a single request
client.users().patch(expected.getId(), PatchRequest.create()
@ -723,11 +701,8 @@ public class UserTest extends AbstractScimTest {
assertRootAttributes(actual, expected);
assertNull(actual.getEnterpriseUser());
configuration.addOrReplaceAttribute(new UPAttribute("employeeNumber", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".employeeNumber")));
configuration.addOrReplaceAttribute(new UPAttribute("costCenter", Map.of(
ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, ENTERPRISE_USER_SCHEMA + ".costCenter")));
realm.admin().users().userProfile().update(configuration);
addOrReplaceUPAttribute(ENTERPRISE_USER_SCHEMA, "employeeNumber");
addOrReplaceUPAttribute(ENTERPRISE_USER_SCHEMA, "costCenter");
client.users().patch(expected.getId(), PatchRequest.create()
.add(ENTERPRISE_USER_SCHEMA + ":" + "employeeNumber", "1234")
.add(ENTERPRISE_USER_SCHEMA + ":" + "costCenter", "5678")
@ -1089,18 +1064,11 @@ public class UserTest extends AbstractScimTest {
@Test
public void testCreateCustomAttribute() {
UPConfig upConfig = realm.admin().users().userProfile().getConfiguration();
String fooSchema = "urn:my:params:scim:schemas:extension:foo:1.0:User";
UPAttribute upAttribute = new UPAttribute("keycloak.team", Map.of(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, fooSchema + ".memberOf"));
upAttribute.setPermissions(new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN)));
upConfig.addOrReplaceAttribute(upAttribute);
realm.admin().users().userProfile().update(upConfig);
addOrReplaceUPAttribute(fooSchema, "memberOf");
String barSchema = "urn:my:params:scim:schemas:extension:bar:1.0:User";
upAttribute = new UPAttribute("keycloak.area", Map.of(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, barSchema + ".myattribute"));
upAttribute.setPermissions(new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN)));
upConfig.addOrReplaceAttribute(upAttribute);
realm.admin().users().userProfile().update(upConfig);
addOrReplaceUPAttribute(barSchema, "myattribute");
User user = new User();
@ -1146,20 +1114,13 @@ public class UserTest extends AbstractScimTest {
}
// adds a user profile attribute
UPConfig upConfig = realm.admin().users().userProfile().getConfiguration();
UPAttribute upAttribute = new UPAttribute("keycloak.team", Map.of(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, KEYCLOAK_USER_SCHEMA + ".memberOf"));
upAttribute.setPermissions(new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN)));
upConfig.addOrReplaceAttribute(upAttribute);
realm.admin().users().userProfile().update(upConfig);
UPAttribute upAttribute = addOrReplaceUPAttribute(KEYCLOAK_USER_SCHEMA, "memberOf");
existing = realm.admin().users().get(existing.getId()).toRepresentation();
existing.singleAttribute(upAttribute.getName(), "core-iam");
realm.admin().users().get(existing.getId()).update(existing);
String customSchema = "urn:my:params:scim:schemas:extension:custom:1.0:User";
upAttribute = new UPAttribute("keycloak.area", Map.of(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, customSchema + ".myattribute"));
upAttribute.setPermissions(new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN)));
upConfig.addOrReplaceAttribute(upAttribute);
realm.admin().users().userProfile().update(upConfig);
upAttribute = addOrReplaceUPAttribute(customSchema, "myattribute");
existing = realm.admin().users().get(existing.getId()).toRepresentation();
existing.singleAttribute(upAttribute.getName(), "myvalue");
realm.admin().users().get(existing.getId()).update(existing);
@ -1222,17 +1183,10 @@ public class UserTest extends AbstractScimTest {
@Test
public void testPatchCustomAttribute() {
UPConfig upConfig = realm.admin().users().userProfile().getConfiguration();
UPAttribute upAttribute = new UPAttribute("keycloak.team", Map.of(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, KEYCLOAK_USER_SCHEMA + ".memberOf"));
upAttribute.setPermissions(new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN)));
upConfig.addOrReplaceAttribute(upAttribute);
realm.admin().users().userProfile().update(upConfig);
addOrReplaceUPAttribute(KEYCLOAK_USER_SCHEMA, "memberOf");
String customSchema = "urn:my:params:scim:schemas:extension:custom:1.0:User";
upAttribute = new UPAttribute("keycloak.area", Map.of(ANNOTATION_SCIM_SCHEMA_ATTRIBUTE, customSchema + ".myattribute"));
upAttribute.setPermissions(new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN)));
upConfig.addOrReplaceAttribute(upAttribute);
realm.admin().users().userProfile().update(upConfig);
addOrReplaceUPAttribute(customSchema, "myattribute");
User user = new User();