From 74731e9e40a5ae94c73faa1068e49f2d25f10f05 Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Tue, 4 Aug 2026 16:30:29 -0400 Subject: [PATCH] fix: smaller scope of an initial refactoring to bridge scim and admin v2 closes: #51326 Signed-off-by: Steve Hawkins --- .../client/ScimBackedClientService.java | 47 +++-- .../client/scim/BaseClientModelSchema.java | 165 ++++++++++++++++++ .../client/scim/ClientJpaQueryExecutor.java | 8 +- .../client/scim/ClientJpaQueryProvider.java | 65 ------- .../client/scim/ClientJpaQuerySchema.java | 101 ----------- .../scim/ClientQueryRepresentation.java | 10 -- .../client/scim/OIDCClientModelSchema.java | 20 +++ .../client/scim/SAMLClientModelSchema.java | 20 +++ .../scim/resource/schema/ModelSchema.java | 3 +- .../resource/schema/attribute/Attribute.java | 9 +- .../schema/attribute/AttributeMapper.java | 3 +- .../attribute/ComplexAttributeSetter.java | 3 +- .../java/org/keycloak/models/ClientModel.java | 2 +- 13 files changed, 247 insertions(+), 209 deletions(-) create mode 100644 rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/BaseClientModelSchema.java delete mode 100644 rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQueryProvider.java delete mode 100644 rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQuerySchema.java delete mode 100644 rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientQueryRepresentation.java create mode 100644 rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/OIDCClientModelSchema.java create mode 100644 rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/SAMLClientModelSchema.java diff --git a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/ScimBackedClientService.java b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/ScimBackedClientService.java index bf0040be5a9c..30feb0ed76de 100644 --- a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/ScimBackedClientService.java +++ b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/ScimBackedClientService.java @@ -1,6 +1,8 @@ package org.keycloak.services.client; import java.io.InputStream; +import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -10,11 +12,13 @@ import jakarta.ws.rs.core.Response.Status; import org.keycloak.authorization.fgap.AdminPermissionsSchema; +import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelException; import org.keycloak.models.RealmModel; -import org.keycloak.models.mapper.ClientModelMappers; import org.keycloak.representations.admin.v2.BaseClientRepresentation; +import org.keycloak.representations.admin.v2.OIDCClientRepresentation; +import org.keycloak.representations.admin.v2.SAMLClientRepresentation; import org.keycloak.scim.filter.ScimFilterException; import org.keycloak.scim.filter.ScimFilterParser.FilterContext; import org.keycloak.services.PatchType; @@ -22,14 +26,18 @@ import org.keycloak.services.client.query.ClientQueryException; import org.keycloak.services.client.query.QueryFieldExtractor; import org.keycloak.services.client.query.QueryParseUtils; +import org.keycloak.services.client.scim.BaseClientModelSchema; import org.keycloak.services.client.scim.ClientJpaQueryExecutor; -import org.keycloak.services.client.scim.ClientJpaQuerySchema; +import org.keycloak.services.client.scim.OIDCClientModelSchema; +import org.keycloak.services.client.scim.SAMLClientModelSchema; import org.keycloak.services.resources.admin.fgap.AdminPermissionEvaluator; import org.keycloak.utils.StringUtil; public class ScimBackedClientService implements ClientService { - private static final ClientModelMappers MAPPERS = new ClientModelMappers(); + private static final Map> SCHEMAS = Map.of( + OIDCClientRepresentation.PROTOCOL, OIDCClientModelSchema.INSTANCE, + SAMLClientRepresentation.PROTOCOL, SAMLClientModelSchema.INSTANCE); private final KeycloakSession session; private final AdminPermissionEvaluator permissions; @@ -70,12 +78,18 @@ public Stream getClients(RealmModel realm, QueryParseUtils.validate(filterContext); } + Set includeFields = projectionOptions.getFields(); + List includeList = includeFields.isEmpty() ? null : includeFields.stream().toList(); Stream stream = ClientJpaQueryExecutor.findClients( session, realm, filterContext, sortAndSliceOptions.getSortOptions(), offset, limit) - .map(client -> delegate.getMapper(client.getProtocol()).fromModel(client)) + .map(client -> { + BaseClientModelSchema schema = SCHEMAS.get(client.getProtocol()); + if (schema == null) return null; + return populateFromSchema(schema, client, includeList); + }) .filter(Objects::nonNull); - return applyProjection(stream, projectionOptions); + return stream; } catch (ClientQueryException | ScimFilterException e) { throw new ServiceException(e.getMessage(), Status.BAD_REQUEST); } catch (ModelException e) { @@ -97,31 +111,28 @@ private boolean canUseJpaQuery(RealmModel realm, ClientSearchOptions searchOptio try { var filterContext = QueryParseUtils.parse(searchOptions.query()); Set queryFields = QueryFieldExtractor.extractFields(filterContext); - return ClientJpaQuerySchema.JPA_FIELDS.containsAll(queryFields); + return BaseClientModelSchema.JPA_FIELDS.containsAll(queryFields); } catch (ClientQueryException e) { return false; } } + private static R populateFromSchema( + BaseClientModelSchema schema, ClientModel client, List includeFields) { + R rep = schema.createRepresentation(); + schema.populate(rep, client, includeFields, null); + return rep; + } + + // TODO: still need to have well defined handling for polymorphic fields private void validateProjectionFields(ClientProjectionOptions projectionOptions) { projectionOptions.getFields().forEach(field -> { - if (!MAPPERS.isKnownField(field)) { + if (SCHEMAS.values().stream().noneMatch(s -> s.getAttributes().containsKey(field))) { throw new ServiceException("%s is an unknown field".formatted(field), Status.BAD_REQUEST); } }); } - private Stream applyProjection(Stream stream, - ClientProjectionOptions projectionOptions) { - if (projectionOptions.getFields().isEmpty()) { - return stream; - } - return stream.map(rep -> { - MAPPERS.applyProjection(rep, projectionOptions.getFields()); - return rep; - }); - } - @Override public Stream deleteClients(RealmModel realm, ClientSearchOptions searchOptions) { return delegate.deleteClients(realm, searchOptions); diff --git a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/BaseClientModelSchema.java b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/BaseClientModelSchema.java new file mode 100644 index 000000000000..10be14fc3676 --- /dev/null +++ b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/BaseClientModelSchema.java @@ -0,0 +1,165 @@ +package org.keycloak.services.client.scim; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; + +import org.keycloak.common.util.TriConsumer; +import org.keycloak.models.ClientModel; +import org.keycloak.models.ModelValidationException; +import org.keycloak.representations.admin.v2.BaseClientRepresentation; +import org.keycloak.scim.resource.schema.ModelSchema; +import org.keycloak.scim.resource.schema.attribute.Attribute; + +/** + * Abstract schema for client models. Defines the 8 shared JPA-queryable fields as + * {@link Attribute} objects and provides attribute-filtered population from a {@link ClientModel}. + * + * @param the representation type, must extend {@link BaseClientRepresentation} + */ +public abstract class BaseClientModelSchema + implements ModelSchema { + + public static final Set JPA_FIELDS = Set.of( + "clientId", "enabled", "description", "displayName", + "protocol", "appUrl", "createdTimestamp", "updatedTimestamp"); + + private final Map> attributes; + + protected BaseClientModelSchema() { + Map> map = new LinkedHashMap<>(); + map.put("clientId", stringAttr("clientId", "clientId", BaseClientRepresentation::setClientId, ClientModel::setClientId)); + map.put("enabled", boolAttr ("enabled", "enabled", BaseClientRepresentation::setEnabled, (model, v) -> model.setEnabled(Boolean.TRUE.equals(v)))); + map.put("description", stringAttr("description", "description", BaseClientRepresentation::setDescription, ClientModel::setDescription)); + map.put("displayName", stringAttr("displayName", "name", BaseClientRepresentation::setDisplayName, ClientModel::setName)); + map.put("protocol", stringAttr("protocol", "protocol", BaseClientRepresentation::setProtocol, ClientModel::setProtocol)); + map.put("appUrl", stringAttr("appUrl", "baseUrl", BaseClientRepresentation::setAppUrl, ClientModel::setBaseUrl)); + map.put("createdTimestamp", longAttr ("createdTimestamp", "createdTimestamp", BaseClientRepresentation::setCreatedTimestamp, null)); // read-only + map.put("updatedTimestamp", longAttr ("updatedTimestamp", "lastModifiedTimestamp", BaseClientRepresentation::setUpdatedTimestamp, null)); // read-only + this.attributes = Map.copyOf(map); + } + + @SuppressWarnings("unchecked") + private Attribute stringAttr(String name, String entityField, + BiConsumer repSetter, + BiConsumer modelSetter) { + return Attribute.simple(name) + .modelAttributeResolver(a -> entityField) + .withModelSetter( + modelSetter != null ? (TriConsumer) (model, n, v) -> modelSetter.accept(model, v) : null, + (BiConsumer) (rep, v) -> repSetter.accept(rep, v)) + .build() + .get(0); + } + + @SuppressWarnings("unchecked") + private Attribute boolAttr(String name, String entityField, + BiConsumer repSetter, + BiConsumer modelSetter) { + return Attribute.simple(name) + .modelAttributeResolver(a -> entityField) + .bool() + .withModelSetter( + modelSetter != null ? (TriConsumer) (model, n, v) -> modelSetter.accept(model, v) : null, + (BiConsumer) (rep, v) -> repSetter.accept(rep, v)) + .build() + .get(0); + } + + @SuppressWarnings("unchecked") + private Attribute longAttr(String name, String entityField, + BiConsumer repSetter, + BiConsumer modelSetter) { + return Attribute.simple(name) + .modelAttributeResolver(a -> entityField) + .timestamp() + .withModelSetter( + modelSetter != null ? (TriConsumer) (model, n, v) -> modelSetter.accept(model, v) : null, + (BiConsumer) (rep, v) -> repSetter.accept(rep, v)) + .build() + .get(0); + } + + @Override + public Map> getAttributes() { + return attributes; + } + + @Override + public Attribute getAttributeByPath(String path) { + return attributes.get(path); + } + + /** + * Populates {@code representation} with fields from {@code model}, honouring inclusion/exclusion filters. + * Mirrors {@code AbstractModelSchema.populateResourceType} but without {@code setId}/{@code addSchema} calls. + */ + @Override + public void populate(R representation, ClientModel model, List attributes, List excludedAttributes) { + for (Attribute attribute : this.attributes.values()) { + if (attribute.isExcluded(this, attributes, excludedAttributes)) { + continue; + } + Object value = getAttributeValue(model, attribute.getModelAttributeName()); + attribute.set(representation, value); + } + } + + /** + * Returns the value of the named model attribute (using the entity-column name, not the schema name). + */ + protected Object getAttributeValue(ClientModel model, String name) { + return switch (name) { + case "clientId" -> model.getClientId(); + case "enabled" -> model.isEnabled(); + case "description" -> model.getDescription(); + case "name" -> model.getName(); + case "protocol" -> model.getProtocol(); + case "baseUrl" -> model.getBaseUrl(); + case "createdTimestamp" -> model.getCreatedTimestamp(); + case "lastModifiedTimestamp" -> model.getLastModifiedTimestamp(); + default -> null; + }; + } + + /** Factory method — subclasses return a fresh, empty representation instance. */ + public abstract R createRepresentation(); + + // ---- Methods not needed for query/projection use ---- + + /** + * Populates {@code model} from {@code representation} by calling the model-setter side of each attribute. + * Read-only attributes (createdTimestamp, updatedTimestamp) are silently skipped. + */ + @Override + public void populate(ClientModel model, R representation) { + throw new UnsupportedOperationException("populate(ClientModel, R) not yet implemented"); + } + + @Override + public void populate(R representation, ClientModel model) { + throw new UnsupportedOperationException("populate(R, ClientModel) is not supported — use populate(R, ClientModel, List, List) instead"); + } + + @Override + public void validate(R representation) throws ModelValidationException { + throw new UnsupportedOperationException("validate is not supported"); + } + + @Override + public String getId() { + return ""; // anonymous + } + + @Override + public String getName() { + throw new UnsupportedOperationException("not needed for v2"); + } + + @Override + public String getDescription() { + throw new UnsupportedOperationException("not needed for v2"); + } +} diff --git a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQueryExecutor.java b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQueryExecutor.java index 16f3626a450f..046c4380d781 100644 --- a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQueryExecutor.java +++ b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQueryExecutor.java @@ -27,7 +27,9 @@ public final class ClientJpaQueryExecutor { - private static final ClientJpaQueryProvider QUERY_PROVIDER = new ClientJpaQueryProvider(); + private static final List SCHEMAS = List.of( + OIDCClientModelSchema.INSTANCE, + SAMLClientModelSchema.INSTANCE); private ClientJpaQueryExecutor() { } @@ -50,14 +52,14 @@ public static Stream findClients(KeycloakSession session, RealmMode session, AdminPermissionsSchema.CLIENTS, realm, cb, query, root)); ScimJPAPredicateEvaluator evaluator = new ScimJPAPredicateEvaluator( - QUERY_PROVIDER, ClientJpaQuerySchema.SCHEMAS, cb, root); + null, SCHEMAS, cb, root); if (filterContext != null) { predicates.add(evaluator.visit(filterContext).predicate()); } var q = query.where(predicates.toArray(Predicate[]::new)); var orders = new ArrayList<>(sortOptions.stream().map(sortOption -> { - var field = ClientJpaQuerySchema.INSTANCE.getAttributeByPath(sortOption.field().toQueryValue()) + var field = OIDCClientModelSchema.INSTANCE.getAttributeByPath(sortOption.field().toQueryValue()) .getModelAttributeName(); return sortOption.isAscending() ? cb.asc(root.get(field)) : cb.desc(root.get(field)); }).toList()); diff --git a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQueryProvider.java b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQueryProvider.java deleted file mode 100644 index 3bc86bd66562..000000000000 --- a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQueryProvider.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.keycloak.services.client.scim; - -import java.util.List; -import java.util.stream.Stream; - -import org.keycloak.models.Model; -import org.keycloak.scim.protocol.request.SearchRequest; -import org.keycloak.scim.resource.schema.ModelSchema; -import org.keycloak.scim.resource.spi.ScimResourceTypeProvider; - -/** - * Minimal {@link ScimResourceTypeProvider} adapter so {@link org.keycloak.scim.model.filter.ScimJPAPredicateEvaluator} - * can resolve client query attributes. SCIM REST operations are not supported. - */ -public class ClientJpaQueryProvider implements ScimResourceTypeProvider { - - @Override - public String getSchema() { - return ClientJpaQuerySchema.INSTANCE.getId(); - } - - @Override - public List> getSchemas() { - return ClientJpaQuerySchema.SCHEMAS; - } - - @Override - public Class getResourceType() { - return ClientQueryRepresentation.class; - } - - @Override - public ClientQueryRepresentation create(ClientQueryRepresentation resource) { - throw new UnsupportedOperationException(); - } - - @Override - public ClientQueryRepresentation update(ClientQueryRepresentation resource) { - throw new UnsupportedOperationException(); - } - - @Override - public ClientQueryRepresentation get(String id) { - throw new UnsupportedOperationException(); - } - - @Override - public Stream getAll(SearchRequest searchRequest) { - throw new UnsupportedOperationException(); - } - - @Override - public Long count(SearchRequest searchRequest) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean delete(String id) { - throw new UnsupportedOperationException(); - } - - @Override - public void close() { - } -} diff --git a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQuerySchema.java b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQuerySchema.java deleted file mode 100644 index 771963bfff6d..000000000000 --- a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientJpaQuerySchema.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.keycloak.services.client.scim; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.keycloak.models.Model; -import org.keycloak.models.ModelValidationException; -import org.keycloak.scim.resource.schema.ModelSchema; -import org.keycloak.scim.resource.schema.attribute.Attribute; - -public final class ClientJpaQuerySchema implements ModelSchema { - - public static final ClientJpaQuerySchema INSTANCE = new ClientJpaQuerySchema(); - - public static final Set JPA_FIELDS = Set.of( - "clientId", "enabled", "description", "displayName", "protocol", "appUrl", "createdTimestamp", "updatedTimestamp"); - - public static final List> SCHEMAS = List.of(INSTANCE); - - private static final String SCHEMA_ID = "urn:keycloak:client-v2:query"; - - private final Map> attributes; - - private ClientJpaQuerySchema() { - Map> map = new LinkedHashMap<>(); - map.put("clientId", stringAttribute("clientId", "clientId")); - map.put("enabled", booleanAttribute("enabled", "enabled")); - map.put("description", stringAttribute("description", "description")); - map.put("displayName", stringAttribute("displayName", "name")); - map.put("protocol", stringAttribute("protocol", "protocol")); - map.put("appUrl", stringAttribute("appUrl", "baseUrl")); - map.put("createdTimestamp", timeAttribute("createdTimestamp", "createdTimestamp")); - map.put("updatedTimestamp", timeAttribute("updatedTimestamp", "lastModifiedTimestamp")); - this.attributes = Map.copyOf(map); - } - - private static Attribute stringAttribute(String queryField, String entityField) { - return Attribute.simple(queryField) - .modelAttributeResolver(attribute -> entityField) - .build() - .get(0); - } - - private static Attribute booleanAttribute(String queryField, String entityField) { - return Attribute.simple(queryField) - .modelAttributeResolver(attribute -> entityField) - .bool() - .build() - .get(0); - } - - private static Attribute timeAttribute(String queryField, String entityField) { - return Attribute.simple(queryField) - .modelAttributeResolver(attribute -> entityField) - .timestamp() - .build() - .get(0); - } - - @Override - public String getId() { - return SCHEMA_ID; - } - - @Override - public String getName() { - return "ClientQuery"; - } - - @Override - public String getDescription() { - return "JPA-queryable client fields for Admin API v2"; - } - - @Override - public Map> getAttributes() { - return attributes; - } - - @Override - public void populate(Model model, ClientQueryRepresentation representation) { - throw new UnsupportedOperationException("Query schema does not support population"); - } - - @Override - public void populate(ClientQueryRepresentation representation, Model model) { - throw new UnsupportedOperationException("Query schema does not support population"); - } - - @Override - public void validate(ClientQueryRepresentation representation) throws ModelValidationException { - throw new UnsupportedOperationException("Query schema does not support validation"); - } - - @Override - public Attribute getAttributeByPath(String path) { - return attributes.get(path); - } -} diff --git a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientQueryRepresentation.java b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientQueryRepresentation.java deleted file mode 100644 index 468e9d147496..000000000000 --- a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/ClientQueryRepresentation.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.keycloak.services.client.scim; - -import org.keycloak.scim.resource.ResourceTypeRepresentation; - -/** - * Placeholder SCIM resource type for {@link ClientJpaQuerySchema} attribute metadata. - * Not used for REST responses. - */ -public class ClientQueryRepresentation extends ResourceTypeRepresentation { -} diff --git a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/OIDCClientModelSchema.java b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/OIDCClientModelSchema.java new file mode 100644 index 000000000000..eecf9631c3f7 --- /dev/null +++ b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/OIDCClientModelSchema.java @@ -0,0 +1,20 @@ +package org.keycloak.services.client.scim; + +import org.keycloak.representations.admin.v2.OIDCClientRepresentation; + +/** + * Schema singleton for OIDC clients. Defines the 8 JPA-queryable fields and + * provides attribute-filtered population of {@link OIDCClientRepresentation}. + */ +public final class OIDCClientModelSchema extends BaseClientModelSchema { + + public static final OIDCClientModelSchema INSTANCE = new OIDCClientModelSchema(); + + private OIDCClientModelSchema() { + } + + @Override + public OIDCClientRepresentation createRepresentation() { + return new OIDCClientRepresentation(); + } +} diff --git a/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/SAMLClientModelSchema.java b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/SAMLClientModelSchema.java new file mode 100644 index 000000000000..327bec6b9ad3 --- /dev/null +++ b/rest/admin-v2/services/src/main/java/org/keycloak/services/client/scim/SAMLClientModelSchema.java @@ -0,0 +1,20 @@ +package org.keycloak.services.client.scim; + +import org.keycloak.representations.admin.v2.SAMLClientRepresentation; + +/** + * Schema singleton for SAML clients. Defines the 8 JPA-queryable fields and + * provides attribute-filtered population of {@link SAMLClientRepresentation}. + */ +public final class SAMLClientModelSchema extends BaseClientModelSchema { + + public static final SAMLClientModelSchema INSTANCE = new SAMLClientModelSchema(); + + private SAMLClientModelSchema() { + } + + @Override + public SAMLClientRepresentation createRepresentation() { + return new SAMLClientRepresentation(); + } +} diff --git a/scim/core/src/main/java/org/keycloak/scim/resource/schema/ModelSchema.java b/scim/core/src/main/java/org/keycloak/scim/resource/schema/ModelSchema.java index 23a0987f25fd..0643ece775c0 100644 --- a/scim/core/src/main/java/org/keycloak/scim/resource/schema/ModelSchema.java +++ b/scim/core/src/main/java/org/keycloak/scim/resource/schema/ModelSchema.java @@ -6,7 +6,6 @@ import org.keycloak.models.Model; import org.keycloak.models.ModelValidationException; -import org.keycloak.scim.resource.ResourceTypeRepresentation; import org.keycloak.scim.resource.schema.attribute.Attribute; import com.fasterxml.jackson.databind.JsonNode; @@ -20,7 +19,7 @@ * the representation of a resource type and to validate and map the attributes from the schema from a {@link ResourceTypeRepresentation}, usually an * object from the RESTful layer, to a {@link Model} and vice versa. */ -public interface ModelSchema { +public interface ModelSchema { /** * The id of the schema. It is used to identify the schema and to associate it with a resource type. diff --git a/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/Attribute.java b/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/Attribute.java index 556bf7ce5be5..0262cfc0104c 100644 --- a/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/Attribute.java +++ b/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/Attribute.java @@ -9,7 +9,6 @@ import org.keycloak.common.util.TriConsumer; import org.keycloak.models.Model; -import org.keycloak.scim.resource.ResourceTypeRepresentation; import org.keycloak.scim.resource.schema.ModelSchema; import com.fasterxml.jackson.databind.JsonNode; @@ -23,7 +22,7 @@ * * @see ModelSchema */ -public class Attribute { +public class Attribute { public static final String RETURNED_ALWAYS = "always"; public static final String RETURNED_DEFAULT = "default"; @@ -74,7 +73,7 @@ public static String getSimpleName(String name) { * @param name the name of the attribute from the {@link R} representation. It should be a simple attribute, meaning that it is not a complex attribute and does not have sub-attributes. * @return the builder */ - public static Builder simple(String name) { + public static Builder simple(String name) { return (Builder) new Builder<>(name, null).string(); } @@ -87,7 +86,7 @@ public static Builder Builder complex(String name, Class complexType) { + public static Builder complex(String name, Class complexType) { Builder builder = new Builder<>(name, complexType); builder.type = "complex"; return builder; @@ -328,7 +327,7 @@ public boolean isExtension() { return getName().contains(":"); } - public static class Builder { + public static class Builder { private final Class complexType; private final String name; diff --git a/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/AttributeMapper.java b/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/AttributeMapper.java index af2aec34feee..71d39149f774 100644 --- a/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/AttributeMapper.java +++ b/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/AttributeMapper.java @@ -7,7 +7,6 @@ import org.keycloak.common.util.TriConsumer; import org.keycloak.models.Model; -import org.keycloak.scim.resource.ResourceTypeRepresentation; import org.keycloak.util.JsonSerialization; import com.fasterxml.jackson.databind.JsonNode; @@ -17,7 +16,7 @@ * * @see Attribute */ -public class AttributeMapper { +public class AttributeMapper { private Attribute attribute; private final TriConsumer modelSetter; diff --git a/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/ComplexAttributeSetter.java b/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/ComplexAttributeSetter.java index 7b4c5482ee91..0c60048cf7ff 100644 --- a/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/ComplexAttributeSetter.java +++ b/scim/core/src/main/java/org/keycloak/scim/resource/schema/attribute/ComplexAttributeSetter.java @@ -8,10 +8,9 @@ import org.keycloak.common.util.TriConsumer; import org.keycloak.models.Model; -import org.keycloak.scim.resource.ResourceTypeRepresentation; import org.keycloak.scim.resource.common.MultiValuedAttribute; -public class ComplexAttributeSetter implements TriConsumer, R, String> { +public class ComplexAttributeSetter implements TriConsumer, R, String> { private final String name; private final String subName; diff --git a/server-spi/src/main/java/org/keycloak/models/ClientModel.java b/server-spi/src/main/java/org/keycloak/models/ClientModel.java index e2f4dad5284f..9254465148ad 100755 --- a/server-spi/src/main/java/org/keycloak/models/ClientModel.java +++ b/server-spi/src/main/java/org/keycloak/models/ClientModel.java @@ -29,7 +29,7 @@ * @author Bill Burke * @version $Revision: 1 $ */ -public interface ClientModel extends ClientScopeModel, RoleContainerModel, ProtocolMapperContainerModel, ScopeContainerModel { +public interface ClientModel extends ClientScopeModel, RoleContainerModel, ProtocolMapperContainerModel, ScopeContainerModel, Model { // COMMON ATTRIBUTES