alpha-python 0.7.4__tar.gz → 0.7.5__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 (158) hide show
  1. {alpha_python-0.7.4 → alpha_python-0.7.5}/PKG-INFO +1 -1
  2. {alpha_python-0.7.4 → alpha_python-0.7.5}/pyproject.toml +1 -1
  3. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/adapters/sqla_unit_of_work.py +11 -3
  4. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/providers/ldap_provider.py +11 -0
  5. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/providers/models/identity.py +43 -6
  6. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/providers/oidc_provider.py +11 -1
  7. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha_python.egg-info/PKG-INFO +1 -1
  8. {alpha_python-0.7.4 → alpha_python-0.7.5}/LICENSE +0 -0
  9. {alpha_python-0.7.4 → alpha_python-0.7.5}/README.md +0 -0
  10. {alpha_python-0.7.4 → alpha_python-0.7.5}/setup.cfg +0 -0
  11. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/__init__.py +0 -0
  12. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/adapters/__init__.py +0 -0
  13. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/adapters/rest_api_unit_of_work.py +0 -0
  14. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/cli.py +0 -0
  15. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/containers/__init__.py +0 -0
  16. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/containers/container.py +0 -0
  17. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/domain/__init__.py +0 -0
  18. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/domain/models/__init__.py +0 -0
  19. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/domain/models/base_model.py +0 -0
  20. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/domain/models/group.py +0 -0
  21. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/domain/models/life_cycle_base.py +0 -0
  22. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/domain/models/role.py +0 -0
  23. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/domain/models/user.py +0 -0
  24. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/encoder.py +0 -0
  25. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/exceptions.py +0 -0
  26. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/__init__.py +0 -0
  27. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/_type_conversion_matrix.py +0 -0
  28. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/_type_mapping.py +0 -0
  29. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/class_factories.py +0 -0
  30. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/default_field_factory.py +0 -0
  31. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/field_iterator.py +0 -0
  32. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/jwt_factory.py +0 -0
  33. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/logging_handler_factory.py +0 -0
  34. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/model_class_factory.py +0 -0
  35. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/models/__init__.py +0 -0
  36. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/models/factory_classes.py +0 -0
  37. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/password_factory.py +0 -0
  38. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/request_factory.py +0 -0
  39. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/response_factory.py +0 -0
  40. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/factories/type_factories.py +0 -0
  41. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/__init__.py +0 -0
  42. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/api_generate_handler.py +0 -0
  43. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/api_run_handler.py +0 -0
  44. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/base_handler.py +0 -0
  45. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/gen-code.sh +0 -0
  46. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/models/__init__.py +0 -0
  47. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/models/argument.py +0 -0
  48. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/models/command.py +0 -0
  49. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/models/section.py +0 -0
  50. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/models/subparser.py +0 -0
  51. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/run-api.sh +0 -0
  52. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/__init__.py +0 -0
  53. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/Dockerfile.mustache +0 -0
  54. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/README.mustache +0 -0
  55. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/__init__model.mustache +0 -0
  56. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/__init__test.mustache +0 -0
  57. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/__main__.mustache +0 -0
  58. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/base_model.mustache +0 -0
  59. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/controller.mustache +0 -0
  60. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/controller_test.mustache +0 -0
  61. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/dockerignore.mustache +0 -0
  62. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/encoder.mustache +0 -0
  63. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/git_push.sh.mustache +0 -0
  64. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/gitignore.mustache +0 -0
  65. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/model.mustache +0 -0
  66. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/openapi.mustache +0 -0
  67. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/param_type.mustache +0 -0
  68. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/requirements.mustache +0 -0
  69. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/security_controller_.mustache +0 -0
  70. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/setup.mustache +0 -0
  71. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/test-requirements.mustache +0 -0
  72. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/tox.mustache +0 -0
  73. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/travis.mustache +0 -0
  74. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/typing_utils.mustache +0 -0
  75. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/handlers/templates/python-flask/util.mustache +0 -0
  76. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/__init__.py +0 -0
  77. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/connectors/__init__.py +0 -0
  78. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/connectors/ldap_connector.py +0 -0
  79. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/connectors/oidc_connector.py +0 -0
  80. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/connectors/sql_alchemy.py +0 -0
  81. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/databases/__init__.py +0 -0
  82. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/databases/sql_alchemy.py +0 -0
  83. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/models/__init__.py +0 -0
  84. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/models/filter_operators.py +0 -0
  85. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/models/json_patch.py +0 -0
  86. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/models/order_by.py +0 -0
  87. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/models/query_clause.py +0 -0
  88. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/infra/models/search_filter.py +0 -0
  89. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/__init__.py +0 -0
  90. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/api_repository.py +0 -0
  91. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/attrs_instance.py +0 -0
  92. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/dataclass_instance.py +0 -0
  93. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/factories.py +0 -0
  94. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/handler.py +0 -0
  95. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/http_client.py +0 -0
  96. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/openapi_model.py +0 -0
  97. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/patchable.py +0 -0
  98. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/providers.py +0 -0
  99. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/pydantic_instance.py +0 -0
  100. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/refresh_repository.py +0 -0
  101. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/sql_database.py +0 -0
  102. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/sql_mapper.py +0 -0
  103. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/sql_repository.py +0 -0
  104. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/token_factory.py +0 -0
  105. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/unit_of_work.py +0 -0
  106. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/interfaces/updatable.py +0 -0
  107. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/mixins/__init__.py +0 -0
  108. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/mixins/group_lifecycle.py +0 -0
  109. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/mixins/jwt_provider.py +0 -0
  110. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/mixins/user_lifecycle.py +0 -0
  111. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/providers/__init__.py +0 -0
  112. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/providers/api_key_provider.py +0 -0
  113. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/providers/database_provider.py +0 -0
  114. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/providers/models/__init__.py +0 -0
  115. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/providers/models/credentials.py +0 -0
  116. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/providers/models/token.py +0 -0
  117. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/py.typed +0 -0
  118. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/__init__.py +0 -0
  119. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/models/__init__.py +0 -0
  120. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/models/repository_model.py +0 -0
  121. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/refresh/__init__.py +0 -0
  122. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/refresh/cache_repository.py +0 -0
  123. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/refresh/database_repository.py +0 -0
  124. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/refresh/file_repository.py +0 -0
  125. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/refresh/memory_repository.py +0 -0
  126. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/rest_api_repository.py +0 -0
  127. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/repositories/sql_alchemy_repository.py +0 -0
  128. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/services/__init__.py +0 -0
  129. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/services/authentication_service.py +0 -0
  130. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/services/models/__init__.py +0 -0
  131. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/services/models/cookie.py +0 -0
  132. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/services/user_lifecycle_management.py +0 -0
  133. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/__init__.py +0 -0
  134. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/_http_codes.py +0 -0
  135. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/cookie.py +0 -0
  136. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/is_attrs.py +0 -0
  137. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/is_pydantic.py +0 -0
  138. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/logging_configurator.py +0 -0
  139. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/logging_level_checker.py +0 -0
  140. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/openapi_test/__init__.py +0 -0
  141. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/openapi_test/container.py +0 -0
  142. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/openapi_test/exceptions.py +0 -0
  143. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/openapi_test/models.py +0 -0
  144. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/openapi_test/orm.py +0 -0
  145. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/openapi_test/response.py +0 -0
  146. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/openapi_test/service.py +0 -0
  147. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/request_headers.py +0 -0
  148. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/response_object.py +0 -0
  149. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/secret_generator.py +0 -0
  150. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/verify_identity.py +0 -0
  151. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha/utils/version_checker.py +0 -0
  152. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha_python.egg-info/SOURCES.txt +0 -0
  153. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha_python.egg-info/dependency_links.txt +0 -0
  154. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha_python.egg-info/entry_points.txt +0 -0
  155. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha_python.egg-info/requires.txt +0 -0
  156. {alpha_python-0.7.4 → alpha_python-0.7.5}/src/alpha_python.egg-info/top_level.txt +0 -0
  157. {alpha_python-0.7.4 → alpha_python-0.7.5}/tests/test_cli.py +0 -0
  158. {alpha_python-0.7.4 → alpha_python-0.7.5}/tests/test_encoder.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpha-python
