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.
Files changed (31) hide show
  1. {scim2_server-0.8.1 → scim2_server-0.8.2}/PKG-INFO +1 -1
  2. {scim2_server-0.8.1 → scim2_server-0.8.2}/pyproject.toml +6 -1
  3. {scim2_server-0.8.1 → scim2_server-0.8.2}/pyproject.toml.orig +3 -1
  4. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testing.py +46 -9
  5. {scim2_server-0.8.1 → scim2_server-0.8.2}/LICENSE +0 -0
  6. {scim2_server-0.8.1 → scim2_server-0.8.2}/README.md +0 -0
  7. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/__init__.py +0 -0
  8. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/applications/__init__.py +0 -0
  9. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/applications/asgi.py +0 -0
  10. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/applications/base.py +0 -0
  11. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/applications/wsgi.py +0 -0
  12. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/bulk.py +0 -0
  13. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/conditions.py +0 -0
  14. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/cursor.py +0 -0
  15. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/errors.py +0 -0
  16. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/handler.py +0 -0
  17. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/memory.py +0 -0
  18. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/py.typed +0 -0
  19. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/requests.py +0 -0
  20. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/resources/default-resource-types.json +0 -0
  21. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/resources/default-schemas.json +0 -0
  22. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/resources/default-service-provider-config.json +0 -0
  23. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/responses.py +0 -0
  24. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/routing.py +0 -0
  25. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/service.py +0 -0
  26. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/storage.py +0 -0
  27. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testserver/__init__.py +0 -0
  28. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testserver/application.py +0 -0
  29. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testserver/cli.py +0 -0
  30. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/testserver/precis.py +0 -0
  31. {scim2_server-0.8.1 → scim2_server-0.8.2}/scim2_server/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: scim2-server
3
- Version: 0.8.1
3
+ Version: 0.8.2
4
4
  Summary: SCIM server core for any storage and web framework
5
5
  Keywords: scim,scim2,provisioning,rfc7643,rfc7644
6
6
  Author: Yaal Coop, Christian Friedrich Coors
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "scim2-server"
7
- version = "0.8.1"
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.1"
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
- storage.create(group_type, group_model(display_name="admins"))
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
- storage.create(group_type, group_model(display_name="admins"))
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
- storage.create(group_type, group_model(display_name="admins"))
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.search_request([user_model], filter=scim_filter)
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.search_request(
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 = storage.create(group_type, group_model(display_name="admins"))
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