Captain-P-Goldfish / SCIM-SDK

a scim implementation as described in RFC7643 and RFC7644
https://github.com/Captain-P-Goldfish/SCIM/wiki
BSD 3-Clause "New" or "Revised" License
122 stars 38 forks source link

Azure SCIM validator error - id attribute #515

Closed ekos2001 closed 1 year ago

ekos2001 commented 1 year ago

I was trying to validate the SCIM server with Azure SCIM validator at https://scimvalidator.microsoft.com/ to use it for Azure gallery app. I was using scim-sdk-springboot-example with slightly modified User schema. It looks like id attribute should not be included into the Schema, but should be sent with response. I was using "Discover schema" and had following result

image

When id is included into the Schema

image image

When id was removed from the Schema

image image /

Captain-P-Goldfish commented 1 year ago

This is a bit difficult. The specification says that some providers MAY include the common attributes. I intentionally built it like this for 2 reasons:

  1. It is easier to keep full control on your schema so you do not need to define attributes like externalId if you do not need it.
  2. The schemas are only registered once in the SchemaFactory class. So if we create a simple new extension-schema for the user endpoint with new custom-attributes we probably do not want to have an id or externalId and also no a meta-Attribute definition on it.

If somehow possible I would ask the Azure support to support this case based on this snippet from the specification:

For backward compatibility, some existing schema definitions MAY list
   common attributes as part of the schema.  The attribute
   characteristics (see [Section 2.2](https://datatracker.ietf.org/doc/html/rfc7643#section-2.2)) listed here SHALL take precedence
   over older definitions that may be included in existing schemas.

RFC7643 section 3.1: https://datatracker.ietf.org/doc/html/rfc7643#section-3.1

Common Attributes

   Each SCIM resource (Users, Groups, etc.) includes the following
   common attributes.  With the exception of the "ServiceProviderConfig"
   and "ResourceType" server discovery endpoints and their associated
   resources, these attributes MUST be defined for all resources,
   including any extended resource types.  When accepted by a service
   provider (e.g., after a SCIM create), the attributes "id" and "meta"
   (and its associated sub-attributes) MUST be assigned values by the
   service provider.  Common attributes are considered to be part of
   every base resource schema and do not use their own "schemas" URI.

   For backward compatibility, some existing schema definitions MAY list
   common attributes as part of the schema.  The attribute
   characteristics (see [Section 2.2](https://datatracker.ietf.org/doc/html/rfc7643#section-2.2)) listed here SHALL take precedence
   over older definitions that may be included in existing schemas.

   id
      A unique identifier for a SCIM resource as defined by the service
      provider.  Each representation of the resource MUST include a
      non-empty "id" value.  This identifier MUST be unique across the
      SCIM service provider's entire set of resources.  It MUST be a
      stable, non-reassignable identifier that does not change when the
      same resource is returned in subsequent requests.  The value of
      the "id" attribute is always issued by the service provider and
      MUST NOT be specified by the client.  The string "bulkId" is a
      reserved keyword and MUST NOT be used within any unique identifier
      value.  The attribute characteristics are "caseExact" as "true", a
      mutability of "readOnly", and a "returned" characteristic of
      "always".  See [Section 9](https://datatracker.ietf.org/doc/html/rfc7643#section-9) for additional considerations regarding
      privacy.

If that is not possible some parts of the API need to be changed in order to get this to work.

ekos2001 commented 1 year ago

Unfortunatelly, I'm not working for Azure. I can try to send a request to their support team and ask, but I think it may take a while. Could you recommend some kind of a workaround without changing of the API code?

Captain-P-Goldfish commented 1 year ago

Is this happening when Azure is reading the /Schemas endpoint? It is possible to override the endpoint if you register it with your own definition. In this case you could extend the original endpoint and manipulate the outgoing result.

Captain-P-Goldfish commented 1 year ago

you could do it like this:

resourceEndpoint.registerEndpoint(new MyCustomSchemaEndpointDefinition(resourceEndpoint.getResourceTypeFactory()));

you can see the original code-part in ResourceEndpointHandler line 107

registerEndpoint(new SchemaEndpointDefinition(resourceTypeFactory));
ekos2001 commented 1 year ago

Is this happening when Azure is reading the /Schemas endpoint?

Yes

It is possible to override the endpoint if you register it with your own definition. In this case you could extend the original endpoint and manipulate the outgoing result.

Thank you, will try

Captain-P-Goldfish commented 1 year ago

Here is a simple example how I would do it. It is important that the schema objects are getting copied!

public class CustomSchemasHandler extends SchemaHandler
{

  public CustomSchemasHandler(ResourceTypeFactory resourceTypeFactory)
  {
    super(resourceTypeFactory);
  }

  @Override
  public Schema getResource(String id,
                            List<SchemaAttribute> attributes,
                            List<SchemaAttribute> excludedAttributes,
                            Context context)
  {
    Schema registeredSchema = super.getResource(id, attributes, excludedAttributes, context);
    Schema copiedSchema = JsonHelper.copyResourceToObject(registeredSchema, Schema.class);
    return makeChangesToSchema(copiedSchema);
  }

  @Override
  public PartialListResponse<Schema> listResources(long startIndex,
                                                   int count,
                                                   FilterNode filter,
                                                   SchemaAttribute sortBy,
                                                   SortOrder sortOrder,
                                                   List<SchemaAttribute> attributes,
                                                   List<SchemaAttribute> excludedAttributes,
                                                   Context context)
  {
    PartialListResponse<Schema> originalListResponse = super.listResources(startIndex,
                                                                           count,
                                                                           filter,
                                                                           sortBy,
                                                                           sortOrder,
                                                                           attributes,
                                                                           excludedAttributes,
                                                                           context);
    List<Schema> copiedSchemas = originalListResponse.getResources()
                                                     .stream()
                                                     .map(schema -> JsonHelper.copyResourceToObject(schema,
                                                                                                    Schema.class))
                                                     .map(this::makeChangesToSchema)
                                                     .collect(Collectors.toList());

    PartialListResponse<Schema> copiedListResponse = PartialListResponse.<Schema> builder()
                                                                        .totalResults(originalListResponse.getTotalResults())
                                                                        .resources(copiedSchemas)
                                                                        .build();
    return copiedListResponse;
  }

  /**
   * TODO make adjustments to Schema
   */
  private Schema makeChangesToSchema(Schema schema)
  {
    // TODO make adjustments to Schema
    // ...
    return schema;
  }
}
ekos2001 commented 1 year ago

Thank you for your response