scim2-server 0.8.1__tar.gz → 0.8.2__tar.gz
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_server-0.8.1 → scim2_server-0.8.2}/PKG-INFO +1 -1
- {scim2_server-0.8.1 → scim2_server-0.8.2}/pyproject.toml +6 -1
- {scim2_server-0.8.1 → scim2_server-0.8.2}/pyproject.toml.orig +3 -1
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testing.py +46 -9
- {scim2_server-0.8.1 → scim2_server-0.8.2}/LICENSE +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/README.md +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/__init__.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/applications/__init__.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/applications/asgi.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/applications/base.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/applications/wsgi.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/bulk.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/conditions.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/cursor.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/errors.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/handler.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/memory.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/py.typed +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/requests.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/resources/default-resource-types.json +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/resources/default-schemas.json +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/resources/default-service-provider-config.json +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/responses.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/routing.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/service.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/storage.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testserver/__init__.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testserver/application.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testserver/cli.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testserver/precis.py +0 -0
- {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/utils.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "scim2-server"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.2"
|
|
8
8
|
description = "SCIM server core for any storage and web framework"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
keywords = [
|
|
@@ -80,6 +80,7 @@ doc = [
|
|
|
80
80
|
]
|
|
81
81
|
|
|
82
82
|
[tool.uv]
|
|
83
|
+
required-version = ">=0.12.13"
|
|
83
84
|
exclude-newer = "14 days"
|
|
84
85
|
|
|
85
86
|
[tool.uv.exclude-newer-package]
|
|
@@ -198,6 +199,10 @@ uv_resolution = "lowest-direct"
|
|
|
198
199
|
|
|
199
200
|
[tool.tox.env.doc]
|
|
200
201
|
dependency_groups = ["doc"]
|
|
202
|
+
extras = [
|
|
203
|
+
"cursor",
|
|
204
|
+
"testing",
|
|
205
|
+
]
|
|
201
206
|
commands = [[
|
|
202
207
|
"sphinx-build",
|
|
203
208
|
"-W",
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "scim2-server"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.2"
|
|
8
8
|
description = "SCIM server core for any storage and web framework"
|
|
9
9
|
authors = [
|
|
10
10
|
{name="Yaal Coop", email="contact@yaal.coop"},
|
|
@@ -77,6 +77,7 @@ doc = [
|
|
|
77
77
|
]
|
|
78
78
|
|
|
79
79
|
[tool.uv]
|
|
80
|
+
required-version = ">=0.12.13"
|
|
80
81
|
exclude-newer = "14 days"
|
|
81
82
|
exclude-newer-package = { scim2-models = false, scim2-client = false, scim2-server = false, scim2-cli = false, scim2-tester = false, pytest-scim2-server = false }
|
|
82
83
|
|
|
@@ -181,6 +182,7 @@ uv_resolution = "lowest-direct"
|
|
|
181
182
|
|
|
182
183
|
[tool.tox.env.doc]
|
|
183
184
|
dependency_groups = ["doc"]
|
|
185
|
+
extras = ["cursor", "testing"]
|
|
184
186
|
commands = [
|
|
185
187
|
["sphinx-build", "-W", "--keep-going", "--builder", "html", "doc", "build/sphinx/html"],
|
|
186
188
|
]
|
|
@@ -29,6 +29,7 @@ from typing import Union
|
|
|
29
29
|
from typing import cast
|
|
30
30
|
|
|
31
31
|
import pytest
|
|
32
|
+
from pydantic import ValidationError
|
|
32
33
|
from scim2_models import Context
|
|
33
34
|
from scim2_models import InvalidFilterException
|
|
34
35
|
from scim2_models import InvalidPathException
|
|
@@ -232,6 +233,18 @@ class ScimStorageContract:
|
|
|
232
233
|
for user_name in user_names
|
|
233
234
|
]
|
|
234
235
|
|
|
236
|
+
def create_group(
|
|
237
|
+
self,
|
|
238
|
+
storage: ScimStorage,
|
|
239
|
+
group_type: ResourceType,
|
|
240
|
+
group_model: Any,
|
|
241
|
+
member: Any,
|
|
242
|
+
) -> Any:
|
|
243
|
+
return storage.create(
|
|
244
|
+
group_type,
|
|
245
|
+
group_model(display_name="admins", members=[{"value": member.id}]),
|
|
246
|
+
)
|
|
247
|
+
|
|
235
248
|
def test_create_fills_the_identifier_and_the_meta(
|
|
236
249
|
self, storage: Any, user_type: ResourceType, user_model: Any
|
|
237
250
|
) -> None:
|
|
@@ -497,8 +510,8 @@ class ScimStorageContract:
|
|
|
497
510
|
group_model: Any,
|
|
498
511
|
) -> None:
|
|
499
512
|
"""A search on a resource type returns its resources only, and counts them."""
|
|
500
|
-
self.create_users(storage, user_type, user_model, "alice", "bob")
|
|
501
|
-
|
|
513
|
+
users = self.create_users(storage, user_type, user_model, "alice", "bob")
|
|
514
|
+
self.create_group(storage, group_type, group_model, users[0])
|
|
502
515
|
|
|
503
516
|
page = self.search(storage, [user_type], self.search_request([user_model]))
|
|
504
517
|
|
|
@@ -594,8 +607,8 @@ class ScimStorageContract:
|
|
|
594
607
|
) -> None:
|
|
595
608
|
"""A search on several resource types returns their resources as one collection."""
|
|
596
609
|
self.require(self.supports_root_search, "searching at the root")
|
|
597
|
-
self.create_users(storage, user_type, user_model, "alice")
|
|
598
|
-
|
|
610
|
+
(user,) = self.create_users(storage, user_type, user_model, "alice")
|
|
611
|
+
self.create_group(storage, group_type, group_model, user)
|
|
599
612
|
|
|
600
613
|
page = self.search(
|
|
601
614
|
storage,
|
|
@@ -621,8 +634,8 @@ class ScimStorageContract:
|
|
|
621
634
|
"""An attribute a resource type does not declare matches none of its resources."""
|
|
622
635
|
self.require(self.supports_root_search, "searching at the root")
|
|
623
636
|
self.require(self.announces(provider, "filter"), "filtering")
|
|
624
|
-
self.create_users(storage, user_type, user_model, "alice")
|
|
625
|
-
|
|
637
|
+
(user,) = self.create_users(storage, user_type, user_model, "alice")
|
|
638
|
+
self.create_group(storage, group_type, group_model, user)
|
|
626
639
|
|
|
627
640
|
page = self.search(
|
|
628
641
|
storage,
|
|
@@ -640,6 +653,21 @@ class ScimStorageContract:
|
|
|
640
653
|
"""
|
|
641
654
|
return SEARCH_USERS
|
|
642
655
|
|
|
656
|
+
def search_request_or_skip(
|
|
657
|
+
self, models: list[type[Resource[Any]]], **parameters: Any
|
|
658
|
+
) -> SearchRequest[Any]:
|
|
659
|
+
"""Build a search request, and skip the current test when the models do not declare an attribute of the filter or the sort."""
|
|
660
|
+
try:
|
|
661
|
+
return self.search_request(models, **parameters)
|
|
662
|
+
except ValidationError as exception:
|
|
663
|
+
errors = exception.errors()
|
|
664
|
+
if any(
|
|
665
|
+
error["type"] not in ("scim_invalidFilter", "scim_invalidPath")
|
|
666
|
+
for error in errors
|
|
667
|
+
):
|
|
668
|
+
raise
|
|
669
|
+
pytest.skip(f"The models refuse the search: {errors[0]['msg']}")
|
|
670
|
+
|
|
643
671
|
def search_or_skip(
|
|
644
672
|
self,
|
|
645
673
|
storage: Any,
|
|
@@ -666,7 +694,7 @@ class ScimStorageContract:
|
|
|
666
694
|
for user in self.search_users():
|
|
667
695
|
storage.create(user_type, user_model.model_validate(user))
|
|
668
696
|
stored = self.search(storage, [user_type], self.search_request([user_model]))
|
|
669
|
-
search_request = self.
|
|
697
|
+
search_request = self.search_request_or_skip([user_model], filter=scim_filter)
|
|
670
698
|
|
|
671
699
|
page = self.search_or_skip(storage, [user_type], search_request)
|
|
672
700
|
|
|
@@ -694,7 +722,7 @@ class ScimStorageContract:
|
|
|
694
722
|
for user in self.search_users():
|
|
695
723
|
storage.create(user_type, user_model.model_validate(user))
|
|
696
724
|
stored = self.search(storage, [user_type], self.search_request([user_model]))
|
|
697
|
-
search_request = self.
|
|
725
|
+
search_request = self.search_request_or_skip(
|
|
698
726
|
[user_model], sort_by=sort_by, sort_order=sort_order
|
|
699
727
|
)
|
|
700
728
|
|
|
@@ -743,6 +771,7 @@ class ScimStorageContract:
|
|
|
743
771
|
self, storage: Any, user_type: ResourceType, user_model: Any
|
|
744
772
|
) -> None:
|
|
745
773
|
"""Following the next cursors returns every resource once, count by count."""
|
|
774
|
+
self.require(storage.supports_cursors, "cursor pagination")
|
|
746
775
|
names = ["alice", "bob", "carol", "dave", "erin"]
|
|
747
776
|
self.create_users(storage, user_type, user_model, *names)
|
|
748
777
|
|
|
@@ -763,6 +792,7 @@ class ScimStorageContract:
|
|
|
763
792
|
user_model: Any,
|
|
764
793
|
) -> None:
|
|
765
794
|
"""The pages of a sorted search follow each other in the sort order."""
|
|
795
|
+
self.require(storage.supports_cursors, "cursor pagination")
|
|
766
796
|
self.require(self.announces(provider, "sort"), "sorting")
|
|
767
797
|
self.create_users(
|
|
768
798
|
storage, user_type, user_model, "dave", "alice", "carol", "bob"
|
|
@@ -795,7 +825,9 @@ class ScimStorageContract:
|
|
|
795
825
|
user_model: Any,
|
|
796
826
|
) -> None:
|
|
797
827
|
"""Resources with the same sort value, or without one, are returned once, even across pages."""
|
|
828
|
+
self.require(storage.supports_cursors, "cursor pagination")
|
|
798
829
|
self.require(self.announces(provider, "sort"), "sorting")
|
|
830
|
+
self.require("nick_name" in user_model.model_fields, "nickName")
|
|
799
831
|
for user_name, nick_name in [
|
|
800
832
|
("alice", "twin"),
|
|
801
833
|
("bob", None),
|
|
@@ -821,6 +853,7 @@ class ScimStorageContract:
|
|
|
821
853
|
self, storage: Any, user_type: ResourceType, user_model: Any
|
|
822
854
|
) -> None:
|
|
823
855
|
"""Following the previous cursors from the last page returns the same pages, back to the first."""
|
|
856
|
+
self.require(storage.supports_cursors, "cursor pagination")
|
|
824
857
|
self.create_users(
|
|
825
858
|
storage, user_type, user_model, "alice", "bob", "carol", "dave", "erin"
|
|
826
859
|
)
|
|
@@ -839,6 +872,7 @@ class ScimStorageContract:
|
|
|
839
872
|
self, storage: Any, user_type: ResourceType, user_model: Any, deleted: str
|
|
840
873
|
) -> None:
|
|
841
874
|
"""Deleting a resource already returned does not skip nor repeat the next ones."""
|
|
875
|
+
self.require(storage.supports_cursors, "cursor pagination")
|
|
842
876
|
self.create_users(
|
|
843
877
|
storage, user_type, user_model, "alice", "bob", "carol", "dave", "erin"
|
|
844
878
|
)
|
|
@@ -860,6 +894,7 @@ class ScimStorageContract:
|
|
|
860
894
|
self, storage: Any, user_type: ResourceType, user_model: Any
|
|
861
895
|
) -> None:
|
|
862
896
|
"""Creating a resource between two pages does not skip nor repeat the existing ones."""
|
|
897
|
+
self.require(storage.supports_cursors, "cursor pagination")
|
|
863
898
|
self.create_users(
|
|
864
899
|
storage, user_type, user_model, "alice", "bob", "carol", "dave", "erin"
|
|
865
900
|
)
|
|
@@ -881,6 +916,7 @@ class ScimStorageContract:
|
|
|
881
916
|
self, storage: Any, user_type: ResourceType, user_model: Any
|
|
882
917
|
) -> None:
|
|
883
918
|
"""Deleting the first resource of a page does not change the page before it."""
|
|
919
|
+
self.require(storage.supports_cursors, "cursor pagination")
|
|
884
920
|
self.create_users(
|
|
885
921
|
storage, user_type, user_model, "alice", "bob", "carol", "dave", "erin"
|
|
886
922
|
)
|
|
@@ -920,9 +956,10 @@ class ScimStorageContract:
|
|
|
920
956
|
group_model: Any,
|
|
921
957
|
) -> None:
|
|
922
958
|
"""A cursor pages the resources of several resource types as one collection."""
|
|
959
|
+
self.require(storage.supports_cursors, "cursor pagination")
|
|
923
960
|
self.require(self.supports_root_search, "searching at the root")
|
|
924
961
|
users = self.create_users(storage, user_type, user_model, "alice", "bob")
|
|
925
|
-
group =
|
|
962
|
+
group = self.create_group(storage, group_type, group_model, users[0])
|
|
926
963
|
|
|
927
964
|
pages = self.walk(
|
|
928
965
|
storage,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/resources/default-resource-types.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|