clean up files
This commit is contained in:
parent
464b9b1b47
commit
62736ff29b
9 changed files with 1239 additions and 925 deletions
70
ee/api/routers/fixtures/group_schema.json
Normal file
70
ee/api/routers/fixtures/group_schema.json
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"id": "urn:ietf:params:scim:schemas:core:2.0:Group",
|
||||
"name": "Group",
|
||||
"description": "Group",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "displayName",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Human readable name for the Group. REQUIRED.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "members",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "A list of members of the Group.",
|
||||
"required": false,
|
||||
"subAttributes": [
|
||||
{
|
||||
"name": "value",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Identifier of the member of this Group.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "immutable",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "$ref",
|
||||
"type": "reference",
|
||||
"referenceTypes": ["User", "Group"],
|
||||
"multiValued": false,
|
||||
"description": "The URI of the corresponding member resource of this Group.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "immutable",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "A label indicating the type of resource; e.g., 'User' or 'Group'.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"canonicalValues": ["User", "Group"],
|
||||
"mutability": "immutable",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
}
|
||||
],
|
||||
"mutability": "readWrite",
|
||||
"returned": "default"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"resourceType": "Schema",
|
||||
"location": "/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:Group",
|
||||
"created": "2025-04-17T15:48:00",
|
||||
"lastModified": "2025-04-17T15:48:00"
|
||||
}
|
||||
}
|
||||
19
ee/api/routers/fixtures/resource_type.json
Normal file
19
ee/api/routers/fixtures/resource_type.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[
|
||||
{
|
||||
"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",
|
||||
"lastModified": "2025-04-16T08:37:00Z",
|
||||
"location": "ResourceType/User"
|
||||
},
|
||||
"schemaExtensions": []
|
||||
}
|
||||
]
|
||||
102
ee/api/routers/fixtures/resource_type_schema.json
Normal file
102
ee/api/routers/fixtures/resource_type_schema.json
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
{
|
||||
"id": "urn:ietf:params:scim:schemas:core:2.0:ResourceType",
|
||||
"name": "ResourceType",
|
||||
"description": "Specifies the schema that describes a SCIM Resource Type",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The resource type's server unique id. May be the same as the 'name' attribute.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The resource type name.",
|
||||
"required": true,
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The resource type's human readable description.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "endpoint",
|
||||
"type": "reference",
|
||||
"referenceTypes": ["uri"],
|
||||
"multiValued": false,
|
||||
"description": "The resource type's HTTP addressable endpoint relative to the Base URL; e.g., /Users.",
|
||||
"required": true,
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "schema",
|
||||
"type": "reference",
|
||||
"referenceTypes": ["uri"],
|
||||
"multiValued": false,
|
||||
"description": "The resource types primary/base schema URI.",
|
||||
"required": true,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "schemaExtensions",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "A list of URIs of the resource type's schema extensions",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{
|
||||
"name": "schema",
|
||||
"type": "reference",
|
||||
"referenceTypes": ["uri"],
|
||||
"multiValued": false,
|
||||
"description": "The URI of a schema extension.",
|
||||
"required": true,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "required",
|
||||
"type": "boolean",
|
||||
"multiValued": false,
|
||||
"description": "Specifies whether the schema extension is required for the resource type.",
|
||||
"required": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"resourceType": "Schema",
|
||||
"location": "/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:ResourceType",
|
||||
"created": "2025-04-17T15:48:00",
|
||||
"lastModified": "2025-04-17T15:48:00"
|
||||
}
|
||||
}
|
||||
304
ee/api/routers/fixtures/schema_schema.json
Normal file
304
ee/api/routers/fixtures/schema_schema.json
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
{
|
||||
"id": "urn:ietf:params:scim:schemas:core:2.0:Schema",
|
||||
"name": "Schema",
|
||||
"description": "Specifies the schema that describes a SCIM Schema",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The unique URI of the schema.",
|
||||
"required": true,
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The schema's human readable name.",
|
||||
"required": true,
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The schema's human readable description.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "attributes",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "A complex attribute that includes the attributes of a schema",
|
||||
"required": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The attribute's name",
|
||||
"required": true,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The attribute's data type.",
|
||||
"required": true,
|
||||
"canonicalValues": ["string","complex","boolean","decimal","integer","dateTime","reference"],
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "multiValued",
|
||||
"type": "boolean",
|
||||
"multiValued": false,
|
||||
"description": "Boolean indicating an attribute's plurality.",
|
||||
"required": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "A human readable description of the attribute.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "required",
|
||||
"type": "boolean",
|
||||
"multiValued": false,
|
||||
"description": "A boolean indicating if the attribute is required.",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "canonicalValues",
|
||||
"type": "string",
|
||||
"multiValued": true,
|
||||
"description": "A collection of canonical values.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "caseExact",
|
||||
"type": "boolean",
|
||||
"multiValued": false,
|
||||
"description": "Indicates if a string attribute is case-sensitive.",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "mutability",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Indicates if an attribute is modifiable.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none",
|
||||
"canonicalValues": ["readOnly","readWrite","immutable","writeOnly"]
|
||||
},
|
||||
{
|
||||
"name": "returned",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Indicates when an attribute is returned in a response.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none",
|
||||
"canonicalValues": ["always","never","default","request"]
|
||||
},
|
||||
{
|
||||
"name": "uniqueness",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Indicates how unique a value must be.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none",
|
||||
"canonicalValues": ["none","server","global"]
|
||||
},
|
||||
{
|
||||
"name": "referenceTypes",
|
||||
"type": "string",
|
||||
"multiValued": true,
|
||||
"description": "Specifies a resourceType that a reference attribute may refer to.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "subAttributes",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "Used to define the sub-attributes of a complex attribute",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The sub-attribute's name",
|
||||
"required": true,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The sub-attribute's data type.",
|
||||
"required": true,
|
||||
"canonicalValues": ["string","complex","boolean","decimal","integer","dateTime","reference"],
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "multiValued",
|
||||
"type": "boolean",
|
||||
"multiValued": false,
|
||||
"description": "Boolean indicating sub-attribute plurality.",
|
||||
"required": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Human readable description of the sub-attribute.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "required",
|
||||
"type": "boolean",
|
||||
"multiValued": false,
|
||||
"description": "Whether the sub-attribute is required.",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "canonicalValues",
|
||||
"type": "string",
|
||||
"multiValued": true,
|
||||
"description": "A collection of canonical values for the sub-attribute.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "caseExact",
|
||||
"type": "boolean",
|
||||
"multiValued": false,
|
||||
"description": "Case sensitivity of the sub-attribute.",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "mutability",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Modifiability of the sub-attribute.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"canonicalValues": ["readOnly","readWrite","immutable","writeOnly"]
|
||||
},
|
||||
{
|
||||
"name": "returned",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "When the sub-attribute is returned.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"canonicalValues": ["always","never","default","request"]
|
||||
},
|
||||
{
|
||||
"name": "uniqueness",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Uniqueness constraint of the sub-attribute.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none",
|
||||
"canonicalValues": ["none","server","global"]
|
||||
},
|
||||
{
|
||||
"name": "referenceTypes",
|
||||
"type": "string",
|
||||
"multiValued": true,
|
||||
"description": "ResourceTypes that the sub-attribute may reference.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"resourceType": "Schema",
|
||||
"location": "/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:Schema",
|
||||
"created": "2025-04-17T15:48:00",
|
||||
"lastModified": "2025-04-17T15:48:00"
|
||||
}
|
||||
}
|
||||
41
ee/api/routers/fixtures/service_provider_config.json
Normal file
41
ee/api/routers/fixtures/service_provider_config.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"schemas": [
|
||||
"urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"
|
||||
],
|
||||
"patch": {
|
||||
"supported": false
|
||||
},
|
||||
"bulk": {
|
||||
"supported": false,
|
||||
"maxOperations": 0,
|
||||
"maxPayloadSize": 0
|
||||
},
|
||||
"filter": {
|
||||
"supported": false,
|
||||
"maxResults": 0
|
||||
},
|
||||
"changePassword": {
|
||||
"supported": false
|
||||
},
|
||||
"sort": {
|
||||
"supported": false
|
||||
},
|
||||
"etag": {
|
||||
"supported": false
|
||||
},
|
||||
"authenticationSchemes": [
|
||||
{
|
||||
"type": "oauthbearertoken",
|
||||
"name": "OAuth Bearer Token",
|
||||
"description": "Authentication scheme using the OAuth Bearer Token Standard. The access token should be sent in the 'Authorization' header using the Bearer schema.",
|
||||
"specUri": "https://tools.ietf.org/html/rfc6750",
|
||||
"primary": true
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"resourceType": "ServiceProviderConfig",
|
||||
"created": "2025-04-15T15:45:00Z",
|
||||
"lastModified": "2025-04-15T15:45:00Z",
|
||||
"location": "/ServiceProviderConfig"
|
||||
}
|
||||
}
|
||||
212
ee/api/routers/fixtures/service_provider_config_schema.json
Normal file
212
ee/api/routers/fixtures/service_provider_config_schema.json
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
{
|
||||
"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": "Boolean value specifying whether 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": "Boolean value specifying whether 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": "Boolean value specifying whether the operation is supported.",
|
||||
"required": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "maxResults",
|
||||
"type": "integer",
|
||||
"multiValued": false,
|
||||
"description": "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 change password options.",
|
||||
"required": true,
|
||||
"returned": "default",
|
||||
"mutability": "readOnly",
|
||||
"subAttributes": [
|
||||
{
|
||||
"name": "supported",
|
||||
"type": "boolean",
|
||||
"multiValued": false,
|
||||
"description": "Boolean value specifying whether 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": "Boolean value specifying whether 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": "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"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"resourceType": "Schema",
|
||||
"location": "/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig",
|
||||
"created": "2025-04-17T15:48:00",
|
||||
"lastModified": "2025-04-17T15:48:00"
|
||||
}
|
||||
}
|
||||
386
ee/api/routers/fixtures/user_schema.json
Normal file
386
ee/api/routers/fixtures/user_schema.json
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
{
|
||||
"id": "urn:ietf:params:scim:schemas:core:2.0:User",
|
||||
"name": "User",
|
||||
"description": "User Account",
|
||||
"attributes": [
|
||||
{
|
||||
"name": "schemas",
|
||||
"type": "string",
|
||||
"multiValued": true,
|
||||
"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.",
|
||||
"required": true,
|
||||
"caseExact": false,
|
||||
"mutability": "immutable",
|
||||
"returned": "always",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Unique identifier for the resource, assigned by the service provider. MUST be non-empty, unique, stable, and non-reassignable. Clients MUST NOT specify this value.",
|
||||
"required": true,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "always",
|
||||
"uniqueness": "server"
|
||||
},
|
||||
{
|
||||
"name": "externalId",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Identifier for the resource as defined by the provisioning client. OPTIONAL; clients MAY include a non-empty value.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "meta",
|
||||
"type": "complex",
|
||||
"multiValued": false,
|
||||
"description": "Resource metadata. MUST be ignored when provided by clients.",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{
|
||||
"name": "resourceType",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The resource type name.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "created",
|
||||
"type": "dateTime",
|
||||
"multiValued": false,
|
||||
"description": "The date and time the resource was added.",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "lastModified",
|
||||
"type": "dateTime",
|
||||
"multiValued": false,
|
||||
"description": "The most recent date and time the resource was modified.",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "location",
|
||||
"type": "reference",
|
||||
"referenceTypes": ["external"],
|
||||
"multiValued": false,
|
||||
"description": "The URI of the resource being returned.",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "The version (ETag) of the resource being returned.",
|
||||
"required": false,
|
||||
"caseExact": true,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "userName",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Unique identifier for the User, used to authenticate. REQUIRED.",
|
||||
"required": true,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "server"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "complex",
|
||||
"multiValued": false,
|
||||
"description": "Components of the user's real name.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none",
|
||||
"subAttributes": [
|
||||
{ "name": "formatted", "type": "string", "multiValued": false, "description": "Complete name, formatted for display.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "familyName", "type": "string", "multiValued": false, "description": "Family name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "givenName", "type": "string", "multiValued": false, "description": "Given name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "middleName", "type": "string", "multiValued": false, "description": "Middle name(s).", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "honorificPrefix","type": "string", "multiValued": false, "description": "Honorific prefix.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "honorificSuffix","type": "string", "multiValued": false, "description": "Honorific suffix.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "displayName",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Full name, suitable for display.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "nickName",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Casual name.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "profileUrl",
|
||||
"type": "reference",
|
||||
"referenceTypes": ["external"],
|
||||
"multiValued": false,
|
||||
"description": "URL of the user's profile.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "User's title (e.g., 'Vice President').",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "userType",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Relationship between organization and user.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "preferredLanguage",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Preferred language, e.g., 'en_US'.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "locale",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Locale for formatting, e.g., 'en-US'.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "timezone",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Time zone in Olson format, e.g., 'America/Los_Angeles'.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"type": "boolean",
|
||||
"multiValued": false,
|
||||
"description": "Administrative status.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default"
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"type": "string",
|
||||
"multiValued": false,
|
||||
"description": "Cleartext password for create/reset operations.",
|
||||
"required": false,
|
||||
"caseExact": false,
|
||||
"mutability": "writeOnly",
|
||||
"returned": "never",
|
||||
"uniqueness": "none"
|
||||
},
|
||||
{
|
||||
"name": "emails",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "Email addresses for the user.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none",
|
||||
"subAttributes": [
|
||||
{ "name": "value", "type": "string", "multiValued": false, "description": "Email address.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "display", "type": "string", "multiValued": false, "description": "Display name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "type", "type": "string", "multiValued": false, "description": "Type: 'work','home','other'.", "required": false, "caseExact": false, "canonicalValues": ["work","home","other"], "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "primary", "type": "boolean", "multiValued": false, "description": "Primary flag; one per list.", "required": false, "mutability": "readWrite", "returned": "default" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "phoneNumbers",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "Phone numbers for the user.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{ "name": "value", "type": "string", "multiValued": false, "description": "Phone number (tel URI).", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "display", "type": "string", "multiValued": false, "description": "Display name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "type", "type": "string", "multiValued": false, "description": "Type: 'work','home','mobile','fax','pager','other'.", "required": false, "caseExact": false, "canonicalValues": ["work","home","mobile","fax","pager","other"], "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "primary", "type": "boolean", "multiValued": false, "description": "Primary flag; one per list.", "required": false, "mutability": "readWrite", "returned": "default" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ims",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "Instant messaging addresses.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{ "name": "value", "type": "string", "multiValued": false, "description": "IM address.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "display", "type": "string", "multiValued": false, "description": "Display name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "type", "type": "string", "multiValued": false, "description": "Type: 'aim','gtalk','icq','xmpp','msn','skype','qq','yahoo'.", "required": false, "caseExact": false, "canonicalValues": ["aim","gtalk","icq","xmpp","msn","skype","qq","yahoo"], "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "primary", "type": "boolean", "multiValued": false, "description": "Primary flag; one per list.", "required": false, "mutability": "readWrite", "returned": "default" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "photos",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "URLs of photos of the user.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{ "name": "value", "type": "reference", "referenceTypes": ["external"], "multiValued": false, "description": "Photo URL.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "display", "type": "string", "multiValued": false, "description": "Display name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "type", "type": "string", "multiValued": false, "description": "Type: 'photo','thumbnail'.", "required": false, "caseExact": false, "canonicalValues": ["photo","thumbnail"], "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "primary", "type": "boolean", "multiValued": false, "description": "Primary flag; one per list.", "required": false, "mutability": "readWrite", "returned": "default" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "addresses",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "Physical mailing addresses.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"uniqueness": "none",
|
||||
"subAttributes": [
|
||||
{ "name": "formatted", "type": "string", "multiValued": false, "description": "Full address, may contain newlines.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "streetAddress", "type": "string", "multiValued": false, "description": "Street address.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "locality", "type": "string", "multiValued": false, "description": "City or locality.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "region", "type": "string", "multiValued": false, "description": "State or region.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "postalCode", "type": "string", "multiValued": false, "description": "Zip or postal code.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "country", "type": "string", "multiValued": false, "description": "Country name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "type", "type": "string", "multiValued": false, "description": "Type: 'work','home','other'.", "required": false, "caseExact": false, "canonicalValues": ["work","home","other"], "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "primary", "type": "boolean","multiValued": false, "description": "Primary flag; one per list.", "required": false, "mutability": "readWrite", "returned": "default" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "groups",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "Groups to which the user belongs.",
|
||||
"required": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{ "name": "value", "type": "string", "multiValued": false, "description": "Group identifier.", "required": false, "caseExact": false, "mutability": "readOnly", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "$ref", "type": "reference", "referenceTypes": ["User","Group"], "multiValued": false, "description": "URI of the Group resource.", "required": false, "caseExact": false, "mutability": "readOnly", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "display", "type": "string", "multiValued": false, "description": "Display name.", "required": false, "caseExact": false, "mutability": "readOnly", "returned": "default", "uniqueness": "none" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "entitlements",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "Entitlements granted to the user.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{ "name": "value", "type": "string", "multiValued": false, "description": "Entitlement value.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "display", "type": "string", "multiValued": false, "description": "Display name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "type", "type": "string", "multiValued": false, "description": "Type label.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "primary", "type": "boolean", "multiValued": false, "description": "Primary flag; one per list.", "required": false, "mutability": "readWrite", "returned": "default" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "roles",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "Roles granted to the user.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{ "name": "value", "type": "string", "multiValued": false, "description": "Role value.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "display", "type": "string", "multiValued": false, "description": "Display name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "type", "type": "string", "multiValued": false, "description": "Type label.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "primary", "type": "boolean", "multiValued": false, "description": "Primary flag; one per list.", "required": false, "mutability": "readWrite", "returned": "default" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "x509Certificates",
|
||||
"type": "complex",
|
||||
"multiValued": true,
|
||||
"description": "X.509 certificates issued to the user.",
|
||||
"required": false,
|
||||
"mutability": "readWrite",
|
||||
"returned": "default",
|
||||
"subAttributes": [
|
||||
{ "name": "value", "type": "binary", "multiValued": false, "description": "Certificate value.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "display", "type": "string", "multiValued": false, "description": "Display name.", "required": false, "caseExact": false, "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "type", "type": "string", "multiValued": false, "description": "Type label.", "required": false, "caseExact": false, "canonicalValues": [], "mutability": "readWrite", "returned": "default", "uniqueness": "none" },
|
||||
{ "name": "primary", "type": "boolean", "multiValued": false, "description": "Primary flag; one per list.", "required": false, "mutability": "readWrite", "returned": "default" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"resourceType": "Schema",
|
||||
"location": "/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:User",
|
||||
"created": "2025-04-17T15:48:00",
|
||||
"lastModified": "2025-04-17T15:48:00"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
import logging
|
||||
from typing import Any, Literal
|
||||
import copy
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from decouple import config
|
||||
from fastapi import Depends, HTTPException, Header, Query, Response, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from pydantic import BaseModel, field_serializer
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from pydantic import BaseModel
|
||||
|
||||
from chalicelib.core import users, tenants
|
||||
from chalicelib.utils.scim_auth import (
|
||||
|
|
@ -26,19 +24,8 @@ logger = logging.getLogger(__name__)
|
|||
public_app, app, app_apikey = get_routers(prefix="/sso/scim/v2")
|
||||
|
||||
|
||||
"""Authentication endpoints"""
|
||||
|
||||
|
||||
class RefreshRequest(BaseModel):
|
||||
refresh_token: str
|
||||
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
||||
|
||||
|
||||
# Login endpoint to generate tokens
|
||||
@public_app.post("/token")
|
||||
async def login(
|
||||
async def post_token(
|
||||
host: str = Header(..., alias="Host"),
|
||||
form_data: OAuth2PasswordRequestForm = Depends(),
|
||||
):
|
||||
|
|
@ -56,17 +43,19 @@ async def login(
|
|||
return {
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
"token_type": "bearer",
|
||||
"token_type": "Bearer",
|
||||
}
|
||||
|
||||
|
||||
# Refresh token endpoint
|
||||
class RefreshRequest(BaseModel):
|
||||
refresh_token: str
|
||||
|
||||
|
||||
@public_app.post("/refresh")
|
||||
async def refresh_token(r: RefreshRequest):
|
||||
async def post_refresh(r: RefreshRequest):
|
||||
payload = verify_refresh_token(r.refresh_token)
|
||||
new_access_token, _ = create_tokens(tenant_id=payload["tenant_id"])
|
||||
|
||||
return {"access_token": new_access_token, "token_type": "bearer"}
|
||||
return {"access_token": new_access_token, "token_type": "Bearer"}
|
||||
|
||||
|
||||
RESOURCE_TYPE_IDS_TO_RESOURCE_TYPE_DETAILS = {
|
||||
|
|
@ -110,9 +99,7 @@ def _mutability_error_response():
|
|||
)
|
||||
|
||||
|
||||
@public_app.get("/ResourceTypes", dependencies=[Depends(auth_required)])
|
||||
async def get_resource_types(filter_param: str | None = Query(None, alias="filter")):
|
||||
if filter_param is not None:
|
||||
def _operation_not_permitted_error_response():
|
||||
return JSONResponse(
|
||||
status_code=403,
|
||||
content={
|
||||
|
|
@ -121,6 +108,24 @@ async def get_resource_types(filter_param: str | None = Query(None, alias="filte
|
|||
"status": "403",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _invalid_value_error_response():
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content={
|
||||
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
|
||||
"detail": "A required value was missing, or the value specified was not compatible with the operation or attribtue type, or resource schema.",
|
||||
"status": "400",
|
||||
"scimType": "invalidValue",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@public_app.get("/ResourceTypes", dependencies=[Depends(auth_required)])
|
||||
async def get_resource_types(filter_param: str | None = Query(None, alias="filter")):
|
||||
if filter_param is not None:
|
||||
return _operation_not_permitted_error_response()
|
||||
return JSONResponse(
|
||||
status_code=200,
|
||||
content={
|
||||
|
|
@ -151,14 +156,7 @@ SCHEMA_IDS_TO_SCHEMA_DETAILS = {
|
|||
@public_app.get("/Schemas", dependencies=[Depends(auth_required)])
|
||||
async def get_schemas(filter_param: str | None = Query(None, alias="filter")):
|
||||
if filter_param is not None:
|
||||
return JSONResponse(
|
||||
status_code=403,
|
||||
content={
|
||||
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
|
||||
"detail": "Operation is not permitted based on the supplied authorization",
|
||||
"status": "403",
|
||||
},
|
||||
)
|
||||
return _operation_not_permitted_error_response()
|
||||
return JSONResponse(
|
||||
status_code=200,
|
||||
content={
|
||||
|
|
@ -189,81 +187,19 @@ async def get_schema(schema_id: str):
|
|||
async def get_service_provider_config(
|
||||
r: Request, tenant_id: str | None = Depends(auth_optional)
|
||||
):
|
||||
content = copy.deepcopy(SERVICE_PROVIDER_CONFIG)
|
||||
content["meta"]["location"] = str(r.url)
|
||||
is_authenticated = tenant_id is not None
|
||||
if not is_authenticated:
|
||||
content = {
|
||||
"schemas": content["schemas"],
|
||||
"authenticationSchemes": content["authenticationSchemes"],
|
||||
"meta": content["meta"],
|
||||
}
|
||||
return JSONResponse(
|
||||
status_code=200,
|
||||
content=content,
|
||||
content={
|
||||
"schemas": SERVICE_PROVIDER_CONFIG["schemas"],
|
||||
"authenticationSchemes": SERVICE_PROVIDER_CONFIG[
|
||||
"authenticationSchemes"
|
||||
],
|
||||
"meta": SERVICE_PROVIDER_CONFIG["meta"],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
"""
|
||||
User endpoints
|
||||
"""
|
||||
|
||||
|
||||
class UserRequest(BaseModel):
|
||||
userName: str
|
||||
|
||||
|
||||
class PatchUserRequest(BaseModel):
|
||||
schemas: list[str]
|
||||
Operations: list[dict]
|
||||
|
||||
|
||||
class ResourceMetaResponse(BaseModel):
|
||||
resourceType: (
|
||||
Literal["ServiceProviderConfig", "ResourceType", "Schema", "User"] | None
|
||||
) = None
|
||||
created: datetime | None = None
|
||||
lastModified: datetime | None = None
|
||||
location: str | None = None
|
||||
version: str | None = None
|
||||
|
||||
@field_serializer("created", "lastModified")
|
||||
def serialize_datetime(self, dt: datetime) -> str | None:
|
||||
if not dt:
|
||||
return None
|
||||
return dt.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
|
||||
class CommonResourceResponse(BaseModel):
|
||||
id: str
|
||||
externalId: str | None = None
|
||||
schemas: list[
|
||||
Literal[
|
||||
"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",
|
||||
"urn:ietf:params:scim:schemas:core:2.0:User",
|
||||
]
|
||||
]
|
||||
meta: ResourceMetaResponse | None = None
|
||||
|
||||
|
||||
class UserResponse(CommonResourceResponse):
|
||||
schemas: list[Literal["urn:ietf:params:scim:schemas:core:2.0:User"]] = [
|
||||
"urn:ietf:params:scim:schemas:core:2.0:User"
|
||||
]
|
||||
userName: str | None = None
|
||||
|
||||
|
||||
class QueryResourceResponse(BaseModel):
|
||||
schemas: list[Literal["urn:ietf:params:scim:api:messages:2.0:ListResponse"]] = [
|
||||
"urn:ietf:params:scim:api:messages:2.0:ListResponse"
|
||||
]
|
||||
totalResults: int
|
||||
# todo(jon): add the other schemas
|
||||
Resources: list[UserResponse]
|
||||
startIndex: int
|
||||
itemsPerPage: int
|
||||
return JSONResponse(status_code=200, content=SERVICE_PROVIDER_CONFIG)
|
||||
|
||||
|
||||
MAX_USERS_PER_PAGE = 10
|
||||
|
|
@ -273,7 +209,7 @@ def _convert_db_user_to_scim_user(
|
|||
db_user: dict[str, Any],
|
||||
attributes: list[str] | None = None,
|
||||
excluded_attributes: list[str] | None = None,
|
||||
) -> UserResponse:
|
||||
) -> dict[str, Any]:
|
||||
user_schema = SCHEMA_IDS_TO_SCHEMA_DETAILS[
|
||||
"urn:ietf:params:scim:schemas:core:2.0:User"
|
||||
]
|
||||
|
|
@ -289,19 +225,19 @@ def _convert_db_user_to_scim_user(
|
|||
)
|
||||
scim_user = {
|
||||
"id": str(db_user["userId"]),
|
||||
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||
"meta": {
|
||||
"resourceType": "User",
|
||||
"created": db_user["createdAt"],
|
||||
"lastModified": db_user[
|
||||
"createdAt"
|
||||
], # todo(jon): we currently don't keep track of this in the db
|
||||
"created": db_user["createdAt"].strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
# todo(jon): we currently don't keep track of this in the db
|
||||
"lastModified": db_user["createdAt"].strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"location": f"Users/{db_user['userId']}",
|
||||
},
|
||||
"userName": db_user["email"],
|
||||
}
|
||||
scim_user = scim_helpers.filter_attributes(scim_user, included_attributes)
|
||||
scim_user = scim_helpers.exclude_attributes(scim_user, excluded_attributes)
|
||||
return UserResponse(**scim_user)
|
||||
return scim_user
|
||||
|
||||
|
||||
@public_app.get("/Users")
|
||||
|
|
@ -318,79 +254,81 @@ async def get_users(
|
|||
)
|
||||
# todo(jon): this might not be the most efficient thing to do. could be better to just do a count.
|
||||
# but this is the fastest thing at the moment just to test that it's working
|
||||
total_users = users.get_users_paginated(1, tenant_id)
|
||||
db_users = users.get_users_paginated(start_index, tenant_id, count=items_per_page)
|
||||
scim_users = [
|
||||
_convert_db_user_to_scim_user(user, attributes, excluded_attributes)
|
||||
for user in db_users
|
||||
total_resources = users.get_users_paginated(1, tenant_id)
|
||||
db_resources = users.get_users_paginated(
|
||||
start_index, tenant_id, count=items_per_page
|
||||
)
|
||||
scim_resources = [
|
||||
_convert_db_user_to_scim_user(resource, attributes, excluded_attributes)
|
||||
for resource in db_resources
|
||||
]
|
||||
return JSONResponse(
|
||||
status_code=200,
|
||||
content=QueryResourceResponse(
|
||||
totalResults=len(total_users),
|
||||
startIndex=start_index,
|
||||
itemsPerPage=len(scim_users),
|
||||
Resources=scim_users,
|
||||
).model_dump(mode="json", exclude_none=True),
|
||||
content={
|
||||
"totalResults": len(total_resources),
|
||||
"startIndex": start_index,
|
||||
"itemsPerPage": len(scim_resources),
|
||||
"Resources": scim_resources,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@public_app.get("/Users/{user_id}")
|
||||
def get_user(
|
||||
async def get_user(
|
||||
user_id: str,
|
||||
tenant_id=Depends(auth_required),
|
||||
attributes: list[str] | None = Query(None),
|
||||
excluded_attributes: list[str] | None = Query(None, alias="excludedAttributes"),
|
||||
):
|
||||
db_user = users.get_scim_user_by_id(user_id, tenant_id)
|
||||
if not db_user:
|
||||
db_resource = users.get_scim_user_by_id(user_id, tenant_id)
|
||||
if not db_resource:
|
||||
return _not_found_error_response(user_id)
|
||||
scim_user = _convert_db_user_to_scim_user(db_user, attributes, excluded_attributes)
|
||||
return JSONResponse(
|
||||
status_code=200, content=scim_user.model_dump(mode="json", exclude_none=True)
|
||||
scim_resource = _convert_db_user_to_scim_user(
|
||||
db_resource, attributes, excluded_attributes
|
||||
)
|
||||
return JSONResponse(status_code=200, content=scim_resource)
|
||||
|
||||
|
||||
@public_app.post("/Users")
|
||||
async def create_user(r: UserRequest, tenant_id=Depends(auth_required)):
|
||||
async def create_user(r: Request, tenant_id=Depends(auth_required)):
|
||||
payload = await r.json()
|
||||
if "userName" not in payload:
|
||||
return _invalid_value_error_response()
|
||||
# note(jon): this method will return soft deleted users as well
|
||||
existing_db_user = users.get_existing_scim_user_by_unique_values(r.userName)
|
||||
if existing_db_user and existing_db_user["deletedAt"] is None:
|
||||
existing_db_resource = users.get_existing_scim_user_by_unique_values(
|
||||
payload["userName"]
|
||||
)
|
||||
if existing_db_resource and existing_db_resource["deletedAt"] is None:
|
||||
return _uniqueness_error_response()
|
||||
if existing_db_user and existing_db_user["deletedAt"] is not None:
|
||||
db_user = users.restore_scim_user(existing_db_user["userId"], tenant_id)
|
||||
if existing_db_resource and existing_db_resource["deletedAt"] is not None:
|
||||
db_resource = users.restore_scim_user(existing_db_resource["userId"], tenant_id)
|
||||
else:
|
||||
db_user = users.create_scim_user(
|
||||
email=r.userName,
|
||||
db_resource = users.create_scim_user(
|
||||
email=payload["userName"],
|
||||
# note(jon): scim schema does not require the `name.formatted` attribute, but we require `name`.
|
||||
# so, we have to define the value ourselves here
|
||||
name="",
|
||||
tenant_id=tenant_id,
|
||||
)
|
||||
scim_user = _convert_db_user_to_scim_user(db_user)
|
||||
response = JSONResponse(
|
||||
status_code=201, content=scim_user.model_dump(mode="json", exclude_none=True)
|
||||
)
|
||||
response.headers["Location"] = scim_user.meta.location
|
||||
scim_resource = _convert_db_user_to_scim_user(db_resource)
|
||||
response = JSONResponse(status_code=201, content=scim_resource)
|
||||
response.headers["Location"] = scim_resource["meta"]["location"]
|
||||
return response
|
||||
|
||||
|
||||
@public_app.put("/Users/{user_id}")
|
||||
def update_user(user_id: str, r: UserRequest, tenant_id=Depends(auth_required)):
|
||||
async def update_user(user_id: str, r: Request, tenant_id=Depends(auth_required)):
|
||||
db_resource = users.get_scim_user_by_id(user_id, tenant_id)
|
||||
if not db_resource:
|
||||
return _not_found_error_response(user_id)
|
||||
current_scim_resource = _convert_db_user_to_scim_user(db_resource).model_dump(
|
||||
mode="json", exclude_none=True
|
||||
)
|
||||
changes = r.model_dump(mode="json")
|
||||
current_scim_resource = _convert_db_user_to_scim_user(db_resource)
|
||||
changes = await r.json()
|
||||
schema = SCHEMA_IDS_TO_SCHEMA_DETAILS["urn:ietf:params:scim:schemas:core:2.0:User"]
|
||||
try:
|
||||
valid_mutable_changes = scim_helpers.filter_mutable_attributes(
|
||||
schema, changes, current_scim_resource
|
||||
)
|
||||
except ValueError:
|
||||
# todo(jon): will need to add a test for this once we have an immutable field
|
||||
return _mutability_error_response()
|
||||
try:
|
||||
updated_db_resource = users.update_scim_user(
|
||||
|
|
@ -399,19 +337,16 @@ def update_user(user_id: str, r: UserRequest, tenant_id=Depends(auth_required)):
|
|||
email=valid_mutable_changes["userName"],
|
||||
)
|
||||
updated_scim_resource = _convert_db_user_to_scim_user(updated_db_resource)
|
||||
return JSONResponse(
|
||||
status_code=200,
|
||||
content=updated_scim_resource.model_dump(mode="json", exclude_none=True),
|
||||
)
|
||||
return JSONResponse(status_code=200, content=updated_scim_resource)
|
||||
except Exception:
|
||||
# note(jon): for now, this is the only error that would happen when updating the scim user
|
||||
return _uniqueness_error_response()
|
||||
|
||||
|
||||
@public_app.delete("/Users/{user_id}")
|
||||
def delete_user(user_id: str, tenant_id=Depends(auth_required)):
|
||||
user = users.get_scim_user_by_id(user_id, tenant_id)
|
||||
if not user:
|
||||
async def delete_user(user_id: str, tenant_id=Depends(auth_required)):
|
||||
db_resource = users.get_scim_user_by_id(user_id, tenant_id)
|
||||
if not db_resource:
|
||||
return _not_found_error_response(user_id)
|
||||
users.soft_delete_scim_user_by_id(user_id, tenant_id)
|
||||
return Response(status_code=204, content="")
|
||||
|
|
|
|||
|
|
@ -1,778 +1,23 @@
|
|||
# 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",
|
||||
"urn:ietf:params:scim:schemas:core:2.0:User",
|
||||
],
|
||||
case_exact=True,
|
||||
mutability="readOnly",
|
||||
required=True,
|
||||
returned="always",
|
||||
),
|
||||
_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,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
USER_SCHEMA = {
|
||||
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Schema"],
|
||||
"id": "urn:ietf:params:scim:schemas:core:2.0:User",
|
||||
"name": "User",
|
||||
"description": "User account.",
|
||||
"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:User",
|
||||
},
|
||||
"attributes": [
|
||||
*_common_resource_attributes(),
|
||||
_attribute_characteristics(
|
||||
name="userName",
|
||||
description="A service provider's unique identifier for the user.",
|
||||
required=True,
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
import json
|
||||
|
||||
SCHEMAS = sorted(
|
||||
[
|
||||
SERVICE_PROVIDER_CONFIG_SCHEMA,
|
||||
RESOURCE_TYPE_SCHEMA,
|
||||
SCHEMA_SCHEMA,
|
||||
USER_SCHEMA,
|
||||
json.load(open("routers/fixtures/service_provider_config_schema.json", "r")),
|
||||
json.load(open("routers/fixtures/resource_type_schema.json", "r")),
|
||||
json.load(open("routers/fixtures/schema_schema.json", "r")),
|
||||
json.load(open("routers/fixtures/user_schema.json", "r")),
|
||||
# todo(jon): add this when we have groups
|
||||
# json.load(open("routers/schemas/group_schema.json", "r")),
|
||||
],
|
||||
key=lambda x: x["id"],
|
||||
)
|
||||
|
||||
SERVICE_PROVIDER_CONFIG = {
|
||||
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
|
||||
"patch": {
|
||||
# todo(jon): this needs to be updated to True once we properly implement patching for users and groups
|
||||
"supported": False,
|
||||
},
|
||||
"bulk": {
|
||||
"supported": False,
|
||||
"maxOperations": 0,
|
||||
"maxPayloadSize": 0,
|
||||
},
|
||||
"filter": {
|
||||
# todo(jon): this needs to be updated to True once we properly implement filtering for users and groups
|
||||
"supported": False,
|
||||
"maxResults": 0,
|
||||
},
|
||||
"changePassword": {
|
||||
"supported": False,
|
||||
},
|
||||
"sort": {
|
||||
"supported": False,
|
||||
},
|
||||
"etag": {
|
||||
"supported": False,
|
||||
},
|
||||
"authenticationSchemes": [
|
||||
{
|
||||
"type": "oauthbearertoken",
|
||||
"name": "OAuth Bearer Token",
|
||||
"description": "Authentication scheme using the OAuth Bearer Token Standard. The access token should be sent in the 'Authorization' header using the Bearer schema.",
|
||||
"specUri": "https://tools.ietf.org/html/rfc6750",
|
||||
# todo(jon): see if we have our own documentation for this
|
||||
# "documentationUri": "", # optional
|
||||
"primary": True,
|
||||
},
|
||||
],
|
||||
"meta": {
|
||||
"resourceType": "ServiceProviderConfig",
|
||||
"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:00Z",
|
||||
"location": "", # note(jon): this field will be computed in the /ServiceProviderConfig endpoint
|
||||
},
|
||||
}
|
||||
SERVICE_PROVIDER_CONFIG = json.load(
|
||||
open("routers/fixtures/service_provider_config.json", "r")
|
||||
)
|
||||
|
||||
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",
|
||||
},
|
||||
}
|
||||
],
|
||||
json.load(open("routers/fixtures/resource_type.json", "r")),
|
||||
key=lambda x: x["id"],
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue