Skip to content

Commit

Permalink
fix: ServiceProviderConfiguration missing attributes and unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
azmeuk committed May 24, 2024
1 parent dc48100 commit 8f181b8
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 8 deletions.
4 changes: 4 additions & 0 deletions pydantic_scim2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
from .responses import PatchRequest
from .responses import SCIMError
from .service_provider import AuthenticationScheme
from .service_provider import AuthenticationSchemeKind
from .service_provider import Bulk
from .service_provider import ChangePassword
from .service_provider import ETag
from .service_provider import Filter
from .service_provider import Patch
from .service_provider import ServiceProviderConfiguration
Expand Down Expand Up @@ -52,6 +54,7 @@
"ChangePassword",
"Sort",
"AuthenticationScheme",
"AuthenticationSchemeKind",
"ServiceProviderConfiguration",
"Name",
"EmailKind",
Expand All @@ -70,4 +73,5 @@
"User",
"Resource",
"Meta",
"ETag",
]
89 changes: 81 additions & 8 deletions pydantic_scim2/service_provider.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# generated by datamodel-codegen:
# filename: serviceProviderConfig.schema.json
# timestamp: 2022-10-04T20:24:15+00:00

from __future__ import annotations

from enum import Enum
from typing import List
from typing import Optional

from pydantic import AnyUrl
from pydantic import BaseModel
from pydantic import Field

from .resource import Meta
from .resource import Resource


class Patch(BaseModel):
supported: bool = Field(
Expand All @@ -24,6 +22,11 @@ class Bulk(BaseModel):
...,
description="A Boolean value specifying whether or not the operation is supported.",
)
maxOperations: int
"""An integer value specifying the maximum number of operations."""

maxPayloadSize: int
"""An integer value specifying the maximum payload size in bytes."""


class Filter(BaseModel):
Expand Down Expand Up @@ -51,7 +54,23 @@ class Sort(BaseModel):
)


class ETag(BaseModel):
supported: bool
"""A Boolean value specifying whether or not the operation is supported."""


class AuthenticationSchemeKind(Enum):
oauth = "oauth"
oauth2 = "oauth2"
oauthbearertoken = "oauthbearertoken"
httpbasic = "httpbasic"
httpdigest = "httpdigest"


class AuthenticationScheme(BaseModel):
type: AuthenticationSchemeKind
"""The authentication scheme."""

name: str = Field(
..., description="The common authentication scheme name, e.g., HTTP Basic."
)
Expand All @@ -66,9 +85,60 @@ class AuthenticationScheme(BaseModel):
None,
description="An HTTP-addressable URL pointing to the authentication scheme's usage documentation.",
)
primary: Optional[bool] = Field(
None,
description="A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g., the preferred mailing address or primary email address. The primary attribute value 'true' MUST appear no more than once.",
)


class ServiceProviderConfiguration(Resource):
# Each SCIM resource (Users, Groups, etc.) includes the following
# common attributes. With the exception of the "ServiceProviderConfig"
# and "ResourceType" server discovery endpoints and their associated
# resources, these attributes MUST be defined for all resources,
# including any extended resource types.

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

externalId: Optional[str] = None
"""A String that is an identifier for the resource as defined by the
provisioning client.
The "externalId" may simplify identification of a resource between
the provisioning client and the service provider by allowing the
client to use a filter to locate the resource with an identifier
from the provisioning domain, obviating the need to store a local
mapping between the provisioning domain's identifier of the resource
and the identifier used by the service provider. Each resource MAY
include a non-empty "externalId" value. The value of the
"externalId" attribute is always issued by the provisioning client
and MUST NOT be specified by the service provider. The service
provider MUST always interpret the externalId as scoped to the
provisioning domain. While the server does not enforce uniqueness,
it is assumed that the value's uniqueness is controlled by the
client setting the value. See Section 9 for additional
considerations regarding privacy. This attribute has "caseExact" as
"true" and a mutability of "readWrite". This attribute is OPTIONAL.
"""

meta: Optional[Meta] = None
"""A complex attribute containing resource metadata."""


