add openreplay user extension and ui friendly permission values

This commit is contained in:
Jonathan Griffin 2025-05-19 13:05:49 +02:00
parent 34bbde13c1
commit 167d94b763
8 changed files with 86 additions and 31 deletions

View file

@ -292,7 +292,7 @@ async def get_schema(schema_id: str, tenant_id=Depends(auth_required)):
user_config = ResourceConfig(
schema_id="urn:ietf:params:scim:schemas:core:2.0:User",
resource_type_id="User",
max_chunk_size=10,
get_active_resource_count=users.get_active_resource_count,
convert_provider_resource_to_client_resource=users.convert_provider_resource_to_client_resource,
@ -310,7 +310,7 @@ user_config = ResourceConfig(
filter_attribute_mapping=users.filter_attribute_mapping,
)
group_config = ResourceConfig(
schema_id="urn:ietf:params:scim:schemas:core:2.0:Group",
resource_type_id="Group",
max_chunk_size=10,
get_active_resource_count=groups.get_active_resource_count,
convert_provider_resource_to_client_resource=groups.convert_provider_resource_to_client_resource,

View file

@ -10,6 +10,9 @@ SCHEMAS = sorted(
json.load(open("routers/scim/fixtures/schema_schema.json", "r")),
json.load(open("routers/scim/fixtures/user_schema.json", "r")),
json.load(open("routers/scim/fixtures/group_schema.json", "r")),
json.load(
open("routers/scim/fixtures/open_replay_user_extension_schema.json", "r")
),
],
key=lambda x: x["id"],
)

View file

@ -0,0 +1,35 @@
{
"id": "urn:ietf:params:scim:schemas:extensions:openreplay:2.0:User",
"name": "User",
"description": "User Account Extension",
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Schema"],
"attributes": [
{
"name": "permissions",
"type": "string",
"multiValued": true,
"description": "Permissions granted to the users of the group.",
"required": false,
"caseExact": true,
"canonicalValues": ["Session Replay", "Developer Tools", "Dashboard", "Assist (Live)", "Assist (Call)", "Spots", "Change Spot Visibility"],
"mutability": "readWrite",
"returned": "default"
},
{
"name": "projectKeys",
"type": "string",
"multiValued": true,
"description": "A list of project keys associated with the group.",
"required": false,
"caseExact": false,
"mutability": "readWrite",
"returned": "default"
}
],
"meta": {
"resourceType": "Schema",
"location": "/v2/Schemas/urn:ietf:params:scim:schemas:extensions:openreplay:2.0:User",
"created": "2025-04-17T15:48:00Z",
"lastModified": "2025-04-17T15:48:00Z"
}
}

View file

@ -14,7 +14,12 @@
"lastModified": "2025-04-16T08:37:00Z",
"location": "ResourceType/User"
},
"schemaExtensions": []
"schemaExtensions": [
{
"schema": "urn:ietf:params:scim:schemas:extensions:openreplay:2.0:User",
"required": true
}
]
},
{
"schemas": [

View file

@ -334,14 +334,18 @@
},
{
"name": "entitlements",
"type": "string",
"type": "complex",
"multiValued": true,
"description": "Entitlements granted to the user.",
"required": false,
"caseExact": true,
"canonicalValues": ["SESSION_REPLAY", "METRICS", "ASSIST_LIVE", "ASSIST_CALL", "SPOT_PUBLIC"],
"mutability": "readWrite",
"returned": "default"
"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",
@ -372,17 +376,7 @@
{ "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" }
]
},
{
"name": "projectKeys",
"type": "string",
"multiValued": true,
"description": "A list of project keys associated with the group.",
"required": false,
"caseExact": false,
"mutability": "readWrite",
"returned": "default"
}
}
],
"meta": {
"resourceType": "Schema",

View file

@ -3,6 +3,7 @@ from typing import Any, Callable
from routers.scim.constants import (
SCHEMA_IDS_TO_SCHEMA_DETAILS,
RESOURCE_TYPE_IDS_TO_RESOURCE_TYPE_DETAILS,
)
from routers.scim import helpers
@ -17,7 +18,7 @@ ProviderInput = dict[str, Any]
@dataclass
class ResourceConfig:
schema_id: str
resource_type_id: str
max_chunk_size: int
get_active_resource_count: Callable[[int], int]
convert_provider_resource_to_client_resource: Callable[
@ -44,7 +45,19 @@ class ResourceConfig:
def get_schema(config: ResourceConfig) -> Schema:
return SCHEMA_IDS_TO_SCHEMA_DETAILS[config.schema_id]
resource_type_id = config.resource_type_id
resource_type = RESOURCE_TYPE_IDS_TO_RESOURCE_TYPE_DETAILS[resource_type_id]
main_schema_id = resource_type["schema"]
schema_extension_ids = [
item["schema"] for item in resource_type["schemaExtensions"]
]
result = SCHEMA_IDS_TO_SCHEMA_DETAILS[main_schema_id]
for schema_id in schema_extension_ids:
result["attributes"].extend(
SCHEMA_IDS_TO_SCHEMA_DETAILS[schema_id]["attributes"]
)
result["schemas"] = [main_schema_id, *schema_extension_ids]
return result
def convert_provider_resource_to_client_resource(

View file

@ -11,11 +11,21 @@ from routers.scim.resource_config import (
ClientInput,
ProviderInput,
)
from schemas.schemas_ee import ValidIdentityProviderPermissions
from schemas.schemas_ee import Permissions
def _is_valid_permission_for_identity_provider(permission: str) -> bool:
return ValidIdentityProviderPermissions.has_value(permission)
permission_display_to_value_mapping = {
"Session Replay": Permissions.SESSION_REPLAY,
"Developer Tools": Permissions.DEV_TOOLS,
"Dashboard": Permissions.METRICS,
"Assist (Live)": Permissions.ASSIST_LIVE,
"Assist (Call)": Permissions.ASSIST_CALL,
"Spots": Permissions.SPOT,
"Change Spot Visibility": Permissions.SPOT_PUBLIC,
}
value = permission_display_to_value_mapping.get(permission)
return Permissions.has_value(value)
def convert_client_resource_update_input_to_provider_resource_update_input(
@ -163,7 +173,10 @@ def convert_provider_resource_to_client_resource(
)
return {
"id": str(provider_resource["user_id"]),
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extensions:openreplay:2.0:User",
],
"meta": {
"resourceType": "User",
"created": provider_resource["created_at"].strftime("%Y-%m-%dT%H:%M:%SZ"),

View file

@ -28,14 +28,6 @@ class ServicePermissions(str, Enum):
READ_NOTES = "SERVICE_READ_NOTES"
class ValidIdentityProviderPermissions(str, Enum):
SESSION_REPLAY = "SESSION_REPLAY"
METRICS = "METRICS"
ASSIST_LIVE = "ASSIST_LIVE"
ASSIST_CALL = "ASSIST_CALL"
SPOT_PUBLIC = "SPOT_PUBLIC"
class CurrentContext(schemas.CurrentContext):
permissions: List[Union[Permissions, ServicePermissions]] = Field(...)
service_account: bool = Field(default=False)