3
- Version: 0.7.4
3
+ Version: 0.7.5
4
4
  Summary: Alpha is intended to be the first dependency you need to add to your Python application. It is a Python library which contains standard building blocks that can be used in applications that are used as APIs and/or make use of database interaction.
5
5
  Author-email: Bart Reijling <bart@reijling.eu>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "alpha-python"
3
- version = "0.7.4"
3
+ version = "0.7.5"
4
4
  description = "Alpha is intended to be the first dependency you need to add to your Python application. It is a Python library which contains standard building blocks that can be used in applications that are used as APIs and/or make use of database interaction."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -78,14 +78,22 @@ class SqlAlchemyUnitOfWork:
78
78
 
79
79
  return self
80
80
 
81
- def __exit__(self, *args: Any) -> None:
82
- """Finalize the Unit of Work context."""
81
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
82
+ """Finalize the Unit of Work context.
83
+
84
+ Roll back only when leaving the context because of an exception.
85
+ Rolling back after a successful commit can expire ORM state and may
86
+ trigger DetachedInstanceError once the session is closed/detached.
87
+ """
83
88
  if not self._session:
84
89
  raise exceptions.DatabaseSessionError(
85
90
  "No active database session is defined"
86
91
  )
92
+
93
+ if exc_type is not None:
94
+ self._session.rollback()
95
+
87
96
  self._session.close()
