From 13f46fe566b3ef5c543e687d341a56a3a12ef06c Mon Sep 17 00:00:00 2001 From: Jonathan Griffin Date: Wed, 16 Apr 2025 14:56:50 +0200 Subject: [PATCH] update schemas to match more closely with rfc docs --- ee/api/routers/scim.py | 24 +- ee/api/routers/scim_constants.py | 1242 +++++++++++++++++------------- 2 files changed, 702 insertions(+), 564 deletions(-) diff --git a/ee/api/routers/scim.py b/ee/api/routers/scim.py index 1a34620a6..2787f185b 100644 --- a/ee/api/routers/scim.py +++ b/ee/api/routers/scim.py @@ -71,7 +71,7 @@ def _not_found_error_response(resource_id: str): @public_app.get("/ResourceTypes", dependencies=[Depends(auth_required)]) -async def get_resource_types(r: Request, filter_param: str | None = Query(None, alias="filter")): +async def get_resource_types(filter_param: str | None = Query(None, alias="filter")): if filter_param is not None: return JSONResponse( status_code=403, @@ -88,34 +88,18 @@ async def get_resource_types(r: Request, filter_param: str | None = Query(None, "itemsPerPage": len(RESOURCE_TYPE_IDS_TO_RESOURCE_TYPE_DETAILS), "startIndex": 1, "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"], - "Resources": [ - { - **value, - "meta": { - "location": str(r.url_for("get_resource_type", resource_id=value["id"])), - "resourceType": "ResourceType", - } - } - for value in RESOURCE_TYPE_IDS_TO_RESOURCE_TYPE_DETAILS.values() - ], + "Resources": list(RESOURCE_TYPE_IDS_TO_RESOURCE_TYPE_DETAILS.values()), }, ) @public_app.get("/ResourceTypes/{resource_id}", dependencies=[Depends(auth_required)]) -async def get_resource_type(r: Request, resource_id: str): +async def get_resource_type(resource_id: str): if resource_id not in RESOURCE_TYPE_IDS_TO_RESOURCE_TYPE_DETAILS: return _not_found_error_response(resource_id) - content = { - **RESOURCE_TYPE_IDS_TO_RESOURCE_TYPE_DETAILS[resource_id], - "meta": { - "location": str(r.url), - "resourceType": "ResourceType", - } - } return JSONResponse( status_code=200, - content=content, + content=RESOURCE_TYPE_IDS_TO_RESOURCE_TYPE_DETAILS[resource_id], ) diff --git a/ee/api/routers/scim_constants.py b/ee/api/routers/scim_constants.py index f349fc5f7..ee11cfee9 100644 --- a/ee/api/routers/scim_constants.py +++ b/ee/api/routers/scim_constants.py @@ -1,551 +1,682 @@ # note(jon): please see https://datatracker.ietf.org/doc/html/rfc7643 for details on these constants +from typing import Any + +def _attribute_characteristics( + name: str, + description: str, + type: str="string", + sub_attributes: dict[str, Any] | None=None, + # note(jon): no default for multiValued is defined in the docs and it is marked as optional. + # from our side, we'll default it to False. + multi_valued: bool=False, + required: bool=False, + canonical_values: list[str] | None=None, + case_exact: bool=False, + mutability: str="readWrite", + returned: str="default", + uniqueness: str="none", + reference_types: list[str] | None=None, +): + characteristics = { + "name": name, + "type": type, + "subAttributes": sub_attributes, + "multiValued": multi_valued, + "description": description, + "required": required, + "canonicalValues": canonical_values, + "caseExact": case_exact, + "mutability": mutability, + "returned": returned, + "uniqueness": uniqueness, + "referenceTypes": reference_types, + } + characteristics_without_none = { + key: value + for key, value in characteristics.items() + if value is not None + } + return characteristics_without_none + + +def _multi_valued_attributes(type_canonical_values: list[str], type_required: bool=False, type_mutability="readWrite"): + return [ + _attribute_characteristics( + name="type", + description="A label indicating the attribute's function.", + canonical_values=type_canonical_values, + case_exact=True, + required=type_required, + mutability=type_mutability, + ), + _attribute_characteristics( + name="primary", + type="boolean", + description="A Boolean value indicating the 'primary' or preferred attribute value for this attribute.", + ), + _attribute_characteristics( + name="display", + description="A human-readable name.", + mutability="immutable", + ), + _attribute_characteristics( + name="value", + description="The attribute's significant value.", + ), + _attribute_characteristics( + name="$ref", + type="reference", + reference_types=["uri"], + description="The reference URI of a target resource." + ), + ] + + +# note(jon): the docs are a little confusing regarding this, but +# in section 3.1 of RFC7643, it is specified that ResourceType and +# ServiceProviderConfig are not included in the common attributes. but +# in other references, they treat them as a resource. +def _common_resource_attributes(id_required: bool=True, id_uniqueness: str="none"): + return [ + _attribute_characteristics( + name="id", + description="A unique identifier for the SCIM resource.", + case_exact=True, + mutability="readOnly", + returned="always", + required=id_required, + uniqueness=id_uniqueness, + ), + _attribute_characteristics( + name="externalId", + description="A String that is an identifier for the resource as defined by the provisioning client.", + case_exact=True, + ), + _attribute_characteristics( + name="schemas", + type="reference", + reference_types=["uri"], + description="An array of Strings containing URI that are used to indicate the namespaces of the SCIM schemas that define the attributes present in the current JSON structure.", + multi_valued=True, + canonical_values=[ + "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig", + "urn:ietf:params:scim:schemas:core:2.0:ResourceType", + "urn:ietf:params:scim:schemas:core:2.0:Schema", + # todo(jon): add the user and group schem when completed + ], + case_exact=True, + mutability="readOnly", + returned="default", + required=True, + ), + _attribute_characteristics( + name="meta", + type="complex", + description="A complex attribute containing resource metadata.", + required=True, + sub_attributes=[ + _attribute_characteristics( + name="resourceType", + description="The name of the resource type of the resource.", + mutability="readOnly", + case_exact=True, + required=True, + ), + _attribute_characteristics( + name="created", + type="dateTime", + description="The 'DateTime' that the resource was added to the service provider.", + mutability="readOnly", + required=True, + ), + _attribute_characteristics( + name="lastModified", + type="dateTime", + description="The most recent DateTime that the details of this resource were updated at the service provider.", + mutability="readOnly", + required=True, + ), + _attribute_characteristics( + name="location", + type="reference", + reference_types=["uri"], + description="The URI of the resource being returned.", + mutability="readOnly", + required=True, + ), + # todo(jon): decide if we'll handle versioning. for now, we won't do it. + ], + ), + ] + + + +SERVICE_PROVIDER_CONFIG_SCHEMA = { + "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Schema"], + "id": "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig", + "name": "Service Provider Configuration", + "description": "Schema for representing the service provider's configuration.", + "meta": { + "resourceType": "Schema", + "created": "2025-04-16T14:48:00Z", + # note(jon): we might want to think about adding this resource as part of our db + # and then updating these timestamps from an api and such. for now, if we update + # the configuration, we should update the timestamp here. + "lastModified": "2025-04-16T14:48:00Z", + "location": "Schemas/urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig", + }, + "attributes": [ + *_common_resource_attributes(id_required=False), + _attribute_characteristics( + name="documentationUri", + type="reference", + reference_types=["external"], + description="An HTTP-addressable URL pointing to the service provider's human-consumable help documentation.", + mutability="readOnly", + ), + _attribute_characteristics( + name="patch", + type="complex", + description="A complex type that specifies PATCH configuration options.", + required=True, + mutability="readOnly", + sub_attributes=[ + _attribute_characteristics( + name="supported", + type="boolean", + description="A Boolean value specifying whether or not the operation is supported.", + required=True, + mutability="readOnly", + ), + ], + ), + _attribute_characteristics( + name="bulk", + type="complex", + description="A complex type that specifies bulk configuration options.", + required=True, + mutability="readOnly", + sub_attributes=[ + _attribute_characteristics( + name="supported", + type="boolean", + description="A Boolean value specifying whether or not the operation is supported.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="maxOperations", + type="integer", + description="An integer value specifying the maximum number of operations.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="maxPayloadSize", + type="integer", + description="An integer value specifying the maximum payload size in bytes.", + required=True, + mutability="readOnly", + ), + ], + ), + _attribute_characteristics( + name="filter", + type="complex", + description="A complex type that specifies FILTER options.", + required=True, + mutability="readOnly", + sub_attributes=[ + _attribute_characteristics( + name="supported", + type="boolean", + description="A Boolean value specifying whether or not the operation is supported.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="maxResults", + type="integer", + description="The integer value specifying the maximum number of resources returned in a response.", + required=True, + mutability="readOnly", + ), + ], + ), + _attribute_characteristics( + name="changePassword", + type="complex", + description="A complex type that specifies configuration options related to changing a password.", + required=True, + mutability="readOnly", + sub_attributes=[ + _attribute_characteristics( + name="supported", + type="boolean", + description="A Boolean value specifying whether or not the operation is supported.", + required=True, + mutability="readOnly", + ), + ], + ), + _attribute_characteristics( + name="sort", + type="complex", + description="A complex type that specifies sort result options.", + required=True, + mutability="readOnly", + sub_attributes=[ + _attribute_characteristics( + name="supported", + type="boolean", + description="A Boolean value specifying whether or not the operation is supported.", + required=True, + mutability="readOnly", + ), + ], + ), + _attribute_characteristics( + name="etag", + type="complex", + description="A complex type that specifies ETag configuration options.", + required=True, + mutability="readOnly", + sub_attributes=[ + _attribute_characteristics( + name="supported", + type="boolean", + description="A Boolean value specifying whether or not the operation is supported.", + required=True, + mutability="readOnly", + ), + ], + ), + _attribute_characteristics( + name="authenticationSchemes", + type="complex", + multi_valued=True, + description="A complex type that specifies supported authentication scheme properties.", + required=True, + mutability="readOnly", + sub_attributes=[ + *_multi_valued_attributes( + type_canonical_values=[ + "oauth", + "oauth2", + "oauthbearertoken", + "httpbasic", + "httpdigest", + ], + type_required=True, + type_mutability="readOnly", + ), + _attribute_characteristics( + name="name", + description="The common authentication scheme name, e.g., HTTP Basic.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="description", + description="A description of the authentication scheme.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="specUri", + type="reference", + reference_types=["external"], + description="An HTTP-addressable URL pointing to the authentication scheme's specification.", + mutability="readOnly", + ), + _attribute_characteristics( + name="documentationUri", + type="reference", + reference_types=["external"], + description="An HTTP-addressable URL pointing to the authentication scheme's usage documentation.", + mutability="readOnly", + ), + ], + ), + ] +} + + +RESOURCE_TYPE_SCHEMA = { + "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Schema"], + "id": "urn:ietf:params:scim:schemas:core:2.0:ResourceType", + "name": "Resource Type", + "description": "Specifies the schema that describes a SCIM resource type.", + "meta": { + "resourceType": "Schema", + "created": "2025-04-16T14:48:00Z", + # note(jon): we might want to think about adding this resource as part of our db + # and then updating these timestamps from an api and such. for now, if we update + # the configuration, we should update the timestamp here. + "lastModified": "2025-04-16T14:48:00Z", + "location": "Schemas/urn:ietf:params:scim:schemas:core:2.0:ResourceType", + }, + "attributes": [ + *_common_resource_attributes(id_required=False, id_uniqueness="global"), + _attribute_characteristics( + name="name", + description="The resource type name.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="description", + description="The resource type's human-readable description.", + mutability="readOnly", + ), + # todo(jon): figure out what the correct type/reference_type is here + _attribute_characteristics( + name="endpoint", + type="reference", + reference_types=["uri"], + description="The resource type's HTTP-addressable endpoint relative to the Base URL of the service provider.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="schema", + type="reference", + reference_types=["uri"], + description="The resource type's primary/base schema URI.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="schemaExtensions", + type="complex", + multi_valued=True, + description="A list of URIs of the resource type's schema extensions.", + mutability="readOnly", + sub_attributes=[ + _attribute_characteristics( + name="schema", + type="reference", + reference_types=["uri"], + description="The URI of an extended schema.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="required", + type="boolean", + description="A Boolean value that specifies whether or not the schema extension is required for the resource type.", + required=True, + mutability="readOnly", + ), + ] + ), + ] +} + +SCHEMA_SCHEMA = { + "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Schema"], + "id": "urn:ietf:params:scim:schemas:core:2.0:Schema", + "name": "Schema", + "description": "Specifies the schema that describes a SCIM schema.", + "meta": { + "resourceType": "Schema", + "created": "2025-04-16T14:48:00Z", + # note(jon): we might want to think about adding this resource as part of our db + # and then updating these timestamps from an api and such. for now, if we update + # the configuration, we should update the timestamp here. + "lastModified": "2025-04-16T14:48:00Z", + "location": "Schemas/urn:ietf:params:scim:schemas:core:2.0:Schema", + }, + "attributes": [ + *_common_resource_attributes(id_uniqueness="global"), + _attribute_characteristics( + name="name", + description="The schema's human‐readable name.", + mutability="readOnly", + ), + _attribute_characteristics( + name="description", + description="The schema's human-readable name.", + mutability="readOnly", + ), + _attribute_characteristics( + name="attributes", + type="complex", + multi_valued=True, + description="A complex attribute that defines service provider attributes and their qualities.", + required=True, + mutability="readOnly", + sub_attributes=[ + _attribute_characteristics( + name="name", + description="The attribute's name.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="type", + description="The attribute's data type.", + required=True, + canonical_values=[ + "string", + "complex", + "boolean", + "decimal", + "integer", + "dateTime", + "reference", + ], + case_exact=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="multiValued", + type="boolean", + description="A Boolean value indicating an attribute's plurality.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="description", + description="The attribute's human-readable description.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="required", + type="boolean", + description="A Boolean value indicating whether or not the attribute is required.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="canonicalValues", + multi_valued=True, + description="A collection of canonical values.", + mutability="readOnly", + ), + _attribute_characteristics( + name="caseExact", + type="boolean", + description="A Boolean that specifies whether or not a string attribute is case sensitive.", + mutability="readOnly", + ), + _attribute_characteristics( + name="mutability", + description="A single keyword indicating the circumstances under which the value of the attribute can be (re)defined.", + required=True, + mutability="readOnly", + canonical_values=[ + "readOnly", + "readWrite", + "immutable", + "writeOnly", + ], + case_exact=True, + ), + _attribute_characteristics( + name="returned", + description="A single keyword that indicates when an attribute and associated values are returned in response to a GET request or in response to a PUT, POST, or PATCH request.", + required=True, + mutability="readOnly", + canonical_values=[ + "always", + "never", + "default", + "request", + ], + case_exact=True, + ), + _attribute_characteristics( + name="uniqueness", + description="A single keyword value that specifies how the service provider enforces uniqueness of attribute values.", + required=True, + mutability="readOnly", + canonical_values=[ + "none", + "server", + "global", + ], + case_exact=True, + ), + _attribute_characteristics( + name="referenceTypes", + multi_valued=True, + description="A multi-valued array of JSON strings that indicate the SCIM resource types that may be referenced.", + mutability="readOnly", + canonical_values=[ + # todo(jon): add "User" and "Group" once those are done. + "external", + "uri" + ], + case_exact=True, + ), + _attribute_characteristics( + name="subAttributes", + type="complex", + multi_valued=True, + description="When an attribute is of type 'complex', this defines a set of sub-attributes.", + mutability="readOnly", + sub_attributes=[ + _attribute_characteristics( + name="name", + description="The attribute's name.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="type", + description="The attribute's data type.", + required=True, + canonical_values=[ + "string", + "complex", + "boolean", + "decimal", + "integer", + "dateTime", + "reference", + ], + case_exact=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="multiValued", + type="boolean", + description="A Boolean value indicating an attribute's plurality.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="description", + description="The attribute's human-readable description.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="required", + type="boolean", + description="A Boolean value indicating whether or not the attribute is required.", + required=True, + mutability="readOnly", + ), + _attribute_characteristics( + name="canonicalValues", + multi_valued=True, + description="A collection of canonical values.", + mutability="readOnly", + ), + _attribute_characteristics( + name="caseExact", + type="boolean", + description="A Boolean that specifies whether or not a string attribute is case sensitive.", + mutability="readOnly", + ), + _attribute_characteristics( + name="mutability", + description="A single keyword indicating the circumstances under which the value of the attribute can be (re)defined.", + required=True, + mutability="readOnly", + canonical_values=[ + "readOnly", + "readWrite", + "immutable", + "writeOnly", + ], + case_exact=True, + ), + _attribute_characteristics( + name="returned", + description="A single keyword that indicates when an attribute and associated values are returned in response to a GET request or in response to a PUT, POST, or PATCH request.", + required=True, + mutability="readOnly", + canonical_values=[ + "always", + "never", + "default", + "request", + ], + case_exact=True, + ), + _attribute_characteristics( + name="uniqueness", + description="A single keyword value that specifies how the service provider enforces uniqueness of attribute values.", + required=True, + mutability="readOnly", + canonical_values=[ + "none", + "server", + "global", + ], + case_exact=True, + ), + _attribute_characteristics( + name="referenceTypes", + multi_valued=True, + description="A multi-valued array of JSON strings that indicate the SCIM resource types that may be referenced.", + mutability="readOnly", + canonical_values=[ + # todo(jon): add "User" and "Group" once those are done. + "external", + "uri" + ], + case_exact=True, + ), + ], + ), + ] + ) + ] +} + -RESOURCE_TYPES = sorted( - [ - { - "schemas": ["urn:ietf:params:scim:schemas:core:2.0:ResourceType"], - "id": "User", - "name": "User", - "endpoint": "/Users", - "description": "User account", - "schema": "urn:ietf:params:scim:schemas:core:2.0:User", - } - ], - key=lambda x: x["id"], -) SCHEMAS = sorted( # todo(jon): add the user schema [ - # todo(jon): update the ResourceType schema to have the correct values - { - "id": "urn:ietf:params:scim:schemas:core:2.0:ResourceType", - "name": "ResourceType", - "description": "Represents the configuration of a SCIM resource type.", - "attributes": [ - { - "name": "id", - "type": "string", - "multiValued": False, - "description": "The resource type's unique identifier.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "global" - }, - { - "name": "name", - "type": "string", - "multiValued": False, - "description": "The resource type's human‐readable name.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "description", - "type": "string", - "multiValued": False, - "description": "A brief description of the resource type.", - "required": False, - "caseExact": False, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none" - }, - { - "name": "endpoint", - "type": "string", - "multiValued": False, - "description": "The HTTP endpoint where the resource type is exposed.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "schema", - "type": "string", - "multiValued": False, - "description": "The primary schema URN for the resource type.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "schemaExtensions", - "type": "complex", - "multiValued": True, - "description": "A list of schema extensions for the resource type.", - "required": False, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - "subAttributes": [ - { - "name": "schema", - "type": "string", - "multiValued": False, - "description": "The URN of the extension schema.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "required", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value indicating whether the extension is required.", - "required": True, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - } - ] - } - ] - }, - # todo(jon): update the Schema schema to have the correct values - { - "id": "urn:ietf:params:scim:schemas:core:2.0:Schema", - "name": "Schema", - "description": "Defines the attributes and metadata for a SCIM resource.", - "attributes": [ - { - "name": "id", - "type": "string", - "multiValued": False, - "description": "The unique identifier of the schema.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "global" - }, - { - "name": "name", - "type": "string", - "multiValued": False, - "description": "The human‐readable name of the schema.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "description", - "type": "string", - "multiValued": False, - "description": "A description of the schema.", - "required": False, - "caseExact": False, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none" - }, - { - "name": "attributes", - "type": "complex", - "multiValued": True, - "description": "The list of attributes defined by the schema.", - "required": True, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - "subAttributes": [ - { - "name": "name", - "type": "string", - "multiValued": False, - "description": "Attribute name.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "type", - "type": "string", - "multiValued": False, - "description": "Data type of the attribute.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "multiValued", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value that indicates whether the attribute is multi-valued.", - "required": True, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "required", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value that indicates whether the attribute is required.", - "required": True, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "caseExact", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value that indicates whether string comparisons are case sensitive.", - "required": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "mutability", - "type": "string", - "multiValued": False, - "description": "Defines whether the attribute is readOnly, readWrite, immutable, or writeOnly.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "returned", - "type": "string", - "multiValued": False, - "description": "Specifies when the attribute is returned in a response.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - }, - { - "name": "uniqueness", - "type": "string", - "multiValued": False, - "description": "Indicates whether the attribute must be unique.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "always", - "uniqueness": "none" - } - ] - } - ] - }, - { - "id": "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig", - "name": "Service Provider Configuration", - "description": "Schema for representing the service provider's configuration.", - "attributes": [ - { - "name": "documentationUri", - "type": "reference", - "referenceTypes": ["external"], - "multiValued": False, - "description": "An HTTP-addressable URL pointing to the service provider's human consumable help documentation.", - "required": False, - "caseExact": False, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - }, - { - "name": "patch", - "type": "complex", - "multiValued": False, - "description": "A complex type that specifies PATCH configuration options.", - "required": True, - "returned": "default", - "mutability": "readOnly", - "subAttributes": [ - { - "name": "supported", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value specifying whether or not the operation is supported.", - "required": True, - "mutability": "readOnly", - "returned": "default", - } - ] - }, - { - "name": "bulk", - "type": "complex", - "multiValued": False, - "description": "A complex type that specifies bulk configuration options.", - "required": True, - "returned": "default", - "mutability": "readOnly", - "subAttributes": [ - { - "name": "supported", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value specifying whether or not the operation is supported.", - "required": True, - "mutability": "readOnly", - "returned": "default", - }, - { - "name": "maxOperations", - "type": "integer", - "multiValued": False, - "description": "An integer value specifying the maximum number of operations.", - "required": True, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - }, - { - "name": "maxPayloadSize", - "type": "integer", - "multiValued": False, - "description": "An integer value specifying the maximum payload size in bytes.", - "required": True, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - } - ] - }, - { - "name": "filter", - "type": "complex", - "multiValued": False, - "description": "A complex type that specifies FILTER options.", - "required": True, - "returned": "default", - "mutability": "readOnly", - "subAttributes": [ - { - "name": "supported", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value specifying whether or not the operation is supported.", - "required": True, - "mutability": "readOnly", - "returned": "default", - }, - { - "name": "maxResults", - "type": "integer", - "multiValued": False, - "description": "The integer value specifying the maximum number of resources returned in a response.", - "required": True, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - } - ] - }, - { - "name": "changePassword", - "type": "complex", - "multiValued": False, - "description": "A complex type that specifies configuration options related to changing a password.", - "required": True, - "returned": "default", - "mutability": "readOnly", - "subAttributes": [ - { - "name": "supported", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value specifying whether or not the operation is supported.", - "required": True, - "mutability": "readOnly", - "returned": "default", - } - ], - }, - { - "name": "sort", - "type": "complex", - "multiValued": False, - "description": "A complex type that specifies sort result options.", - "required": True, - "returned": "default", - "mutability": "readOnly", - "subAttributes": [ - { - "name": "supported", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value specifying whether or not the operation is supported.", - "required": True, - "mutability": "readOnly", - "returned": "default", - } - ], - }, - { - "name": "etag", - "type": "complex", - "multiValued": False, - "description": "A complex type that specifies ETag configuration options.", - "required": True, - "returned": "default", - "mutability": "readOnly", - "subAttributes": [ - { - "name": "supported", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value specifying whether or not the operation is supported.", - "required": True, - "mutability": "readOnly", - "returned": "default", - } - ], - }, - { - "name": "authenticationSchemes", - "type": "complex", - "multiValued": True, - "description": "A complex type that specifies supported authentication scheme properties.", - "required": True, - "returned": "default", - "mutability": "readOnly", - "subAttributes": [ - { - "name": "type", - "type": "string", - "multiValued": False, - "description": "The authentication scheme. This specification defines the values 'oauth', 'oauth2', 'oauthbearertoken', 'httpbasic', and 'httpdigest'.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - }, - { - "name": "name", - "type": "string", - "multiValued": False, - "description": "The common authentication scheme name, e.g., HTTP Basic.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - }, - { - "name": "description", - "type": "string", - "multiValued": False, - "description": "A description of the authentication scheme.", - "required": True, - "caseExact": False, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - }, - { - "name": "specUri", - "type": "reference", - "referenceTypes": ["external"], - "multiValued": False, - "description": "An HTTP-addressable URL pointing to the authentication scheme's specification.", - "required": False, - "caseExact": False, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - }, - { - "name": "documentationUri", - "type": "reference", - "referenceTypes": ["external"], - "multiValued": False, - "description": "An HTTP-addressable URL pointing to the authentication scheme's usage documentation.", - "required": False, - "caseExact": False, - "mutability": "readOnly", - "returned": "default", - "uniqueness": "none", - }, - { - "name": "primary", - "type": "boolean", - "multiValued": False, - "description": "A Boolean value specifying whether or not the attribute is the preferred attribute value.", - "required": True, - "mutability": "readOnly", - "returned": "default", - } - ] - }, - { - "name": "meta", - "type": "complex", - "multiValued": True, - "description": "A complex type that specifies resource metadata.", - "required": True, - "returned": "default", - "mutability": "readOnly", - "subAttributes": [ - { - "name": "resourceType", - "type": "string", - "multiValued": False, - "description": "The name of the resource type of the resource.", - "required": True, - "caseExact": True, - "mutability": "readOnly", - "returned": "default", - }, - { - "name": "created", - "type": "datetime", - "multiValued": False, - "description": " The 'DateTime' that the resource was added to the service provider.", - "required": True, - "mutability": "readOnly", - "returned": "default", - }, - { - "name": "lastModified", - "type": "datetime", - "multiValued": False, - "description": "The most recent DateTime that the details of this resource were updated at the service provider.", - "required": True, - "mutability": "readOnly", - "returned": "default", - }, - { - "name": "location", - "type": "reference", - "referenceTypes": ["ServiceProviderConfig"], - "multiValued": False, - "description": "The URI of the resource being returned.", - "required": True, - "mutability": "readOnly", - "returned": "default", - }, - ] - }, - ] - }, + SERVICE_PROVIDER_CONFIG_SCHEMA, + RESOURCE_TYPE_SCHEMA, + SCHEMA_SCHEMA, ], key=lambda x: x["id"], ) @@ -588,11 +719,34 @@ SERVICE_PROVIDER_CONFIG = { ], "meta": { "resourceType": "ServiceProviderConfig", - "created": "2025-04-15T15:45", + "created": "2025-04-15T15:45:00Z", # note(jon): we might want to think about adding this resource as part of our db # and then updating these timestamps from an api and such. for now, if we update # the configuration, we should update the timestamp here. - "lastModified": "2025-04-15T15:45", + "lastModified": "2025-04-15T15:45:00Z", "location": "", # note(jon): this field will be computed in the /ServiceProviderConfig endpoint }, } + +RESOURCE_TYPES = sorted( + [ + { + "schemas": ["urn:ietf:params:scim:schemas:core:2.0:ResourceType"], + "id": "User", + "name": "User", + "endpoint": "/Users", + "description": "User account", + "schema": "urn:ietf:params:scim:schemas:core:2.0:User", + "meta": { + "resourceType": "ResourceType", + "created": "2025-04-16T08:37:00Z", + # note(jon): we might want to think about adding this resource as part of our db + # and then updating these timestamps from an api and such. for now, if we update + # the configuration, we should update the timestamp here. + "lastModified": "2025-04-16T08:37:00Z", + "location": "ResourceType/User", + }, + } + ], + key=lambda x: x["id"], +)