scim2-models 0.3.6__py3-none-any.whl → 0.3.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,143 @@
1
+ """Base SCIM object classes with schema identification."""
2
+
3
+ from typing import TYPE_CHECKING
4
+ from typing import Annotated
5
+ from typing import Any
6
+ from typing import Optional
7
+
8
+ from .annotations import Required
9
+ from .base import BaseModel
10
+ from .context import Context
11
+ from .utils import normalize_attribute_name
12
+
13
+ if TYPE_CHECKING:
14
+ from .rfc7643.resource import Resource
15
+
16
+
17
+ def validate_model_attribute(model: type["BaseModel"], attribute_base: str) -> None:
18
+ """Validate that an attribute name or a sub-attribute path exist for a given model."""
19
+ attribute_name, *sub_attribute_blocks = attribute_base.split(".")
20
+ sub_attribute_base = ".".join(sub_attribute_blocks)
21
+
22
+ aliases = {field.validation_alias for field in model.model_fields.values()}
23
+
24
+ if normalize_attribute_name(attribute_name) not in aliases:
25
+ raise ValueError(
26
+ f"Model '{model.__name__}' has no attribute named '{attribute_name}'"
27
+ )
28
+
29
+ if sub_attribute_base:
30
+ attribute_type = model.get_field_root_type(attribute_name)
31
+
32
+ if not attribute_type or not issubclass(attribute_type, BaseModel):
33
+ raise ValueError(
34
+ f"Attribute '{attribute_name}' is not a complex attribute, and cannot have a '{sub_attribute_base}' sub-attribute"
35
+ )
36
+
37
+ validate_model_attribute(attribute_type, sub_attribute_base)
38
+
39
+
40
+ def extract_schema_and_attribute_base(attribute_urn: str) -> tuple[str, str]:
41
+ """Extract the schema urn part and the attribute name part from attribute name.
42
+
43
+ As defined in :rfc:`RFC7644 §3.10 <7644#section-3.10>`.
44
+ """
45
+ *urn_blocks, attribute_base = attribute_urn.split(":")
46
+ schema = ":".join(urn_blocks)
47
+ return schema, attribute_base
48
+
49
+
50
+ def validate_attribute_urn(
51
+ attribute_name: str,
52
+ default_resource: Optional[type["Resource"]] = None,
53
+ resource_types: Optional[list[type["Resource"]]] = None,
54
+ ) -> str:
55
+ """Validate that an attribute urn is valid or not.
56
+
57
+ :param attribute_name: The attribute urn to check.
58
+ :default_resource: The default resource if `attribute_name` is not an absolute urn.
59
+ :resource_types: The available resources in which to look for the attribute.
60
+ :return: The normalized attribute URN.
61
+ """
62
+ from .rfc7643.resource import Resource
63
+
64
+ if not resource_types:
65
+ resource_types = []
66
+
67
+ if default_resource and default_resource not in resource_types:
68
+ resource_types.append(default_resource)
69
+
70
+ default_schema = (
71
+ default_resource.model_fields["schemas"].default[0]
72
+ if default_resource
73
+ else None
74
+ )
75
+
76
+ schema: Optional[Any]
77
+ schema, attribute_base = extract_schema_and_attribute_base(attribute_name)
78
+ if not schema:
79
+ schema = default_schema
80
+
81
+ if not schema:
82
+ raise ValueError("No default schema and relative URN")
83
+
84
+ resource = Resource.get_by_schema(resource_types, schema)
85
+ if not resource:
86
+ raise ValueError(f"No resource matching schema '{schema}'")
87
+
88
+ validate_model_attribute(resource, attribute_base)
89
+
90
+ return f"{schema}:{attribute_base}"
91
+
92
+
93
+ class ScimObject(BaseModel):
94
+ schemas: Annotated[list[str], Required.true]
95
+ """The "schemas" attribute is a REQUIRED attribute and is an array of
96
+ Strings containing URIs that are used to indicate the namespaces of the
97
+ SCIM schemas that define the attributes present in the current JSON
98
+ structure."""
99
+
100
+ def _prepare_model_dump(
101
+ self,
102
+ scim_ctx: Optional[Context] = Context.DEFAULT,
103
+ **kwargs: Any,
104
+ ) -> dict[str, Any]:
105
+ kwargs.setdefault("context", {}).setdefault("scim", scim_ctx)
106
+
107
+ if scim_ctx:
108
+ kwargs.setdefault("exclude_none", True)
109
+ kwargs.setdefault("by_alias", True)
110
+
111
+ return kwargs
112
+
113
+ def model_dump(
114
+ self,
115
+ *args: Any,
116
+ scim_ctx: Optional[Context] = Context.DEFAULT,
117
+ **kwargs: Any,
118
+ ) -> dict[str, Any]:
119
+ """Create a model representation that can be included in SCIM messages by using Pydantic :code:`BaseModel.model_dump`.
120
+
121
+ :param scim_ctx: If a SCIM context is passed, some default values of
122
+ Pydantic :code:`BaseModel.model_dump` are tuned to generate valid SCIM
123
+ messages. Pass :data:`None` to get the default Pydantic behavior.
124
+ """
125
+ dump_kwargs = self._prepare_model_dump(scim_ctx, **kwargs)
126
+ if scim_ctx:
127
+ dump_kwargs.setdefault("mode", "json")
128
+ return super(BaseModel, self).model_dump(*args, **dump_kwargs)
129
+
130
+ def model_dump_json(
131
+ self,
132
+ *args: Any,
133
+ scim_ctx: Optional[Context] = Context.DEFAULT,
134
+ **kwargs: Any,
135
+ ) -> str:
136
+ """Create a JSON model representation that can be included in SCIM messages by using Pydantic :code:`BaseModel.model_dump_json`.
137
+
138
+ :param scim_ctx: If a SCIM context is passed, some default values of
139
+ Pydantic :code:`BaseModel.model_dump` are tuned to generate valid SCIM
140
+ messages. Pass :data:`None` to get the default Pydantic behavior.
141
+ """
142
+ dump_kwargs = self._prepare_model_dump(scim_ctx, **kwargs)
143
+ return super(BaseModel, self).model_dump_json(*args, **dump_kwargs)
scim2_models/utils.py CHANGED
@@ -11,7 +11,7 @@ from pydantic.alias_generators import to_snake
11
11
  from pydantic_core import PydanticCustomError