88
- self.rollback()
89
97
  self._session = None # type: ignore
90
98
 
91
99
  def commit(self) -> None:
@@ -45,6 +45,7 @@ class LDAPProvider(JWTProviderMixin):
45
45
  populate_groups: bool = True,
46
46
  populate_permissions: bool = False,
47
47
  populate_claims: bool = True,
48
+ flatten_claims: bool = True,
48
49
  auto_connect: bool = True,
49
50
  change_password_supported: bool = False,
50
51
  additional_connector_params: dict[str, Any] | None = None,
@@ -71,6 +72,9 @@ class LDAPProvider(JWTProviderMixin):
71
72
  Whether to populate permissions in the Identity, by default False
72
73
  populate_claims
73
74
  Whether to populate claims in the Identity, by default True
75
+ flatten_claims
76
+ Whether to flatten single-value claims in the Identity, by default
77
+ True
74
78
  auto_connect
75
79
  Whether to automatically connect using the connector, by default
76
80
  True
@@ -89,6 +93,7 @@ class LDAPProvider(JWTProviderMixin):
89
93
  self._populate_groups = populate_groups
90
94
  self._populate_permissions = populate_permissions
91
95
  self._populate_claims = populate_claims
96
+ self._flatten_claims = flatten_claims
92
97
  self._auto_connect = auto_connect
93
98
  self._change_password_supported = change_password_supported
94
99
  self._additional_connector_params = additional_connector_params or {
@@ -297,6 +302,7 @@ class LDAPProvider(JWTProviderMixin):
297
302
  populate_claims=self._populate_claims,
298
303
  populate_groups=self._populate_groups,
299
304
  populate_permissions=self._populate_permissions,
305
+ flatten_claims=self._flatten_claims,
300
306
  )
301
307
  return identity
302
308
 
@@ -324,6 +330,7 @@ class ADProvider(LDAPProvider):
324
330
  populate_groups: bool = True,
325
331
  populate_permissions: bool = False,
326
332
  populate_claims: bool = True,
333
+ flatten_claims: bool = True,
327
334
  auto_connect: bool = True,
328
335
  change_password_supported: bool = False,
329
336
  ) -> None:
@@ -356,6 +363,9 @@ class ADProvider(LDAPProvider):
356
363
  populate_claims
357
364
  Whether to populate claims on the :class:`Identity`, by default
358
365
  True.
366
+ flatten_claims
367
+ Whether to flatten single-value claims in the Identity, by default
368
+ True
359
369
  auto_connect
360
370
  Whether to automatically open the LDAP connection on first use, by
361
371
  default True.
@@ -373,6 +383,7 @@ class ADProvider(LDAPProvider):
373
383
  populate_groups=populate_groups,
374
384
  populate_permissions=populate_permissions,
375
385
  populate_claims=populate_claims,
386
+ flatten_claims=flatten_claims,
376
387
  auto_connect=auto_connect,
377
388
  change_password_supported=change_password_supported,
378
389
  )
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- from typing import Mapping, Any, Self, Sequence, TYPE_CHECKING
4
+ from typing import Mapping, Any, Self, Sequence, TYPE_CHECKING, cast
5
5
  from datetime import datetime, timezone
6
6
 
7
7
  from alpha.domain.models.group import Group
@@ -160,6 +160,7 @@ class Identity:
160
160
  populate_groups: bool = True,
161
161
  populate_permissions: bool = False,
162
162
  populate_claims: bool = True,
163
+ flatten_claims: bool = False,
163
164
  ) -> Identity:
