# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from typing import Any from jsonref import replace_refs from keystone.federation import schema as federation_schema from keystone.federation import utils as federation_mapping_schema from codegenerator.common.schema import ParameterSchema from codegenerator.common.schema import TypeSchema from codegenerator.openapi.keystone_schemas import auth _identity_provider_properties = { "enabled": { "type": "boolean", "description": "Whether the identity provider is enabled or not", }, "description": { "type": ["string", "null"], "description": "The identity provider description", }, "authorization_ttl": { "type": ["integer", "null"], "minimum": 0, "description": ( "The length of validity in minutes for group memberships " "carried over through mapping and persisted in the database. " "If left unset, the default value configured in keystone will " "be used, if enabled." ), }, "remote_ids": { "type": ["array", "null"], "description": "List of the unique identity provider's remote IDs", "items": {"type": "string"}, "uniqueItems": True, }, } IDENTITY_PROVIDER_SCHEMA: dict[str, Any] = { "type": "object", "properties": { "id": { "type": "string", "description": "The Identity Provider unique ID", }, "description": { "type": "string", "description": "The Identity Provider description", }, "domain_id": { "type": "string", "format": "uuid", "description": "The ID of a domain that is associated with the Identity Provider.", }, "authorization_ttl": { "type": "integer", "description": "The length of validity in minutes for group memberships carried over through mapping and persisted in the database.", }, "enabled": { "type": "boolean", "description": "Whether the Identity Provider is enabled or not", }, "remote_ids": { "type": "array", "description": "List of the unique Identity Provider’s remote IDs", "items": {"type": "string"}, }, }, } IDENTITY_PROVIDER_CONTAINER_SCHEMA: dict[str, Any] = { "type": "object", "properties": {"identity_provider": IDENTITY_PROVIDER_SCHEMA}, } IDENTITY_PROVIDER_CREATE_SCHEMA: dict[str, Any] = { "type": "object", "properties": { "identity_provider": { "type": "object", "projecties": { **_identity_provider_properties, "domain_id": { "type": ["string", "null"], "minLength": 1, "maxLength": 64, "pattern": r"^[a-zA-Z0-9-]+$", "description": ( "The ID of a domain that is associated with the " "identity provider. Federated users that " "authenticate with the identity provider will be " "created under the domain specified." ), }, }, } }, } IDENTITY_PROVIDER_UPDATE_SCHEMA: dict[str, Any] = { "type": "object", "properties": { "identity_provider": { "type": "object", "properties": _identity_provider_properties, "additionalProperties": False, "minProperties": 1, } }, } IDENTITY_PROVIDERS_SCHEMA: dict[str, Any] = { "type": "object", "properties": { "identity_providers": { "type": "array", "items": IDENTITY_PROVIDER_SCHEMA, } }, } IDENTITY_PROVIDERS_LIST_PARAMETERS: dict[str, Any] = { "OS_FEDERATION_identity_providers_id": { "in": "query", "name": "id", "description": "Filter for Identity Providers’ ID attribute", "schema": {"type": "string"}, }, "OS_FEDERATION_identity_providers_enabled": { "in": "query", "name": "enabled", "description": "Filter for Identity Providers’ enabled attribute", "schema": {"type": "boolean"}, }, } IDENTITY_PROVIDER_PROTOCOL_SCHEMA: dict[str, Any] = { "type": "object", "properties": { "id": { "type": "string", "format": "uuid", "description": "The federation protocol ID", }, "mapping_id": {"type": "string"}, "remote_id_attribute": {"type": "string", "maxLength": 64}, }, } IDENTITY_PROVIDER_PROTOCOL_CONTAINER_SCHEMA: dict[str, Any] = { "type": "object", "properties": {"protocol": IDENTITY_PROVIDER_PROTOCOL_SCHEMA}, } IDENTITY_PROVIDER_PROTOCOLS_SCHEMA: dict[str, Any] = { "type": "object", "properties": { "protocols": { "type": "array", "items": IDENTITY_PROVIDER_PROTOCOL_SCHEMA, } }, } IDENTITY_PROVIDER_PROTOCOL_CREATE_SCHEMA: dict[str, Any] = { "type": "object", "properties": {"protocol": federation_schema.protocol_create}, } IDENTITY_PROVIDER_PROTOCOL_UPDATE_SCHEMA: dict[str, Any] = { "type": "object", "properties": {"protocol": federation_schema.protocol_update}, } MAPPING_PROPERTIES = replace_refs( federation_mapping_schema.IDP_ATTRIBUTE_MAPPING_SCHEMA_2_0, proxies=False ) MAPPING_PROPERTIES.pop("definitions", None) MAPPING_PROPERTIES["properties"]["schema_version"] = { "type": "string", "description": "Mapping schema version", } MAPPING_SCHEMA: dict[str, Any] = { "type": "object", "properties": { "id": { "type": "string", "description": "The Federation Mapping unique ID", }, **MAPPING_PROPERTIES["properties"], }, } MAPPING_CONTAINER_SCHEMA: dict[str, Any] = { "type": "object", "properties": {"mapping": MAPPING_SCHEMA}, } MAPPINGS_SCHEMA: dict[str, Any] = { "type": "object", "properties": {"mappings": {"type": "array", "items": MAPPING_SCHEMA}}, } MAPPING_CREATE_SCHEMA: dict[str, Any] = { "type": "object", "properties": {"mapping": MAPPING_PROPERTIES}, } def _post_process_operation_hook( openapi_spec, operation_spec, path: str | None = None ): """Hook to allow service specific generator to modify details""" operationId = operation_spec.operationId if operationId == "OS-FEDERATION/identity_providers:get": for key, val in IDENTITY_PROVIDERS_LIST_PARAMETERS.items(): openapi_spec.components.parameters.setdefault( key, ParameterSchema(**val) ) ref = f"#/components/parameters/{key}" if ref not in [x.ref for x in operation_spec.parameters]: operation_spec.parameters.append(ParameterSchema(ref=ref)) elif operationId in [ "OS-FEDERATION/projects:get", "OS-FEDERATION/projects:head", "OS-FEDERATION/domains:get", "OS-FEDERATION/domains:head", "endpoints/endpoint_id/OS-ENDPOINT-POLICY/policy:get", ]: operation_spec.deprecated = True def _get_schema_ref( openapi_spec, name, description=None, schema_def=None, action_name=None ) -> tuple[str | None, str | None, bool]: mime_type: str = "application/json" ref: str | None if name == "Os_FederationProjectsGetResponse": openapi_spec.components.schemas.setdefault( name, TypeSchema(**auth.AUTH_PROJECTS_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name in ["Os_FederationDomainsGetResponse"]: openapi_spec.components.schemas.setdefault( name, TypeSchema(**auth.AUTH_DOMAINS_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name in [ "AuthOs_FederationSaml2PostRequest", "AuthOs_FederationSaml2EcpPostRequest", ]: openapi_spec.components.schemas.setdefault( name, TypeSchema(**auth.AUTH_TOKEN_ISSUE_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name in [ "AuthOs_FederationSaml2PostResponse", "AuthOs_FederationSaml2EcpPostResponse", ]: mime_type = "text/xml" openapi_spec.components.schemas.setdefault( name, TypeSchema( type="string", format="xml", descripion="SAML assertion in XML format", ), ) ref = f"#/components/schemas/{name}" elif name in [ "AuthOs_FederationWebssoGetResponse", "AuthOs_FederationWebssoPostResponse", "AuthOs_FederationIdentity_ProvidersProtocolsWebssoGetResponse", "AuthOs_FederationIdentity_ProvidersProtocolsWebssoPostResponse", "Os_FederationIdentity_ProvidersProtocolsAuthGetResponse", "Os_FederationIdentity_ProvidersProtocolsAuthPostResponse", ]: # Federation based auth returns unscoped token even it is not # described explicitly in apiref openapi_spec.components.schemas.setdefault( name, TypeSchema(**auth.AUTH_TOKEN_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name in [ "AuthOs_FederationWebssoPostRequest", "AuthOs_FederationIdentity_ProvidersProtocolsWebssoPostRequest", ]: ref = None # ### Identity provider elif name == "Os_FederationIdentity_ProvidersGetResponse": openapi_spec.components.schemas.setdefault( name, TypeSchema(**IDENTITY_PROVIDERS_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name in [ "Os_FederationIdentity_ProviderGetResponse", "Os_FederationIdentity_ProviderPutResponse", "Os_FederationIdentity_ProviderPatchResponse", ]: openapi_spec.components.schemas.setdefault( name, TypeSchema(**IDENTITY_PROVIDER_CONTAINER_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name == "Os_FederationIdentity_ProviderPutRequest": openapi_spec.components.schemas.setdefault( name, TypeSchema(**IDENTITY_PROVIDER_CREATE_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name == "Os_FederationIdentity_ProviderPatchRequest": openapi_spec.components.schemas.setdefault( name, TypeSchema(**IDENTITY_PROVIDER_UPDATE_SCHEMA) ) ref = f"#/components/schemas/{name}" # ### Identity provider protocols elif name == "Os_FederationIdentity_ProvidersProtocolsGetResponse": openapi_spec.components.schemas.setdefault( name, TypeSchema(**IDENTITY_PROVIDER_PROTOCOLS_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name in [ "Os_FederationIdentity_ProvidersProtocolGetResponse", "Os_FederationIdentity_ProvidersProtocolPutResponse", "Os_FederationIdentity_ProvidersProtocolPatchResponse", ]: openapi_spec.components.schemas.setdefault( name, TypeSchema(**IDENTITY_PROVIDER_PROTOCOL_CONTAINER_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name == "Os_FederationIdentity_ProvidersProtocolPutRequest": openapi_spec.components.schemas.setdefault( name, TypeSchema(**IDENTITY_PROVIDER_PROTOCOL_CREATE_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name == "Os_FederationIdentity_ProvidersProtocolPatchRequest": openapi_spec.components.schemas.setdefault( name, TypeSchema(**IDENTITY_PROVIDER_PROTOCOL_UPDATE_SCHEMA) ) ref = f"#/components/schemas/{name}" # ### Identity provider mapping elif name == "Os_FederationMappingsGetResponse": openapi_spec.components.schemas.setdefault( name, TypeSchema(**MAPPINGS_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name in [ "Os_FederationMappingGetResponse", "Os_FederationMappingPutResponse", "Os_FederationMappingPatchResponse", ]: openapi_spec.components.schemas.setdefault( name, TypeSchema(**MAPPING_CONTAINER_SCHEMA) ) ref = f"#/components/schemas/{name}" elif name in [ "Os_FederationMappingPutRequest", "Os_FederationMappingPatchRequest", ]: openapi_spec.components.schemas.setdefault( name, TypeSchema(**MAPPING_CREATE_SCHEMA) ) ref = f"#/components/schemas/{name}" # SAML2 Metadata elif name == "Os_FederationSaml2MetadataGetResponse": mime_type = "text/xml" openapi_spec.components.schemas.setdefault( name, TypeSchema( type="string", format="xml", descripion="Identity Provider metadata information in XML format", ), ) ref = f"#/components/schemas/{name}" else: return (None, None, False) return (ref, mime_type, True)