12
12
 
13
13
  try:
14
- from types import UnionType # type: ignore
14
+ from types import UnionType
15
15
 
16
16
  UNION_TYPES = [Union, UnionType]
17
17
  except ImportError:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scim2-models
3
- Version: 0.3.6
3
+ Version: 0.3.7
4
4
  Summary: SCIM2 models serialization and validation with pydantic
5
5
  Project-URL: documentation, https://scim2-models.readthedocs.io
6
6
  Project-URL: repository, https://github.com/python-scim/scim2-models
@@ -0,0 +1,29 @@
1
+ scim2_models/__init__.py,sha256=yqYNm1B18OD-c8Y3QC3VWWv6Yoxw4PAvpC9eABhxef0,3128
2
+ scim2_models/annotations.py,sha256=oRjlKL1fqrYfa9UtaMdxF5fOT8CUUN3m-rdzvf7aiSA,3304
3
+ scim2_models/attributes.py,sha256=VloM7txtM2oexPrHO4phVY6nDzORMr3Vqf6EFAEZzyk,1804
4
+ scim2_models/base.py,sha256=BcAjsbwaHhO8AnGJJF05MqfL636UBlM2njFuCvFL_SY,16795
5
+ scim2_models/constants.py,sha256=9egq8JW0dFAqPng85CiHoH5T6pRtYL87-gC0C-IMGsk,573
6
+ scim2_models/context.py,sha256=oV8BCBSl9NvXuue11q4EYnoSze1yqtja9U_HEdrR-88,9125
7
+ scim2_models/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ scim2_models/reference.py,sha256=EQM8bbSr_kxbFMNlWYf_4sAJlSsOS5wUrn-9_eF0Ykc,2483
9
+ scim2_models/scim_object.py,sha256=t1p6ha719w1YU2Gtcy_k2NY-X0kc8PJxwstKOGP0oPA,5183
10
+ scim2_models/utils.py,sha256=1fmJoVCtrFURmvBegGti8UnXdj9EnzkNmg83jtpj70A,2755
11
+ scim2_models/rfc7643/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ scim2_models/rfc7643/enterprise_user.py,sha256=TVa5aS-eLHcDkwyr58hZYsRKk0AwjUaUaSFhU51mn5E,1806
13
+ scim2_models/rfc7643/group.py,sha256=lJXKopa__LoWhkaNqu0JFVyQaOMe-AF4vISaq0Gg7cE,1458
14
+ scim2_models/rfc7643/resource.py,sha256=jPP6nGGEVOLIX7Zmka5xHLGk3jJBfpaMBPizU1mXFoo,17275
15
+ scim2_models/rfc7643/resource_type.py,sha256=scLqbD3HX3fXT2JOXY9OUVnZz7i5ty2lx4VuiVxt6DE,3314
16
+ scim2_models/rfc7643/schema.py,sha256=DJQvDx31jYYZyfS3noTu4-8C429u0ctUqRysmvUULyI,10647
17
+ scim2_models/rfc7643/service_provider_config.py,sha256=6xJ182T-1szEQnN5Zb1cTdQCgTYIFi4XKygbvDlTKTM,5446
18
+ scim2_models/rfc7643/user.py,sha256=ErOghhilUF7fipwDRqARyLwJhbntQx4GJG3u2sZNJXs,10664
19
+ scim2_models/rfc7644/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ scim2_models/rfc7644/bulk.py,sha256=ZVGtitEDy--iDBzU2WR-LT7_rIizZFBD3sOo6rJ_WII,2590
21
+ scim2_models/rfc7644/error.py,sha256=KZBJfI2z4TByAK6W3mN3e3m0ZrDguPLm66v9W971Awk,6302
22
+ scim2_models/rfc7644/list_response.py,sha256=8o1_WR3AOZNv0hbCGcgq8lyeXaNjo1CiLxfteq8gctk,2396
23
+ scim2_models/rfc7644/message.py,sha256=rX2KezTdM81Z9mw7JTJYjWF-VDnDeUm6-ZxsarmVdsE,3738
24
+ scim2_models/rfc7644/patch_op.py,sha256=Yk2qr-vsuU6MMh6uFijo3Tw1vgo-clbgj1T0B8S1Tcs,2856
25
+ scim2_models/rfc7644/search_request.py,sha256=kLhIfyjAjc7ar6wkT7t-A2ySB-_3XYP-gf_LspLU2RU,3063
26
+ scim2_models-0.3.7.dist-info/METADATA,sha256=tqCuz5S5ESdKVlEQhpsOVxgXHUDfUyAC32rkIOH1w98,16288
27
+ scim2_models-0.3.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
28
+ scim2_models-0.3.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
29
+ scim2_models-0.3.7.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- scim2_models/__init__.py,sha256=Y06vTA_51lXfv9zk_dTzyIIqEo1H2bcencvMM5KAwn8,3063
2
- scim2_models/base.py,sha256=NV-uaDWG_pN6Cl_PT1E9PqdG7QH7jMMADGi3PVVurHQ,34917
3
- scim2_models/constants.py,sha256=SuMGFtVNletdV5ZJRUcIq7o2CqZCRvOurnIdLE_cakE,540
4
- scim2_models/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- scim2_models/utils.py,sha256=MzZz212-lkVWgXcpXvNwoi_u28wBTpkTwPrfYC5v92A,2771
6
- scim2_models/rfc7643/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- scim2_models/rfc7643/enterprise_user.py,sha256=EaxdHH2dcBrwWwGpaZC6iZ9dbcaVN1NpoRLAdkTYssQ,1781
8
- scim2_models/rfc7643/group.py,sha256=JML8OtgKjvHHv2YufiYcTtbZuN5GFVQTbm1J6MLtp8o,1427
9
- scim2_models/rfc7643/resource.py,sha256=oEm0RVBFonTrrhS-iYQquAdiy4UW4nXHbk_Bf--fR3g,12849
10
- scim2_models/rfc7643/resource_type.py,sha256=vT2ItHvBzmsFVt3zbIJABxjcnSLUK-mW-zWduHxGE3k,3570
11
- scim2_models/rfc7643/schema.py,sha256=B7TzMbT6ngYQrMqvqW5_LberN6EaqtZaFwBVsgoA3S0,10388
12
- scim2_models/rfc7643/service_provider_config.py,sha256=deMNCXlqiNzuLcVRN9mdHiTUxhczDnvi-oO6k-Anj8U,5402
13
- scim2_models/rfc7643/user.py,sha256=EEje4V_zbMVwVYOu2Gj1W7GjD90WnC0lUK3FJuh4jWE,10607
14
- scim2_models/rfc7644/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- scim2_models/rfc7644/bulk.py,sha256=I6S40kyJPwDQwPFi668wFFDVTST7z6QTe9HTL5QUViI,2577
16
- scim2_models/rfc7644/error.py,sha256=l4-vtGQYm5u13-ParhbHSeqXEil0E09QXSO9twAT3SU,6185
17
- scim2_models/rfc7644/list_response.py,sha256=GNfRKpL2WK2KHGC3kg7ldaSiPwCXl0AxDfhnbHByEAY,4589
18
- scim2_models/rfc7644/message.py,sha256=F4kPqbHAka3-wZzap9a45noQZw-o1vznTJypNABBF7w,253
19
- scim2_models/rfc7644/patch_op.py,sha256=OE-ixDanTkY5zQP7EK7OAp88uE_fMk03mqmaZHxgJ-g,2210
20
- scim2_models/rfc7644/search_request.py,sha256=DRGlixcWtYtbUuP9MT7PsnvyxlONLcXGEcQveWdqQng,3003
21
- scim2_models-0.3.6.dist-info/METADATA,sha256=ihhjxgKo0dF-ufWhrgCdsNiH6vGXOplFlga07UVHrLw,16288
22
- scim2_models-0.3.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
- scim2_models-0.3.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
24
- scim2_models-0.3.6.dist-info/RECORD,,