164
165
  """Instantiate an Identity from an LDAP entry dictionary.
165
166
 
@@ -178,6 +179,9 @@ class Identity:
178
179
  populate_claims
179
180
  Whether to populate the claims dictionary from the LDAP entry, by
180
181
  default True
182
+ flatten_claims
183
+ Whether to replace claim lists containing a single value with that
184
+ value, by default False
181
185
 
182
186
  Returns
183
187
  -------
@@ -188,6 +192,12 @@ class Identity:
188
192
  if not username:
189
193
  username = cls._get_key(entry, mappings["subject"])
190
194
 
195
+ claims = (
196
+ cls._remove_password_from_claims(entry) if populate_claims else {}
197
+ )
198
+ if populate_claims and flatten_claims:
199
+ claims = cls.flatten_single_value_claims(claims)
200
+
191
201
  return cls(
192
202
  subject=cls._get_key(entry, mappings["subject"], ""),
193
203
  username=username,
@@ -204,11 +214,7 @@ class Identity:
204
214
  if populate_permissions
205
215
  else []
206
216
  ),
207
- claims=(
208
- cls._remove_password_from_claims(entry)
209
- if populate_claims
210
- else {}
211
- ),
217
+ claims=claims,
212
218
  issued_at=datetime.now(tz=timezone.utc),
213
219
  )
214
220
 
@@ -430,6 +436,37 @@ class Identity:
430
436
  }
431
437
  return filtered_claims
432
438
 
439
+ @staticmethod
440
+ def flatten_single_value_claims(
441
+ claims: Mapping[str, Any],
442
+ ) -> Mapping[str, Any]:
443
+ """Replace single-value claim lists with their contained value.
444
+
445
+ Password-related claims (keys containing "password", case-insensitive)
446
+ are removed before flattening.
447
+
448
+ Parameters
449
+ ----------
450
+ claims
451
+ Original claims dictionary.
452
+
453
+ Returns
454
+ -------
455
+ Mapping[str, Any]
456
+ A new claims dictionary with password-related keys removed and
457
+ single-value lists replaced by their contained value.
458
+ """
459
+ claims = Identity._remove_password_from_claims(claims)
460
+
461
+ return {
462
+ key: (
463
+ value[0]
464
+ if isinstance(value, list) and len(cast(list[Any], value)) == 1
465
+ else value
466
+ )
467
+ for key, value in claims.items()
468
+ }
469
+
433
470
  @staticmethod
434
471
  def _extract_groups(
435
472
  entry: Mapping[str, Any],
@@ -57,6 +57,7 @@ class OIDCProvider(JWTProviderMixin):
57
57
  populate_groups: bool = True,
58
58
  populate_permissions: bool = False,
59
59
  populate_claims: bool = False,
60
+ flatten_claims: bool = True,
60
61
  change_password_supported: bool = False,
61
62
  ) -> None:
62
63
  """Initialize OIDCProvider.
@@ -90,6 +91,7 @@ class OIDCProvider(JWTProviderMixin):
90
91
  self._populate_groups = populate_groups
91
92
  self._populate_permissions = populate_permissions
92
93
  self._populate_claims = populate_claims
94
+ self._flatten_claims = flatten_claims
93
95
  self._change_password_supported = change_password_supported
94
96
 
95
97
  def authenticate(self, credentials: PasswordCredentials) -> Identity:
@@ -237,6 +239,9 @@ class OIDCProvider(JWTProviderMixin):
237
239
 
238
240
  audience = self._extract_audience(claims)
239
241
 
242
+ if self._populate_claims and self._flatten_claims:
243
+ claims = Identity.flatten_single_value_claims(claims)
244
+
240
245
  identity = Identity(
241
246
  subject=str(subject),
242
247
  username=username,
@@ -244,7 +249,7 @@ class OIDCProvider(JWTProviderMixin):
244
249
  display_name=self._get_claim(claims, "display_name"),
245
250
  groups=groups,
246
251
  permissions=permissions,
247
- claims=dict(claims) if self._populate_claims else {},
252
+ claims=claims if self._populate_claims else {},
248
253
  issued_at=issued_at,
249
254
  audience=audience,
250
255
  role=self._get_claim(claims, "role"),
@@ -403,6 +408,7 @@ class KeyCloakProvider(OIDCProvider):
403
408
  populate_groups: bool = True,
404
409
  populate_permissions: bool = False,
405
410
  populate_claims: bool = False,
411
+ flatten_claims: bool = True,
406
412
  change_password_supported: bool = False,
407
413
  ) -> None:
408
414
  """Initialize KeyCloakProvider.
@@ -427,6 +433,9 @@ class KeyCloakProvider(OIDCProvider):
427
433
  Whether to populate permissions on the Identity.
428
434
  populate_claims
429
435
  Whether to include raw claims on the Identity.
436
+ flatten_claims
437
+ Whether to flatten single-value claims when populating raw claims
438
+ on the Identity.
430
439
  change_password_supported
431
440
  Whether this provider supports changing passwords.
432
441
  """
@@ -437,5 +446,6 @@ class KeyCloakProvider(OIDCProvider):
437
446
  populate_groups=populate_groups,
438
447
  populate_permissions=populate_permissions,
439
448
  populate_claims=populate_claims,
449
+ flatten_claims=flatten_claims,
440
450
  change_password_supported=change_password_supported,
441
451
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alpha-python
3
- Version: 0.7.4
3
+ Version: 0.7.5
4
4
  Summary: Alpha is intended to be the first dependency you need to add to your Python application. It is a Python library which contains standard building blocks that can be used in applications that are used as APIs and/or make use of database interaction.
5
5
  Author-email: Bart Reijling <bart@reijling.eu>
6
6
  License-Expression: MIT
File without changes
File without changes
File without changes