scim2-models 0.5.1__py3-none-any.whl → 0.6.0__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.
- scim2_models/__init__.py +32 -0
- scim2_models/attributes.py +5 -1
- scim2_models/base.py +68 -17
- scim2_models/exceptions.py +265 -0
- scim2_models/messages/bulk.py +3 -7
- scim2_models/messages/error.py +184 -15
- scim2_models/messages/list_response.py +2 -5
- scim2_models/messages/message.py +4 -4
- scim2_models/messages/patch_op.py +127 -261
- scim2_models/messages/search_request.py +9 -57
- scim2_models/path.py +731 -0
- scim2_models/reference.py +139 -48
- scim2_models/resources/enterprise_user.py +10 -8
- scim2_models/resources/group.py +9 -7
- scim2_models/resources/resource.py +110 -23
- scim2_models/resources/resource_type.py +15 -14
- scim2_models/resources/schema.py +15 -20
- scim2_models/resources/service_provider_config.py +10 -13
- scim2_models/resources/user.py +13 -10
- scim2_models/scim_object.py +84 -1
- scim2_models/utils.py +0 -140
- {scim2_models-0.5.1.dist-info → scim2_models-0.6.0.dist-info}/METADATA +2 -2
- scim2_models-0.6.0.dist-info/RECORD +30 -0
- scim2_models/urn.py +0 -126
- scim2_models-0.5.1.dist-info/RECORD +0 -29
- {scim2_models-0.5.1.dist-info → scim2_models-0.6.0.dist-info}/WHEEL +0 -0
scim2_models/__init__.py
CHANGED
|
@@ -7,6 +7,18 @@ from .attributes import ComplexAttribute
|
|
|
7
7
|
from .attributes import MultiValuedComplexAttribute
|
|
8
8
|
from .base import BaseModel
|
|
9
9
|
from .context import Context
|
|
10
|
+
from .exceptions import InvalidFilterException
|
|
11
|
+
from .exceptions import InvalidPathException
|
|
12
|
+
from .exceptions import InvalidSyntaxException
|
|
13
|
+
from .exceptions import InvalidValueException
|
|
14
|
+
from .exceptions import InvalidVersionException
|
|
15
|
+
from .exceptions import MutabilityException
|
|
16
|
+
from .exceptions import NoTargetException
|
|
17
|
+
from .exceptions import PathNotFoundException
|
|
18
|
+
from .exceptions import SCIMException
|
|
19
|
+
from .exceptions import SensitiveException
|
|
20
|
+
from .exceptions import TooManyException
|
|
21
|
+
from .exceptions import UniquenessException
|
|
10
22
|
from .messages.bulk import BulkOperation
|
|
11
23
|
from .messages.bulk import BulkRequest
|
|
12
24
|
from .messages.bulk import BulkResponse
|
|
@@ -16,6 +28,10 @@ from .messages.message import Message
|
|
|
16
28
|
from .messages.patch_op import PatchOp
|
|
17
29
|
from .messages.patch_op import PatchOperation
|
|
18
30
|
from .messages.search_request import SearchRequest
|
|
31
|
+
from .path import URN
|
|
32
|
+
from .path import Path
|
|
33
|
+
from .reference import URI
|
|
34
|
+
from .reference import External
|
|
19
35
|
from .reference import ExternalReference
|
|
20
36
|
from .reference import Reference
|
|
21
37
|
from .reference import URIReference
|
|
@@ -72,6 +88,7 @@ __all__ = [
|
|
|
72
88
|
"EnterpriseUser",
|
|
73
89
|
"Entitlement",
|
|
74
90
|
"Error",
|
|
91
|
+
"External",
|
|
75
92
|
"ExternalReference",
|
|
76
93
|
"Extension",
|
|
77
94
|
"Filter",
|
|
@@ -79,13 +96,22 @@ __all__ = [
|
|
|
79
96
|
"GroupMember",
|
|
80
97
|
"GroupMembership",
|
|
81
98
|
"Im",
|
|
99
|
+
"InvalidFilterException",
|
|
100
|
+
"InvalidPathException",
|
|
101
|
+
"InvalidSyntaxException",
|
|
102
|
+
"InvalidValueException",
|
|
103
|
+
"InvalidVersionException",
|
|
82
104
|
"ListResponse",
|
|
83
105
|
"Manager",
|
|
84
106
|
"Message",
|
|
85
107
|
"Meta",
|
|
86
108
|
"Mutability",
|
|
109
|
+
"MutabilityException",
|
|
87
110
|
"MultiValuedComplexAttribute",
|
|
88
111
|
"Name",
|
|
112
|
+
"NoTargetException",
|
|
113
|
+
"Path",
|
|
114
|
+
"PathNotFoundException",
|
|
89
115
|
"Patch",
|
|
90
116
|
"PatchOp",
|
|
91
117
|
"PatchOperation",
|
|
@@ -97,13 +123,19 @@ __all__ = [
|
|
|
97
123
|
"ResourceType",
|
|
98
124
|
"Returned",
|
|
99
125
|
"Role",
|
|
126
|
+
"SCIMException",
|
|
100
127
|
"Schema",
|
|
101
128
|
"SchemaExtension",
|
|
102
129
|
"SearchRequest",
|
|
130
|
+
"SensitiveException",
|
|
103
131
|
"ServiceProviderConfig",
|
|
104
132
|
"Sort",
|
|
133
|
+
"TooManyException",
|
|
105
134
|
"Uniqueness",
|
|
135
|
+
"UniquenessException",
|
|
136
|
+
"URI",
|
|
106
137
|
"URIReference",
|
|
138
|
+
"URN",
|
|
107
139
|
"User",
|
|
108
140
|
"X509Certificate",
|
|
109
141
|
]
|
scim2_models/attributes.py
CHANGED
|
@@ -34,7 +34,11 @@ class MultiValuedComplexAttribute(ComplexAttribute):
|
|
|
34
34
|
|
|
35
35
|
primary: bool | None = None
|
|
36
36
|
"""A Boolean value indicating the 'primary' or preferred attribute value
|
|
37
|
-
for this attribute.
|
|
37
|
+
for this attribute.
|
|
38
|
+
|
|
39
|
+
Per :rfc:`RFC 7643 §2.4 <7643#section-2.4>`, the primary attribute value
|
|
40
|
+
``True`` MUST appear no more than once in a multi-valued attribute list.
|
|
41
|
+
"""
|
|
38
42
|
|
|
39
43
|
display: Annotated[str | None, Mutability.immutable] = None
|
|
40
44
|
"""A human-readable name, primarily used for display purposes."""
|
scim2_models/base.py
CHANGED
|
@@ -29,12 +29,21 @@ from scim2_models.utils import _normalize_attribute_name
|
|
|
29
29
|
from scim2_models.utils import _to_camel
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
def _is_attribute_requested(requested_urns: list[str], current_urn: str) -> bool:
|
|
33
|
+
"""Check if an attribute should be included based on the requested URNs.
|
|
34
|
+
|
|
35
|
+
Returns True if:
|
|
36
|
+
- The current attribute is explicitly requested
|
|
37
|
+
- A sub-attribute of the current attribute is requested
|
|
38
|
+
- The current attribute is a sub-attribute of a requested attribute
|
|
39
|
+
"""
|
|
40
|
+
return (
|
|
41
|
+
current_urn in requested_urns
|
|
42
|
+
or any(
|
|
43
|
+
item.startswith(f"{current_urn}.") or item.startswith(f"{current_urn}:")
|
|
44
|
+
for item in requested_urns
|
|
45
|
+
)
|
|
46
|
+
or any(current_urn.startswith(f"{item}.") for item in requested_urns)
|
|
38
47
|
)
|
|
39
48
|
|
|
40
49
|
|
|
@@ -355,6 +364,49 @@ class BaseModel(PydanticBaseModel):
|
|
|
355
364
|
cls._check_mutability_issues(original, obj)
|
|
356
365
|
return obj
|
|
357
366
|
|
|
367
|
+
@model_validator(mode="after")
|
|
368
|
+
def check_primary_attribute_uniqueness(self, info: ValidationInfo) -> Self:
|
|
369
|
+
"""Validate that only one attribute can be marked as primary in multi-valued lists.
|
|
370
|
+
|
|
371
|
+
Per RFC 7643 Section 2.4: The primary attribute value 'true' MUST appear no more than once.
|
|
372
|
+
"""
|
|
373
|
+
scim_context = info.context.get("scim") if info.context else None
|
|
374
|
+
if not scim_context or scim_context == Context.DEFAULT:
|
|
375
|
+
return self
|
|
376
|
+
|
|
377
|
+
for field_name in self.__class__.model_fields:
|
|
378
|
+
if not self.get_field_multiplicity(field_name):
|
|
379
|
+
continue
|
|
380
|
+
|
|
381
|
+
field_value = getattr(self, field_name)
|
|
382
|
+
if field_value is None:
|
|
383
|
+
continue
|
|
384
|
+
|
|
385
|
+
element_type = self.get_field_root_type(field_name)
|
|
386
|
+
if (
|
|
387
|
+
element_type is None
|
|
388
|
+
or not isclass(element_type)
|
|
389
|
+
or not issubclass(element_type, PydanticBaseModel)
|
|
390
|
+
or "primary" not in element_type.model_fields
|
|
391
|
+
):
|
|
392
|
+
continue
|
|
393
|
+
|
|
394
|
+
primary_count = sum(
|
|
395
|
+
1 for item in field_value if getattr(item, "primary", None) is True
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
if primary_count > 1:
|
|
399
|
+
raise PydanticCustomError(
|
|
400
|
+
"primary_uniqueness_error",
|
|
401
|
+
"Field '{field_name}' has {count} items marked as primary, but only one is allowed per RFC 7643",
|
|
402
|
+
{
|
|
403
|
+
"field_name": field_name,
|
|
404
|
+
"count": primary_count,
|
|
405
|
+
},
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
return self
|
|
409
|
+
|
|
358
410
|
@classmethod
|
|
359
411
|
def _check_mutability_issues(
|
|
360
412
|
cls, original: "BaseModel", replacement: "BaseModel"
|
|
@@ -397,7 +449,9 @@ class BaseModel(PydanticBaseModel):
|
|
|
397
449
|
main_schema = self._attribute_urn
|
|
398
450
|
separator = "."
|
|
399
451
|
else:
|
|
400
|
-
main_schema = self.__class__
|
|
452
|
+
main_schema = getattr(self.__class__, "__schema__", None)
|
|
453
|
+
if main_schema is None:
|
|
454
|
+
return
|
|
401
455
|
separator = ":"
|
|
402
456
|
|
|
403
457
|
for field_name in self.__class__.model_fields:
|
|
@@ -478,9 +532,7 @@ class BaseModel(PydanticBaseModel):
|
|
|
478
532
|
if returnability == Returned.default and (
|
|
479
533
|
(
|
|
480
534
|
included_urns
|
|
481
|
-
and not
|
|
482
|
-
included_urns, attribute_urn
|
|
483
|
-
)
|
|
535
|
+
and not _is_attribute_requested(included_urns, attribute_urn)
|
|
484
536
|
)
|
|
485
537
|
or attribute_urn in excluded_urns
|
|
486
538
|
):
|
|
@@ -533,13 +585,12 @@ class BaseModel(PydanticBaseModel):
|
|
|
533
585
|
"""
|
|
534
586
|
from scim2_models.resources.resource import Extension
|
|
535
587
|
|
|
536
|
-
main_schema = self.__class__
|
|
588
|
+
main_schema = getattr(self.__class__, "__schema__", None)
|
|
537
589
|
field = self.__class__.model_fields[field_name]
|
|
538
590
|
alias = field.serialization_alias or field_name
|
|
539
591
|
field_type = self.get_field_root_type(field_name)
|
|
540
|
-
|
|
541
|
-
alias
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
return full_urn
|
|
592
|
+
if isclass(field_type) and issubclass(field_type, Extension):
|
|
593
|
+
return alias
|
|
594
|
+
if main_schema is None:
|
|
595
|
+
return alias
|
|
596
|
+
return f"{main_schema}:{alias}"
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""SCIM exceptions corresponding to RFC 7644 error types.
|
|
2
|
+
|
|
3
|
+
This module provides a hierarchy of exceptions that map to SCIM protocol errors.
|
|
4
|
+
Each exception can be converted to a :class:`~scim2_models.Error` response object
|
|
5
|
+
or to a :class:`~pydantic_core.PydanticCustomError` for use in Pydantic validators.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from pydantic_core import PydanticCustomError
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from .messages.error import Error
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SCIMException(Exception):
|
|
18
|
+
"""Base exception for SCIM protocol errors.
|
|
19
|
+
|
|
20
|
+
Each subclass corresponds to a scimType defined in :rfc:`RFC 7644 Table 9 <7644#section-3.12>`.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
status: int = 400
|
|
24
|
+
scim_type: str = ""
|
|
25
|
+
_default_detail: str = "A SCIM error occurred"
|
|
26
|
+
|
|
27
|
+
def __init__(self, *, detail: str | None = None, **context: Any):
|
|
28
|
+
self.context = context
|
|
29
|
+
self._detail = detail
|
|
30
|
+
super().__init__(detail or self._default_detail)
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def detail(self) -> str:
|
|
34
|
+
"""The error detail message."""
|
|
35
|
+
return self._detail or self._default_detail
|
|
36
|
+
|
|
37
|
+
def to_error(self) -> "Error":
|
|
38
|
+
"""Convert this exception to a SCIM Error response object."""
|
|
39
|
+
from .messages.error import Error
|
|
40
|
+
|
|
41
|
+
return Error(
|
|
42
|
+
status=self.status,
|
|
43
|
+
scim_type=self.scim_type or None,
|
|
44
|
+
detail=str(self),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def as_pydantic_error(self) -> PydanticCustomError:
|
|
48
|
+
"""Convert to PydanticCustomError for use in Pydantic validators."""
|
|
49
|
+
return PydanticCustomError(
|
|
50
|
+
f"scim_{self.scim_type}" if self.scim_type else "scim_error",
|
|
51
|
+
str(self),
|
|
52
|
+
{"scim_type": self.scim_type, "status": self.status, **self.context},
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class InvalidFilterException(SCIMException):
|
|
57
|
+
"""The specified filter syntax was invalid.
|
|
58
|
+
|
|
59
|
+
Corresponds to scimType ``invalidFilter`` with HTTP status 400.
|
|
60
|
+
|
|
61
|
+
:rfc:`RFC 7644 Section 3.4.2.2 <7644#section-3.4.2.2>`
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
status = 400
|
|
65
|
+
scim_type = "invalidFilter"
|
|
66
|
+
_default_detail = (
|
|
67
|
+
"The specified filter syntax was invalid, "
|
|
68
|
+
"or the specified attribute and filter comparison combination is not supported"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def __init__(self, *, filter: str | None = None, **kw: Any):
|
|
72
|
+
self.filter = filter
|
|
73
|
+
super().__init__(**kw)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TooManyException(SCIMException):
|
|
77
|
+
"""The specified filter yields too many results.
|
|
78
|
+
|
|
79
|
+
Corresponds to scimType ``tooMany`` with HTTP status 400.
|
|
80
|
+
|
|
81
|
+
:rfc:`RFC 7644 Section 3.4.2.2 <7644#section-3.4.2.2>`
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
status = 400
|
|
85
|
+
scim_type = "tooMany"
|
|
86
|
+
_default_detail = (
|
|
87
|
+
"The specified filter yields many more results "
|
|
88
|
+
"than the server is willing to calculate or process"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class UniquenessException(SCIMException):
|
|
93
|
+
"""One or more attribute values are already in use or reserved.
|
|
94
|
+
|
|
95
|
+
Corresponds to scimType ``uniqueness`` with HTTP status 409.
|
|
96
|
+
|
|
97
|
+
:rfc:`RFC 7644 Section 3.3.1 <7644#section-3.3.1>`
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
status = 409
|
|
101
|
+
scim_type = "uniqueness"
|
|
102
|
+
_default_detail = (
|
|
103
|
+
"One or more of the attribute values are already in use or are reserved"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def __init__(
|
|
107
|
+
self, *, attribute: str | None = None, value: Any | None = None, **kw: Any
|
|
108
|
+
):
|
|
109
|
+
self.attribute = attribute
|
|
110
|
+
self.value = value
|
|
111
|
+
super().__init__(**kw)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class MutabilityException(SCIMException):
|
|
115
|
+
"""The attempted modification is not compatible with the attribute's mutability.
|
|
116
|
+
|
|
117
|
+
Corresponds to scimType ``mutability`` with HTTP status 400.
|
|
118
|
+
|
|
119
|
+
:rfc:`RFC 7644 Section 3.5.2 <7644#section-3.5.2>`
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
status = 400
|
|
123
|
+
scim_type = "mutability"
|
|
124
|
+
_default_detail = (
|
|
125
|
+
"The attempted modification is not compatible with the target attribute's "
|
|
126
|
+
"mutability or current state"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def __init__(
|
|
130
|
+
self,
|
|
131
|
+
*,
|
|
132
|
+
attribute: str | None = None,
|
|
133
|
+
mutability: str | None = None,
|
|
134
|
+
operation: str | None = None,
|
|
135
|
+
**kw: Any,
|
|
136
|
+
):
|
|
137
|
+
self.attribute = attribute
|
|
138
|
+
self.mutability = mutability
|
|
139
|
+
self.operation = operation
|
|
140
|
+
super().__init__(**kw)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class InvalidSyntaxException(SCIMException):
|
|
144
|
+
"""The request body message structure was invalid.
|
|
145
|
+
|
|
146
|
+
Corresponds to scimType ``invalidSyntax`` with HTTP status 400.
|
|
147
|
+
|
|
148
|
+
:rfc:`RFC 7644 Section 3.12 <7644#section-3.12>`
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
status = 400
|
|
152
|
+
scim_type = "invalidSyntax"
|
|
153
|
+
_default_detail = (
|
|
154
|
+
"The request body message structure was invalid "
|
|
155
|
+
"or did not conform to the request schema"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class InvalidPathException(SCIMException):
|
|
160
|
+
"""The path attribute was invalid or malformed.
|
|
161
|
+
|
|
162
|
+
Corresponds to scimType ``invalidPath`` with HTTP status 400.
|
|
163
|
+
|
|
164
|
+
:rfc:`RFC 7644 Section 3.5.2 <7644#section-3.5.2>`
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
status = 400
|
|
168
|
+
scim_type = "invalidPath"
|
|
169
|
+
_default_detail = "The path attribute was invalid or malformed"
|
|
170
|
+
|
|
171
|
+
def __init__(self, *, path: str | None = None, **kw: Any):
|
|
172
|
+
self.path = path
|
|
173
|
+
super().__init__(**kw)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class PathNotFoundException(InvalidPathException):
|
|
177
|
+
"""The path references a non-existent field.
|
|
178
|
+
|
|
179
|
+
This is a specialized form of :class:`InvalidPathException`.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
_default_detail = "The specified path references a non-existent field"
|
|
183
|
+
|
|
184
|
+
def __init__(self, *, path: str | None = None, field: str | None = None, **kw: Any):
|
|
185
|
+
self.field = field
|
|
186
|
+
super().__init__(path=path, **kw)
|
|
187
|
+
|
|
188
|
+
def __str__(self) -> str:
|
|
189
|
+
if self._detail:
|
|
190
|
+
return self._detail
|
|
191
|
+
if self.field:
|
|
192
|
+
return f"Field not found: {self.field}"
|
|
193
|
+
return self._default_detail
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class NoTargetException(SCIMException):
|
|
197
|
+
"""The specified path did not yield a target that could be operated on.
|
|
198
|
+
|
|
199
|
+
Corresponds to scimType ``noTarget`` with HTTP status 400.
|
|
200
|
+
|
|
201
|
+
:rfc:`RFC 7644 Section 3.5.2 <7644#section-3.5.2>`
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
status = 400
|
|
205
|
+
scim_type = "noTarget"
|
|
206
|
+
_default_detail = (
|
|
207
|
+
"The specified path did not yield an attribute or attribute value "
|
|
208
|
+
"that could be operated on"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def __init__(self, *, path: str | None = None, **kw: Any):
|
|
212
|
+
self.path = path
|
|
213
|
+
super().__init__(**kw)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class InvalidValueException(SCIMException):
|
|
217
|
+
"""A required value was missing or the value was not compatible.
|
|
218
|
+
|
|
219
|
+
Corresponds to scimType ``invalidValue`` with HTTP status 400.
|
|
220
|
+
|
|
221
|
+
:rfc:`RFC 7644 Section 3.12 <7644#section-3.12>`
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
status = 400
|
|
225
|
+
scim_type = "invalidValue"
|
|
226
|
+
_default_detail = (
|
|
227
|
+
"A required value was missing, or the value specified was not compatible "
|
|
228
|
+
"with the operation or attribute type, or resource schema"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
def __init__(
|
|
232
|
+
self, *, attribute: str | None = None, reason: str | None = None, **kw: Any
|
|
233
|
+
):
|
|
234
|
+
self.attribute = attribute
|
|
235
|
+
self.reason = reason
|
|
236
|
+
super().__init__(**kw)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class InvalidVersionException(SCIMException):
|
|
240
|
+
"""The specified SCIM protocol version is not supported.
|
|
241
|
+
|
|
242
|
+
Corresponds to scimType ``invalidVers`` with HTTP status 400.
|
|
243
|
+
|
|
244
|
+
:rfc:`RFC 7644 Section 3.13 <7644#section-3.13>`
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
status = 400
|
|
248
|
+
scim_type = "invalidVers"
|
|
249
|
+
_default_detail = "The specified SCIM protocol version is not supported"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class SensitiveException(SCIMException):
|
|
253
|
+
"""The request cannot be completed due to sensitive information in the URI.
|
|
254
|
+
|
|
255
|
+
Corresponds to scimType ``sensitive`` with HTTP status 400.
|
|
256
|
+
|
|
257
|
+
:rfc:`RFC 7644 Section 7.5.2 <7644#section-7.5.2>`
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
status = 400
|
|
261
|
+
scim_type = "sensitive"
|
|
262
|
+
_default_detail = (
|
|
263
|
+
"The specified request cannot be completed, due to the passing of sensitive "
|
|
264
|
+
"information in a request URI"
|
|
265
|
+
)
|
scim2_models/messages/bulk.py
CHANGED
|
@@ -5,8 +5,8 @@ from typing import Any
|
|
|
5
5
|
from pydantic import Field
|
|
6
6
|
from pydantic import PlainSerializer
|
|
7
7
|
|
|
8
|
-
from ..annotations import Required
|
|
9
8
|
from ..attributes import ComplexAttribute
|
|
9
|
+
from ..path import URN
|
|
10
10
|
from ..utils import _int_to_str
|
|
11
11
|
from .message import Message
|
|
12
12
|
|
|
@@ -53,9 +53,7 @@ class BulkRequest(Message):
|
|
|
53
53
|
The models for Bulk operations are defined, but their behavior is not implemented nor tested yet.
|
|
54
54
|
"""
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
"urn:ietf:params:scim:api:messages:2.0:BulkRequest"
|
|
58
|
-
]
|
|
56
|
+
__schema__ = URN("urn:ietf:params:scim:api:messages:2.0:BulkRequest")
|
|
59
57
|
|
|
60
58
|
fail_on_errors: int | None = None
|
|
61
59
|
"""An integer specifying the number of errors that the service provider
|
|
@@ -76,9 +74,7 @@ class BulkResponse(Message):
|
|
|
76
74
|
The models for Bulk operations are defined, but their behavior is not implemented nor tested yet.
|
|
77
75
|
"""
|
|
78
76
|
|
|
79
|
-
|
|
80
|
-
"urn:ietf:params:scim:api:messages:2.0:BulkResponse"
|
|
81
|
-
]
|
|
77
|
+
__schema__ = URN("urn:ietf:params:scim:api:messages:2.0:BulkResponse")
|
|
82
78
|
|
|
83
79
|
operations: list[BulkOperation] | None = Field(
|
|
84
80
|
None, serialization_alias="Operations"
|