scim2-models 0.5.0__py3-none-any.whl → 0.5.1__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/attributes.py +6 -7
- scim2_models/base.py +3 -3
- scim2_models/messages/bulk.py +11 -12
- scim2_models/messages/error.py +3 -4
- scim2_models/messages/list_response.py +4 -7
- scim2_models/messages/message.py +4 -5
- scim2_models/messages/patch_op.py +4 -6
- scim2_models/messages/search_request.py +15 -16
- scim2_models/reference.py +1 -1
- scim2_models/resources/enterprise_user.py +9 -10
- scim2_models/resources/group.py +6 -8
- scim2_models/resources/resource.py +25 -26
- scim2_models/resources/resource_type.py +8 -9
- scim2_models/resources/schema.py +22 -20
- scim2_models/resources/service_provider_config.py +26 -29
- scim2_models/resources/user.py +67 -69
- scim2_models/scim_object.py +3 -4
- scim2_models/urn.py +4 -5
- scim2_models/utils.py +10 -7
- {scim2_models-0.5.0.dist-info → scim2_models-0.5.1.dist-info}/METADATA +2 -2
- scim2_models-0.5.1.dist-info/RECORD +29 -0
- {scim2_models-0.5.0.dist-info → scim2_models-0.5.1.dist-info}/WHEEL +1 -1
- scim2_models-0.5.0.dist-info/RECORD +0 -29
scim2_models/resources/user.py
CHANGED
|
@@ -2,8 +2,6 @@ from enum import Enum
|
|
|
2
2
|
from typing import Annotated
|
|
3
3
|
from typing import ClassVar
|
|
4
4
|
from typing import Literal
|
|
5
|
-
from typing import Optional
|
|
6
|
-
from typing import Union
|
|
7
5
|
|
|
8
6
|
from pydantic import EmailStr
|
|
9
7
|
from pydantic import Field
|
|
@@ -22,27 +20,27 @@ from .resource import Resource
|
|
|
22
20
|
|
|
23
21
|
|
|
24
22
|
class Name(ComplexAttribute):
|
|
25
|
-
formatted:
|
|
23
|
+
formatted: str | None = None
|
|
26
24
|
"""The full name, including all middle names, titles, and suffixes as
|
|
27
25
|
appropriate, formatted for display (e.g., 'Ms. Barbara J Jensen, III')."""
|
|
28
26
|
|
|
29
|
-
family_name:
|
|
27
|
+
family_name: str | None = None
|
|
30
28
|
"""The family name of the User, or last name in most Western languages
|
|
31
29
|
(e.g., 'Jensen' given the full name 'Ms. Barbara J Jensen, III')."""
|
|
32
30
|
|
|
33
|
-
given_name:
|
|
31
|
+
given_name: str | None = None
|
|
34
32
|
"""The given name of the User, or first name in most Western languages
|
|
35
33
|
(e.g., 'Barbara' given the full name 'Ms. Barbara J Jensen, III')."""
|
|
36
34
|
|
|
37
|
-
middle_name:
|
|
35
|
+
middle_name: str | None = None
|
|
38
36
|
"""The middle name(s) of the User (e.g., 'Jane' given the full name 'Ms.
|
|
39
37
|
Barbara J Jensen, III')."""
|
|
40
38
|
|
|
41
|
-
honorific_prefix:
|
|
39
|
+
honorific_prefix: str | None = None
|
|
42
40
|
"""The honorific prefix(es) of the User, or title in most Western languages
|
|
43
41
|
(e.g., 'Ms.' given the full name 'Ms. Barbara J Jensen, III')."""
|
|
44
42
|
|
|
45
|
-
honorific_suffix:
|
|
43
|
+
honorific_suffix: str | None = None
|
|
46
44
|
"""The honorific suffix(es) of the User, or suffix in most Western
|
|
47
45
|
languages (e.g., 'III' given the full name 'Ms. Barbara J Jensen, III')."""
|
|
48
46
|
|
|
@@ -53,16 +51,16 @@ class Email(ComplexAttribute):
|
|
|
53
51
|
home = "home"
|
|
54
52
|
other = "other"
|
|
55
53
|
|
|
56
|
-
value:
|
|
54
|
+
value: EmailStr | None = None
|
|
57
55
|
"""Email addresses for the user."""
|
|
58
56
|
|
|
59
|
-
display:
|
|
57
|
+
display: str | None = None
|
|
60
58
|
"""A human-readable name, primarily used for display purposes."""
|
|
61
59
|
|
|
62
|
-
type:
|
|
60
|
+
type: Type | None = Field(None, examples=["work", "home", "other"])
|
|
63
61
|
"""A label indicating the attribute's function, e.g., 'work' or 'home'."""
|
|
64
62
|
|
|
65
|
-
primary:
|
|
63
|
+
primary: bool | None = None
|
|
66
64
|
"""A Boolean value indicating the 'primary' or preferred attribute value
|
|
67
65
|
for this attribute, e.g., the preferred mailing address or primary email
|
|
68
66
|
address."""
|
|
@@ -77,19 +75,19 @@ class PhoneNumber(ComplexAttribute):
|
|
|
77
75
|
pager = "pager"
|
|
78
76
|
other = "other"
|
|
79
77
|
|
|
80
|
-
value:
|
|
78
|
+
value: str | None = None
|
|
81
79
|
"""Phone number of the User."""
|
|
82
80
|
|
|
83
|
-
display:
|
|
81
|
+
display: str | None = None
|
|
84
82
|
"""A human-readable name, primarily used for display purposes."""
|
|
85
83
|
|
|
86
|
-
type:
|
|
84
|
+
type: Type | None = Field(
|
|
87
85
|
None, examples=["work", "home", "mobile", "fax", "pager", "other"]
|
|
88
86
|
)
|
|
89
87
|
"""A label indicating the attribute's function, e.g., 'work', 'home',
|
|
90
88
|
'mobile'."""
|
|
91
89
|
|
|
92
|
-
primary:
|
|
90
|
+
primary: bool | None = None
|
|
93
91
|
"""A Boolean value indicating the 'primary' or preferred attribute value
|
|
94
92
|
for this attribute, e.g., the preferred phone number or primary phone
|
|
95
93
|
number."""
|
|
@@ -106,19 +104,19 @@ class Im(ComplexAttribute):
|
|
|
106
104
|
qq = "qq"
|
|
107
105
|
yahoo = "yahoo"
|
|
108
106
|
|
|
109
|
-
value:
|
|
107
|
+
value: str | None = None
|
|
110
108
|
"""Instant messaging address for the User."""
|
|
111
109
|
|
|
112
|
-
display:
|
|
110
|
+
display: str | None = None
|
|
113
111
|
"""A human-readable name, primarily used for display purposes."""
|
|
114
112
|
|
|
115
|
-
type:
|
|
113
|
+
type: Type | None = Field(
|
|
116
114
|
None, examples=["aim", "gtalk", "icq", "xmpp", "msn", "skype", "qq", "yahoo"]
|
|
117
115
|
)
|
|
118
116
|
"""A label indicating the attribute's function, e.g., 'aim', 'gtalk',
|
|
119
117
|
'xmpp'."""
|
|
120
118
|
|
|
121
|
-
primary:
|
|
119
|
+
primary: bool | None = None
|
|
122
120
|
"""A Boolean value indicating the 'primary' or preferred attribute value
|
|
123
121
|
for this attribute, e.g., the preferred messenger or primary messenger."""
|
|
124
122
|
|
|
@@ -128,17 +126,17 @@ class Photo(ComplexAttribute):
|
|
|
128
126
|
photo = "photo"
|
|
129
127
|
thumbnail = "thumbnail"
|
|
130
128
|
|
|
131
|
-
value: Annotated[
|
|
129
|
+
value: Annotated[Reference[ExternalReference] | None, CaseExact.true] = None
|
|
132
130
|
"""URL of a photo of the User."""
|
|
133
131
|
|
|
134
|
-
display:
|
|
132
|
+
display: str | None = None
|
|
135
133
|
"""A human-readable name, primarily used for display purposes."""
|
|
136
134
|
|
|
137
|
-
type:
|
|
135
|
+
type: Type | None = Field(None, examples=["photo", "thumbnail"])
|
|
138
136
|
"""A label indicating the attribute's function, i.e., 'photo' or
|
|
139
137
|
'thumbnail'."""
|
|
140
138
|
|
|
141
|
-
primary:
|
|
139
|
+
primary: bool | None = None
|
|
142
140
|
"""A Boolean value indicating the 'primary' or preferred attribute value
|
|
143
141
|
for this attribute, e.g., the preferred photo or thumbnail."""
|
|
144
142
|
|
|
@@ -149,67 +147,67 @@ class Address(ComplexAttribute):
|
|
|
149
147
|
home = "home"
|
|
150
148
|
other = "other"
|
|
151
149
|
|
|
152
|
-
formatted:
|
|
150
|
+
formatted: str | None = None
|
|
153
151
|
"""The full mailing address, formatted for display or use with a mailing
|
|
154
152
|
label."""
|
|
155
153
|
|
|
156
|
-
street_address:
|
|
154
|
+
street_address: str | None = None
|
|
157
155
|
"""The full street address component, which may include house number,
|
|
158
156
|
street name, P.O.
|
|
159
157
|
|
|
160
158
|
box, and multi-line extended street address information.
|
|
161
159
|
"""
|
|
162
160
|
|
|
163
|
-
locality:
|
|
161
|
+
locality: str | None = None
|
|
164
162
|
"""The city or locality component."""
|
|
165
163
|
|
|
166
|
-
region:
|
|
164
|
+
region: str | None = None
|
|
167
165
|
"""The state or region component."""
|
|
168
166
|
|
|
169
|
-
postal_code:
|
|
167
|
+
postal_code: str | None = None
|
|
170
168
|
"""The zip code or postal code component."""
|
|
171
169
|
|
|
172
|
-
country:
|
|
170
|
+
country: str | None = None
|
|
173
171
|
"""The country name component."""
|
|
174
172
|
|
|
175
|
-
type:
|
|
173
|
+
type: Type | None = Field(None, examples=["work", "home", "other"])
|
|
176
174
|
"""A label indicating the attribute's function, e.g., 'work' or 'home'."""
|
|
177
175
|
|
|
178
|
-
primary:
|
|
176
|
+
primary: bool | None = None
|
|
179
177
|
"""A Boolean value indicating the 'primary' or preferred attribute value
|
|
180
178
|
for this attribute, e.g., the preferred photo or thumbnail."""
|
|
181
179
|
|
|
182
180
|
|
|
183
181
|
class Entitlement(ComplexAttribute):
|
|
184
|
-
value:
|
|
182
|
+
value: str | None = None
|
|
185
183
|
"""The value of an entitlement."""
|
|
186
184
|
|
|
187
|
-
display:
|
|
185
|
+
display: str | None = None
|
|
188
186
|
"""A human-readable name, primarily used for display purposes."""
|
|
189
187
|
|
|
190
|
-
type:
|
|
188
|
+
type: str | None = None
|
|
191
189
|
"""A label indicating the attribute's function."""
|
|
192
190
|
|
|
193
|
-
primary:
|
|
191
|
+
primary: bool | None = None
|
|
194
192
|
"""A Boolean value indicating the 'primary' or preferred attribute value
|
|
195
193
|
for this attribute."""
|
|
196
194
|
|
|
197
195
|
|
|
198
196
|
class GroupMembership(ComplexAttribute):
|
|
199
|
-
value: Annotated[
|
|
197
|
+
value: Annotated[str | None, Mutability.read_only] = None
|
|
200
198
|
"""The identifier of the User's group."""
|
|
201
199
|
|
|
202
200
|
ref: Annotated[
|
|
203
|
-
|
|
201
|
+
Reference[Literal["User"] | Literal["Group"]] | None,
|
|
204
202
|
Mutability.read_only,
|
|
205
203
|
] = Field(None, serialization_alias="$ref")
|
|
206
204
|
"""The reference URI of a target resource, if the attribute is a
|
|
207
205
|
reference."""
|
|
208
206
|
|
|
209
|
-
display: Annotated[
|
|
207
|
+
display: Annotated[str | None, Mutability.read_only] = None
|
|
210
208
|
"""A human-readable name, primarily used for display purposes."""
|
|
211
209
|
|
|
212
|
-
type: Annotated[
|
|
210
|
+
type: Annotated[str | None, Mutability.read_only] = Field(
|
|
213
211
|
None, examples=["direct", "indirect"]
|
|
214
212
|
)
|
|
215
213
|
"""A label indicating the attribute's function, e.g., 'direct' or
|
|
@@ -217,31 +215,31 @@ class GroupMembership(ComplexAttribute):
|
|
|
217
215
|
|
|
218
216
|
|
|
219
217
|
class Role(ComplexAttribute):
|
|
220
|
-
value:
|
|
218
|
+
value: str | None = None
|
|
221
219
|
"""The value of a role."""
|
|
222
220
|
|
|
223
|
-
display:
|
|
221
|
+
display: str | None = None
|
|
224
222
|
"""A human-readable name, primarily used for display purposes."""
|
|
225
223
|
|
|
226
|
-
type:
|
|
224
|
+
type: str | None = None
|
|
227
225
|
"""A label indicating the attribute's function."""
|
|
228
226
|
|
|
229
|
-
primary:
|
|
227
|
+
primary: bool | None = None
|
|
230
228
|
"""A Boolean value indicating the 'primary' or preferred attribute value
|
|
231
229
|
for this attribute."""
|
|
232
230
|
|
|
233
231
|
|
|
234
232
|
class X509Certificate(ComplexAttribute):
|
|
235
|
-
value: Annotated[
|
|
233
|
+
value: Annotated[Base64Bytes | None, CaseExact.true] = None
|
|
236
234
|
"""The value of an X.509 certificate."""
|
|
237
235
|
|
|
238
|
-
display:
|
|
236
|
+
display: str | None = None
|
|
239
237
|
"""A human-readable name, primarily used for display purposes."""
|
|
240
238
|
|
|
241
|
-
type:
|
|
239
|
+
type: str | None = None
|
|
242
240
|
"""A label indicating the attribute's function."""
|
|
243
241
|
|
|
244
|
-
primary:
|
|
242
|
+
primary: bool | None = None
|
|
245
243
|
"""A Boolean value indicating the 'primary' or preferred attribute value
|
|
246
244
|
for this attribute."""
|
|
247
245
|
|
|
@@ -251,101 +249,101 @@ class User(Resource[AnyExtension]):
|
|
|
251
249
|
"urn:ietf:params:scim:schemas:core:2.0:User"
|
|
252
250
|
]
|
|
253
251
|
|
|
254
|
-
user_name: Annotated[
|
|
252
|
+
user_name: Annotated[str | None, Uniqueness.server, Required.true] = None
|
|
255
253
|
"""Unique identifier for the User, typically used by the user to directly
|
|
256
254
|
authenticate to the service provider."""
|
|
257
255
|
|
|
258
|
-
name:
|
|
256
|
+
name: Name | None = None
|
|
259
257
|
"""The components of the user's real name."""
|
|
260
258
|
|
|
261
259
|
Name: ClassVar[type[ComplexAttribute]] = Name
|
|
262
260
|
|
|
263
|
-
display_name:
|
|
261
|
+
display_name: str | None = None
|
|
264
262
|
"""The name of the User, suitable for display to end-users."""
|
|
265
263
|
|
|
266
|
-
nick_name:
|
|
264
|
+
nick_name: str | None = None
|
|
267
265
|
"""The casual way to address the user in real life, e.g., 'Bob' or 'Bobby'
|
|
268
266
|
instead of 'Robert'."""
|
|
269
267
|
|
|
270
|
-
profile_url:
|
|
268
|
+
profile_url: Reference[ExternalReference] | None = None
|
|
271
269
|
"""A fully qualified URL pointing to a page representing the User's online
|
|
272
270
|
profile."""
|
|
273
271
|
|
|
274
|
-
title:
|
|
272
|
+
title: str | None = None
|
|
275
273
|
"""The user's title, such as "Vice President"."""
|
|
276
274
|
|
|
277
|
-
user_type:
|
|
275
|
+
user_type: str | None = None
|
|
278
276
|
"""Used to identify the relationship between the organization and the user.
|
|
279
277
|
|
|
280
278
|
Typical values used might be 'Contractor', 'Employee', 'Intern',
|
|
281
279
|
'Temp', 'External', and 'Unknown', but any value may be used.
|
|
282
280
|
"""
|
|
283
281
|
|
|
284
|
-
preferred_language:
|
|
282
|
+
preferred_language: str | None = None
|
|
285
283
|
"""Indicates the User's preferred written or spoken language.
|
|
286
284
|
|
|
287
285
|
Generally used for selecting a localized user interface; e.g.,
|
|
288
286
|
'en_US' specifies the language English and country US.
|
|
289
287
|
"""
|
|
290
288
|
|
|
291
|
-
locale:
|
|
289
|
+
locale: str | None = None
|
|
292
290
|
"""Used to indicate the User's default location for purposes of localizing
|
|
293
291
|
items such as currency, date time format, or numerical representations."""
|
|
294
292
|
|
|
295
|
-
timezone:
|
|
293
|
+
timezone: str | None = None
|
|
296
294
|
"""The User's time zone in the 'Olson' time zone database format, e.g.,
|
|
297
295
|
'America/Los_Angeles'."""
|
|
298
296
|
|
|
299
|
-
active:
|
|
297
|
+
active: bool | None = None
|
|
300
298
|
"""A Boolean value indicating the User's administrative status."""
|
|
301
299
|
|
|
302
|
-
password: Annotated[
|
|
300
|
+
password: Annotated[str | None, Mutability.write_only, Returned.never] = None
|
|
303
301
|
"""The User's cleartext password."""
|
|
304
302
|
|
|
305
|
-
emails:
|
|
303
|
+
emails: list[Email] | None = None
|
|
306
304
|
"""Email addresses for the user."""
|
|
307
305
|
|
|
308
306
|
Emails: ClassVar[type[ComplexAttribute]] = Email
|
|
309
307
|
|
|
310
|
-
phone_numbers:
|
|
308
|
+
phone_numbers: list[PhoneNumber] | None = None
|
|
311
309
|
"""Phone numbers for the User."""
|
|
312
310
|
|
|
313
311
|
PhoneNumbers: ClassVar[type[ComplexAttribute]] = PhoneNumber
|
|
314
312
|
|
|
315
|
-
ims:
|
|
313
|
+
ims: list[Im] | None = None
|
|
316
314
|
"""Instant messaging addresses for the User."""
|
|
317
315
|
|
|
318
316
|
Ims: ClassVar[type[ComplexAttribute]] = Im
|
|
319
317
|
|
|
320
|
-
photos:
|
|
318
|
+
photos: list[Photo] | None = None
|
|
321
319
|
"""URLs of photos of the User."""
|
|
322
320
|
|
|
323
321
|
Photos: ClassVar[type[ComplexAttribute]] = Photo
|
|
324
322
|
|
|
325
|
-
addresses:
|
|
323
|
+
addresses: list[Address] | None = None
|
|
326
324
|
"""A physical mailing address for this User."""
|
|
327
325
|
|
|
328
326
|
Addresses: ClassVar[type[ComplexAttribute]] = Address
|
|
329
327
|
|
|
330
|
-
groups: Annotated[
|
|
328
|
+
groups: Annotated[list[GroupMembership] | None, Mutability.read_only] = None
|
|
331
329
|
"""A list of groups to which the user belongs, either through direct
|
|
332
330
|
membership, through nested groups, or dynamically calculated."""
|
|
333
331
|
|
|
334
332
|
Groups: ClassVar[type[ComplexAttribute]] = GroupMembership
|
|
335
333
|
|
|
336
|
-
entitlements:
|
|
334
|
+
entitlements: list[Entitlement] | None = None
|
|
337
335
|
"""A list of entitlements for the User that represent a thing the User
|
|
338
336
|
has."""
|
|
339
337
|
|
|
340
338
|
Entitlements: ClassVar[type[ComplexAttribute]] = Entitlement
|
|
341
339
|
|
|
342
|
-
roles:
|
|
340
|
+
roles: list[Role] | None = None
|
|
343
341
|
"""A list of roles for the User that collectively represent who the User
|
|
344
342
|
is, e.g., 'Student', 'Faculty'."""
|
|
345
343
|
|
|
346
344
|
Roles: ClassVar[type[ComplexAttribute]] = Role
|
|
347
345
|
|
|
348
|
-
x509_certificates:
|
|
346
|
+
x509_certificates: list[X509Certificate] | None = None
|
|
349
347
|
"""A list of certificates issued to the User."""
|
|
350
348
|
|
|
351
349
|
X509Certificates: ClassVar[type[ComplexAttribute]] = X509Certificate
|
scim2_models/scim_object.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
from typing import Annotated
|
|
5
5
|
from typing import Any
|
|
6
|
-
from typing import Optional
|
|
7
6
|
|
|
8
7
|
from .annotations import Required
|
|
9
8
|
from .base import BaseModel
|
|
@@ -22,7 +21,7 @@ class ScimObject(BaseModel):
|
|
|
22
21
|
|
|
23
22
|
def _prepare_model_dump(
|
|
24
23
|
self,
|
|
25
|
-
scim_ctx:
|
|
24
|
+
scim_ctx: Context | None = Context.DEFAULT,
|
|
26
25
|
**kwargs: Any,
|
|
27
26
|
) -> dict[str, Any]:
|
|
28
27
|
kwargs.setdefault("context", {}).setdefault("scim", scim_ctx)
|
|
@@ -36,7 +35,7 @@ class ScimObject(BaseModel):
|
|
|
36
35
|
def model_dump(
|
|
37
36
|
self,
|
|
38
37
|
*args: Any,
|
|
39
|
-
scim_ctx:
|
|
38
|
+
scim_ctx: Context | None = Context.DEFAULT,
|
|
40
39
|
**kwargs: Any,
|
|
41
40
|
) -> dict[str, Any]:
|
|
42
41
|
"""Create a model representation that can be included in SCIM messages by using Pydantic :code:`BaseModel.model_dump`.
|
|
@@ -53,7 +52,7 @@ class ScimObject(BaseModel):
|
|
|
53
52
|
def model_dump_json(
|
|
54
53
|
self,
|
|
55
54
|
*args: Any,
|
|
56
|
-
scim_ctx:
|
|
55
|
+
scim_ctx: Context | None = Context.DEFAULT,
|
|
57
56
|
**kwargs: Any,
|
|
58
57
|
) -> str:
|
|
59
58
|
"""Create a JSON model representation that can be included in SCIM messages by using Pydantic :code:`BaseModel.model_dump_json`.
|
scim2_models/urn.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING
|
|
2
2
|
from typing import Any
|
|
3
|
-
from typing import Optional
|
|
4
3
|
from typing import Union
|
|
5
4
|
|
|
6
5
|
from .base import BaseModel
|
|
@@ -24,7 +23,7 @@ def _get_or_create_extension_instance(
|
|
|
24
23
|
return extension_instance
|
|
25
24
|
|
|
26
25
|
|
|
27
|
-
def _normalize_path(model:
|
|
26
|
+
def _normalize_path(model: type["BaseModel"] | None, path: str) -> tuple[str, str]:
|
|
28
27
|
"""Resolve a path to (schema_urn, attribute_path)."""
|
|
29
28
|
from .resources.resource import Resource
|
|
30
29
|
|
|
@@ -76,7 +75,7 @@ def _validate_model_attribute(model: type["BaseModel"], attribute_base: str) ->
|
|
|
76
75
|
|
|
77
76
|
def _validate_attribute_urn(
|
|
78
77
|
attribute_name: str, resource: type["Resource[Any]"]
|
|
79
|
-
) ->
|
|
78
|
+
) -> str | None:
|
|
80
79
|
"""Validate that an attribute urn is valid or not.
|
|
81
80
|
|
|
82
81
|
:param attribute_name: The attribute urn to check.
|
|
@@ -84,7 +83,7 @@ def _validate_attribute_urn(
|
|
|
84
83
|
"""
|
|
85
84
|
from .resources.resource import Resource
|
|
86
85
|
|
|
87
|
-
schema:
|
|
86
|
+
schema: Any | None
|
|
88
87
|
schema, attribute_base = _normalize_path(resource, attribute_name)
|
|
89
88
|
|
|
90
89
|
validated_resource = Resource.get_by_schema([resource], schema)
|
|
@@ -101,7 +100,7 @@ def _validate_attribute_urn(
|
|
|
101
100
|
|
|
102
101
|
def _resolve_path_to_target(
|
|
103
102
|
resource: "Resource[Any]", path: str
|
|
104
|
-
) -> tuple[
|
|
103
|
+
) -> tuple[Union["Resource[Any]", "Extension"] | None, str]:
|
|
105
104
|
"""Resolve a path to a target and an attribute_path.
|
|
106
105
|
|
|
107
106
|
The target can be the resource itself, or an extension object.
|
scim2_models/utils.py
CHANGED
|
@@ -3,7 +3,6 @@ import re
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
from typing import Annotated
|
|
5
5
|
from typing import Literal
|
|
6
|
-
from typing import Optional
|
|
7
6
|
from typing import Union
|
|
8
7
|
|
|
9
8
|
from pydantic import EncodedBytes
|
|
@@ -22,8 +21,12 @@ except ImportError:
|
|
|
22
21
|
# Python 3.9 has no UnionType
|
|
23
22
|
UNION_TYPES = [Union]
|
|
24
23
|
|
|
24
|
+
_UNDERSCORE_ALPHANUMERIC = re.compile(r"_+([0-9A-Za-z]+)")
|
|
25
|
+
_NON_WORD_UNDERSCORE = re.compile(r"[\W_]+")
|
|
26
|
+
_VALID_PATH_PATTERN = re.compile(r'^[a-zA-Z][a-zA-Z0-9._:\-\[\]"=\s]*$')
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
|
|
29
|
+
def _int_to_str(status: int | None) -> str | None:
|
|
27
30
|
return None if status is None else str(status)
|
|
28
31
|
|
|
29
32
|
|
|
@@ -87,7 +90,7 @@ def _to_camel(string: str) -> str:
|
|
|
87
90
|
'$ref' stays '$ref'.
|
|
88
91
|
"""
|
|
89
92
|
snake = to_snake(string)
|
|
90
|
-
camel =
|
|
93
|
+
camel = _UNDERSCORE_ALPHANUMERIC.sub(lambda m: m.group(1).title(), snake)
|
|
91
94
|
return camel
|
|
92
95
|
|
|
93
96
|
|
|
@@ -98,7 +101,7 @@ def _normalize_attribute_name(attribute_name: str) -> str:
|
|
|
98
101
|
"""
|
|
99
102
|
is_extension_attribute = ":" in attribute_name
|
|
100
103
|
if not is_extension_attribute:
|
|
101
|
-
attribute_name =
|
|
104
|
+
attribute_name = _NON_WORD_UNDERSCORE.sub("", attribute_name)
|
|
102
105
|
|
|
103
106
|
return attribute_name.lower()
|
|
104
107
|
|
|
@@ -122,7 +125,7 @@ def _validate_scim_path_syntax(path: str) -> bool:
|
|
|
122
125
|
|
|
123
126
|
# Cannot contain invalid characters (basic check)
|
|
124
127
|
# Allow alphanumeric, dots, underscores, hyphens, colons (for URNs), brackets
|
|
125
|
-
if not
|
|
128
|
+
if not _VALID_PATH_PATTERN.match(path):
|
|
126
129
|
return False
|
|
127
130
|
|
|
128
131
|
# If it contains a colon, validate it's a proper URN format
|
|
@@ -158,7 +161,7 @@ def _validate_scim_urn_syntax(path: str) -> bool:
|
|
|
158
161
|
return True
|
|
159
162
|
|
|
160
163
|
|
|
161
|
-
def _extract_field_name(path: str) ->
|
|
164
|
+
def _extract_field_name(path: str) -> str | None:
|
|
162
165
|
"""Extract the field name from a path.
|
|
163
166
|
|
|
164
167
|
For now, only handle simple paths (no filters, no complex expressions).
|
|
@@ -181,7 +184,7 @@ def _extract_field_name(path: str) -> Optional[str]:
|
|
|
181
184
|
return path
|
|
182
185
|
|
|
183
186
|
|
|
184
|
-
def _find_field_name(model_class: type["BaseModel"], attr_name: str) ->
|
|
187
|
+
def _find_field_name(model_class: type["BaseModel"], attr_name: str) -> str | None:
|
|
185
188
|
"""Find the actual field name in a resource class from an attribute name.
|
|
186
189
|
|
|
187
190
|
:param resource_class: The resource class to search in
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: scim2-models
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: SCIM2 models serialization and validation with pydantic
|
|
5
5
|
Keywords: scim,scim2,provisioning,pydantic,rfc7643,rfc7644
|
|
6
6
|
Author: Yaal Coop
|
|
@@ -219,7 +219,7 @@ Classifier: Environment :: Web Environment
|
|
|
219
219
|
Classifier: Programming Language :: Python
|
|
220
220
|
Classifier: Operating System :: OS Independent
|
|
221
221
|
Requires-Dist: pydantic[email]>=2.7.0
|
|
222
|
-
Requires-Python: >=3.
|
|
222
|
+
Requires-Python: >=3.10
|
|
223
223
|
Project-URL: changelog, https://scim2-models.readthedocs.io/en/latest/changelog.html
|
|
224
224
|
Project-URL: documentation, https://scim2-models.readthedocs.io
|
|
225
225
|
Project-URL: funding, https://github.com/sponsors/python-scim
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
scim2_models/__init__.py,sha256=IACL_c94UhKq0ZEXOb3LMZgeEQMfQhdOVpWGDnHSpXo,3201
|
|
2
|
+
scim2_models/annotations.py,sha256=oRjlKL1fqrYfa9UtaMdxF5fOT8CUUN3m-rdzvf7aiSA,3304
|
|
3
|
+
scim2_models/attributes.py,sha256=bgYWgNUAuLuzLL7JzVU71-Y8404rQc2NiTgq2OtdMNM,1713
|
|
4
|
+
scim2_models/base.py,sha256=RqRo5ju18Dbt-MB84KOtr3E_CZj9er-B7qDn12zKyyA,20400
|
|
5
|
+
scim2_models/constants.py,sha256=9egq8JW0dFAqPng85CiHoH5T6pRtYL87-gC0C-IMGsk,573
|
|
6
|
+
scim2_models/context.py,sha256=RjgMIvWPr8f41qbVL1sjaDnm9GRKyrCrgfC4npwwcMg,9149
|
|
7
|
+
scim2_models/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
scim2_models/messages/bulk.py,sha256=mI4eVJSVvW9LDfqWKnSUG4Pb0OOLoof8TASzmqQOr_M,2531
|
|
9
|
+
scim2_models/messages/error.py,sha256=04nMIVMzIIpCqI8sr9W6VTvCKL1gGT7AwRteJxvWBM0,6267
|
|
10
|
+
scim2_models/messages/list_response.py,sha256=Ft_ITnOiWCQ1HT3Ogc7PVq12P98G57Bbgqc2InvZmuY,2346
|
|
11
|
+
scim2_models/messages/message.py,sha256=Ty1-JX33kbGoVf4jP4TacJjp8rJrbNM9eMw8p18eoWM,4108
|
|
12
|
+
scim2_models/messages/patch_op.py,sha256=1nr6gNr6vd9dsZn8YvEl4RVy2xkZbUmwQpYTTws31As,21049
|
|
13
|
+
scim2_models/messages/search_request.py,sha256=Rnb9CQyas8TCfI-FHH_FrcWHUFhb-vtpVvHSGlCkdFM,4486
|
|
14
|
+
scim2_models/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
scim2_models/reference.py,sha256=LurTfNJnkz1NjtpSbT-GTzPxUTe3XtofN-uC4AFfsig,2411
|
|
16
|
+
scim2_models/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
scim2_models/resources/enterprise_user.py,sha256=e4HzNY1VtpyS4N8gEcHJnxXTCKACmlUAl3r7AmMaMjg,1751
|
|
18
|
+
scim2_models/resources/group.py,sha256=_KN-4ths_2kds0ihrzg2Gj43VVTykHtGriiJnlYhp3A,1345
|
|
19
|
+
scim2_models/resources/resource.py,sha256=viEXkWaG2uzxXya_-jnFL6l3xRKxy9Uf7TE9_xTcqRY,17413
|
|
20
|
+
scim2_models/resources/resource_type.py,sha256=xAp5tmRi6T4np-18V1H-6x2EBiBACQB3jNBBjRlnsDc,3295
|
|
21
|
+
scim2_models/resources/schema.py,sha256=n7lUXEI-hM-ZQ9niAE-9bGco8gMhUuLweRF_pIrShIE,10381
|
|
22
|
+
scim2_models/resources/service_provider_config.py,sha256=0VAkXmm3wi5agNLyqLv0M0qxAZ4pymfhsRFk6Y7KZB0,5373
|
|
23
|
+
scim2_models/resources/user.py,sha256=9VkMmKNTLC5D4tIUtg-Avlr_AD5mZMli4vdnO9FNMy0,11364
|
|
24
|
+
scim2_models/scim_object.py,sha256=4L10EKkrdaOCRf-J2P1P7yQaXYTztBhxH9rPQB-Bnnk,2367
|
|
25
|
+
scim2_models/urn.py,sha256=E2YY8Lx_yuhLXtdqU9rNOXcb_-9rnndAXd35ie_mQSA,4061
|
|
26
|
+
scim2_models/utils.py,sha256=YX_aQCLZY--s5isPEQT_iJygka1IKvP-F6Hly5fqjBI,5888
|
|
27
|
+
scim2_models-0.5.1.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
28
|
+
scim2_models-0.5.1.dist-info/METADATA,sha256=Y8-2HrRdIixXK9IqBK-6WhR8dPvG22O7HhMPDswVvYE,16485
|
|
29
|
+
scim2_models-0.5.1.dist-info/RECORD,,
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
scim2_models/__init__.py,sha256=20008bfdcf785212aad1911739bdcb31981e11031f42174e5695860e71d2a57a,3201
|
|
2
|
-
scim2_models/annotations.py,sha256=a118e528bd5faab61f6bd52d68c7711797ce4fc09450dde6fab773bdfeda8920,3304
|
|
3
|
-
scim2_models/attributes.py,sha256=5a9ab6a04246a09b9d4592ceaa7990c321e7f3d067114c49bc2c32d274baa178,1759
|
|
4
|
-
scim2_models/base.py,sha256=4c611456f69766fe6eb91f89fed47a30a79a3e365e18addc2cde933e8b702676,20409
|
|
5
|
-
scim2_models/constants.py,sha256=f5e82af095b474502a3e783ce42887a07e53ea946d60bf3bfa00b40be20c1ac9,573
|
|
6
|
-
scim2_models/context.py,sha256=46380c22f58fafc7f8d6a6d52f5b236839e6f4644acab0ab81f0b89e9c3070c8,9149
|
|
7
|
-
scim2_models/messages/__init__.py,sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,0
|
|
8
|
-
scim2_models/messages/bulk.py,sha256=b273c1ef6d95fccb20d4904571a4de116fec6be8edf8a1efe9f874ef53682828,2592
|
|
9
|
-
scim2_models/messages/error.py,sha256=fc81d4a05c25f9026220221c46485a5dfffd0bb4578225fb4cbff6e905c3a367,6304
|
|
10
|
-
scim2_models/messages/list_response.py,sha256=07e5c42c14e4b3a238e692a17804b91a8f71e480ca86b2c95aca82f32dc6d34b,2400
|
|
11
|
-
scim2_models/messages/message.py,sha256=aece1972be605e5f0a0ccb1c2ce60aa78b2983d96f1ffa37c3e467d91efa78dd,4118
|
|
12
|
-
scim2_models/messages/patch_op.py,sha256=643b2fb14dd740bd7da0c6555806931ecd88d75f83fddac9d63c44fbed2d7fad,21117
|
|
13
|
-
scim2_models/messages/search_request.py,sha256=eeffc02ee255eaa809fd50f985c76af4aa312058b00a01a3f50aab942ebde42a,4571
|
|
14
|
-
scim2_models/py.typed,sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,0
|
|
15
|
-
scim2_models/reference.py,sha256=1f06d8d0f2c5d7a06eff5cdac576eca6304ffc2b53ad091a2b628b983d72703d,2422
|
|
16
|
-
scim2_models/resources/__init__.py,sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,0
|
|
17
|
-
scim2_models/resources/enterprise_user.py,sha256=4d56b9692f9e2c7703930cabe7c85962c44a9340308d4694692161539d669f91,1806
|
|
18
|
-
scim2_models/resources/group.py,sha256=128b68e7ddc2953eb0a26efef1487d07d603578627f43cbb047e303b32b68d39,1422
|
|
19
|
-
scim2_models/resources/resource.py,sha256=e7081240a22f9e8f7600c021ac5aed90e51a37516318830de4e137ce154df5a4,17525
|
|
20
|
-
scim2_models/resources/resource_type.py,sha256=0e019e7d143d6d70c597502c30f6dfbb39c9a8cb217cac9459ba68d2fcecc9eb,3347
|
|
21
|
-
scim2_models/resources/schema.py,sha256=692eeaefcbf952e7258c5632d90aa3d34cc42fc90c50dbf1c74f81756240c423,10350
|
|
22
|
-
scim2_models/resources/service_provider_config.py,sha256=9de827d36b5f1222570db4349d8c5d785b14248a064526498fa9030b0630daa2,5487
|
|
23
|
-
scim2_models/resources/user.py,sha256=faf269b55bdd9089a08f133131ee92f7760d1bc066ab3aba5978cba1da2f631d,11624
|
|
24
|
-
scim2_models/scim_object.py,sha256=e9afa57fc88842194c63b94233b45c49d7b87f59040a7704d007bcb89e1169a0,2404
|
|
25
|
-
scim2_models/urn.py,sha256=2ea4e24678fadee6b28b850b49eb43a0834893fd8f0eaa3d9077c64c25766a24,4101
|
|
26
|
-
scim2_models/utils.py,sha256=b54a710fba69cfb6bf04c7754d00bd48e988375ee0c76d68ae313319d26bd23c,5765
|
|
27
|
-
scim2_models-0.5.0.dist-info/WHEEL,sha256=0f7d664a881437bddec71c703c3c2f01fd13581519f95130abcc96e296ef0426,79
|
|
28
|
-
scim2_models-0.5.0.dist-info/METADATA,sha256=059c7d8a77fb904d9cfb31ebe2a2d073134adb2d84e8003c0880ad0a11384df7,16484
|
|
29
|
-
scim2_models-0.5.0.dist-info/RECORD,,
|