class ServiceProviderConfiguration(BaseModel):
documentationUri: Optional[AnyUrl] = Field(
None,
description="An HTTP-addressable URL pointing to the service provider's human-consumable help documentation.",
Expand All @@ -89,6 +159,9 @@ class ServiceProviderConfiguration(BaseModel):
sort: Sort = Field(
..., description="A complex type that specifies sort result options."
)
etag: ETag
"""A complex type that specifies ETag configuration options."""

authenticationSchemes: List[AuthenticationScheme] = Field(
...,
description="A complex type that specifies supported authentication scheme properties.",
Expand Down
60 changes: 60 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
from pydantic import AnyUrl

from pydantic_scim2 import AddressKind
from pydantic_scim2 import AuthenticationSchemeKind
from pydantic_scim2 import EmailKind
from pydantic_scim2 import Group
from pydantic_scim2 import ImKind
from pydantic_scim2 import PhoneNumberKind
from pydantic_scim2 import PhotoKind
from pydantic_scim2 import ServiceProviderConfiguration
from pydantic_scim2 import User


Expand Down Expand Up @@ -148,6 +150,7 @@ def test_enterprise_user(full_enterprise_payload): ...

def test_group(group_payload):
obj = Group.model_validate(group_payload)

assert obj.schemas == ["urn:ietf:params:scim:schemas:core:2.0:Group"]
assert obj.id == "e9e30dba-f08f-4109-8486-d5c6a331660a"
assert obj.displayName == "Tour Guides"
Expand All @@ -173,3 +176,60 @@ def test_group(group_payload):
obj.meta.location
== "https://example.com/v2/Groups/e9e30dba-f08f-4109-8486-d5c6a331660a"
)


def test_service_provider_configuration(service_provider_configuration_payload):
obj = ServiceProviderConfiguration.model_validate(
service_provider_configuration_payload
)

assert obj.schemas == [
"urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"
]
assert obj.documentationUri == AnyUrl("http://example.com/help/scim.html")
assert obj.patch.supported is True
assert obj.bulk.supported is True
assert obj.bulk.maxOperations == 1000
assert obj.bulk.maxPayloadSize == 1048576
assert obj.filter.supported is True
assert obj.filter.maxResults == 200
assert obj.changePassword.supported is True
assert obj.sort.supported is True
assert obj.etag.supported is True
assert obj.authenticationSchemes[0].name == "OAuth Bearer Token"
assert (
obj.authenticationSchemes[0].description
== "Authentication scheme using the OAuth Bearer Token Standard"
)
assert obj.authenticationSchemes[0].specUri == AnyUrl(
"http://www.rfc-editor.org/info/rfc6750"
)
assert obj.authenticationSchemes[0].documentationUri == AnyUrl(
"http://example.com/help/oauth.html"
)
assert (
obj.authenticationSchemes[0].type == AuthenticationSchemeKind.oauthbearertoken
)
assert obj.authenticationSchemes[0].primary is True

assert obj.authenticationSchemes[1].name == "HTTP Basic"
assert (
obj.authenticationSchemes[1].description
== "Authentication scheme using the HTTP Basic Standard"
)
assert obj.authenticationSchemes[1].specUri == AnyUrl(
"http://www.rfc-editor.org/info/rfc2617"
)
assert obj.authenticationSchemes[1].documentationUri == AnyUrl(
"http://example.com/help/httpBasic.html"
)
assert obj.authenticationSchemes[1].type == AuthenticationSchemeKind.httpbasic
assert obj.meta.location == "https://example.com/v2/ServiceProviderConfig"
assert obj.meta.resourceType == "ServiceProviderConfig"
assert obj.meta.created == datetime.datetime(
2010, 1, 23, 4, 56, 22, tzinfo=datetime.timezone.utc
)
assert obj.meta.lastModified == datetime.datetime(
2011, 5, 13, 4, 42, 34, tzinfo=datetime.timezone.utc
)
assert obj.meta.version == 'W\\/"3694e05e9dff594"'

0 comments on commit 8f181b8

Please sign in to comment.