elody 0.0.63__py3-none-any.whl → 0.0.162__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.
Files changed (41) hide show
  1. elody/client.py +70 -23
  2. elody/csv.py +118 -21
  3. elody/error_codes.py +112 -0
  4. elody/exceptions.py +14 -0
  5. elody/job.py +95 -0
  6. elody/loader.py +33 -5
  7. elody/migration/__init__.py +0 -0
  8. elody/migration/base_object_migrator.py +18 -0
  9. elody/object_configurations/__init__.py +0 -0
  10. elody/object_configurations/base_object_configuration.py +174 -0
  11. elody/object_configurations/elody_configuration.py +144 -0
  12. elody/object_configurations/job_configuration.py +65 -0
  13. elody/policies/authentication/base_user_tenant_validation_policy.py +48 -15
  14. elody/policies/authorization/filter_generic_objects_policy.py +68 -22
  15. elody/policies/authorization/filter_generic_objects_policy_v2.py +166 -0
  16. elody/policies/authorization/generic_object_detail_policy.py +10 -27
  17. elody/policies/authorization/generic_object_mediafiles_policy.py +82 -0
  18. elody/policies/authorization/generic_object_metadata_policy.py +8 -27
  19. elody/policies/authorization/generic_object_relations_policy.py +12 -29
  20. elody/policies/authorization/generic_object_request_policy.py +56 -55
  21. elody/policies/authorization/generic_object_request_policy_v2.py +133 -0
  22. elody/policies/authorization/mediafile_derivatives_policy.py +92 -0
  23. elody/policies/authorization/mediafile_download_policy.py +71 -0
  24. elody/policies/authorization/multi_tenant_policy.py +14 -6
  25. elody/policies/authorization/tenant_request_policy.py +3 -1
  26. elody/policies/helpers.py +37 -0
  27. elody/policies/permission_handler.py +217 -199
  28. elody/policies/tenant_id_resolver.py +375 -0
  29. elody/schemas.py +0 -3
  30. elody/util.py +165 -11
  31. {elody-0.0.63.dist-info → elody-0.0.162.dist-info}/METADATA +16 -11
  32. elody-0.0.162.dist-info/RECORD +47 -0
  33. {elody-0.0.63.dist-info → elody-0.0.162.dist-info}/WHEEL +1 -1
  34. {elody-0.0.63.dist-info → elody-0.0.162.dist-info}/top_level.txt +1 -0
  35. tests/__init_.py +0 -0
  36. tests/data.py +74 -0
  37. tests/unit/__init__.py +0 -0
  38. tests/unit/test_csv.py +410 -0
  39. tests/unit/test_utils.py +293 -0
  40. elody-0.0.63.dist-info/RECORD +0 -27
  41. {elody-0.0.63.dist-info → elody-0.0.162.dist-info}/LICENSE +0 -0
elody/loader.py CHANGED
@@ -2,7 +2,7 @@ import elody.util as util
2
2
  import json
3
3
  import os
4
4
 
5
- from elody.policies.permission_handler import set_permissions
5
+ from apscheduler.triggers.cron import CronTrigger
6
6
  from importlib import import_module
7
7
  from inuits_policy_based_auth.exceptions import (
8
8
  PolicyFactoryException,
@@ -17,9 +17,32 @@ def load_apps(flask_app, logger):
17
17
  flask_app.register_blueprint(api_bp)
18
18
 
19
19
 
20
- def load_policies(policy_factory, logger, permissions={}):
20
+ def load_jobs(scheduler, logger):
21
+ apps = util.read_json_as_dict(os.getenv("APPS_MANIFEST"), logger)
22
+ for app in apps:
23
+ for job, job_properties in apps[app].get("jobs", {}).items():
24
+ try:
25
+ job_class = __get_class_from_module(
26
+ import_module(f"apps.{app}.cron_jobs.{job}")
27
+ )
28
+ scheduler.add_job(
29
+ job_class(),
30
+ CronTrigger.from_crontab(
31
+ job_properties.get("expression", "0 0 * * *")
32
+ ),
33
+ )
34
+ except ModuleNotFoundError:
35
+ pass
36
+
37
+
38
+ def load_policies(
39
+ policy_factory, logger, permissions: dict = {}, placeholders: list[str] = []
40
+ ):
21
41
  if permissions:
22
- set_permissions(permissions)
42
+ from elody.policies.permission_handler import set_permissions
43
+
44
+ set_permissions(permissions, placeholders)
45
+
23
46
  apps = util.read_json_as_dict(os.getenv("APPS_MANIFEST", ""), logger)
24
47
  for app in apps:
25
48
  try:
@@ -43,6 +66,7 @@ def load_policies(policy_factory, logger, permissions={}):
43
66
 
44
67
 
45
68
  def load_queues(logger):
69
+ import_module("resources.queues")
46
70
  apps = util.read_json_as_dict(os.getenv("APPS_MANIFEST"), logger)
47
71
  for app in apps:
48
72
  try:
@@ -66,11 +90,15 @@ def __get_class(app, auth_type, policy_module_name):
66
90
  pass
67
91
  else:
68
92
  raise ModuleNotFoundError(f"Policy {policy_module_name} not found")
69
- policy_class_name = module.__name__.split(".")[-1].title().replace("_", "")
70
- policy = getattr(module, policy_class_name)
93
+ policy = __get_class_from_module(module)
71
94
  return policy
72
95
 
73
96
 
97
+ def __get_class_from_module(module):
98
+ class_name = module.__name__.split(".")[-1].title().replace("_", "")
99
+ return getattr(module, class_name)
100
+
101
+
74
102
  def __instantiate_authentication_policy(policy_module_name, policy, logger):
75
103
  allow_anonymous_users = os.getenv("ALLOW_ANONYMOUS_USERS", False) in [
76
104
  "True",
File without changes
@@ -0,0 +1,18 @@
1
+ class BaseObjectMigrator:
2
+ def __init__(self, *, status, silent=False):
3
+ self._status = status
4
+ self._silent = silent
5
+
6
+ @property
7
+ def status(self):
8
+ return self._status
9
+
10
+ @property
11
+ def silent(self):
12
+ return self._silent
13
+
14
+ def bulk_migrate(self, *, dry_run=False): # pyright: ignore
15
+ pass
16
+
17
+ def lazy_migrate(self, item, *, dry_run=False): # pyright: ignore
18
+ return item
File without changes
@@ -0,0 +1,174 @@
1
+ from abc import ABC, abstractmethod
2
+ from copy import deepcopy
3
+ from elody.migration.base_object_migrator import BaseObjectMigrator
4
+
5
+
6
+ class BaseObjectConfiguration(ABC):
7
+ SCHEMA_TYPE = "elody"
8
+ SCHEMA_VERSION = 1
9
+
10
+ @abstractmethod
11
+ def crud(self):
12
+ return {
13
+ "creator": lambda post_body, **kwargs: post_body,
14
+ "document_content_patcher": lambda *, document, content, overwrite=False, **kwargs: self._document_content_patcher(
15
+ document=document,
16
+ content=content,
17
+ overwrite=overwrite,
18
+ **kwargs,
19
+ ),
20
+ "nested_matcher_builder": lambda object_lists, keys_info, value: self.__build_nested_matcher(
21
+ object_lists, keys_info, value
22
+ ),
23
+ "post_crud_hook": lambda **kwargs: None,
24
+ "pre_crud_hook": lambda *, document, **kwargs: document,
25
+ "storage_type": "db",
26
+ }
27
+
28
+ @abstractmethod
29
+ def document_info(self):
30
+ return {}
31
+
32
+ @abstractmethod
33
+ def logging(self, flat_document, **kwargs):
34
+ info_labels = {
35
+ "uuid": flat_document.get("_id"),
36
+ "type": flat_document.get("type"),
37
+ "schema": f"{flat_document.get('schema.type')}:{flat_document.get('schema.version')}",
38
+ }
39
+ try:
40
+ from policy_factory import get_user_context # pyright: ignore
41
+
42
+ user_context = get_user_context()
43
+ info_labels["http_method"] = user_context.bag.get("http_method")
44
+ info_labels["requested_endpoint"] = user_context.bag.get(
45
+ "requested_endpoint"
46
+ )
47
+ info_labels["full_path"] = user_context.bag.get("full_path")
48
+ info_labels["preferred_username"] = user_context.preferred_username
49
+ info_labels["email"] = user_context.email
50
+ info_labels["user_roles"] = ", ".join(user_context.x_tenant.roles)
51
+ info_labels["x_tenant"] = user_context.x_tenant.id
52
+ except Exception:
53
+ pass
54
+ return {"info_labels": info_labels, "loki_indexed_info_labels": {}}
55
+
56
+ @abstractmethod
57
+ def migration(self):
58
+ return BaseObjectMigrator(status="disabled")
59
+
60
+ @abstractmethod
61
+ def serialization(self, from_format, to_format):
62
+ def serializer(document, **_):
63
+ return document
64
+
65
+ return serializer
66
+
67
+ @abstractmethod
68
+ def validation(self):
69
+ def validator(http_method, content, item, **_):
70
+ pass
71
+
72
+ return "function", validator
73
+
74
+ def _document_content_patcher(
75
+ self, *, document, content, overwrite=False, **kwargs
76
+ ):
77
+ raise NotImplementedError(
78
+ "Provide concrete implementation in child object configuration"
79
+ )
80
+
81
+ def _merge_object_lists(self, source, target, object_list_key):
82
+ for target_item in target:
83
+ for source_item in source:
84
+ if source_item[object_list_key] == target_item[object_list_key]:
85
+ source.remove(source_item)
86
+ return [*source, *target]
87
+
88
+ def _get_user_context_id(self):
89
+ try:
90
+ from policy_factory import get_user_context # pyright: ignore
91
+
92
+ return get_user_context().id
93
+ except Exception:
94
+ return None
95
+
96
+ def _sanitize_document(self, *, document, **kwargs):
97
+ sanitized_document = {}
98
+ document_deepcopy = deepcopy(document)
99
+ for key, value in document_deepcopy.items():
100
+ if isinstance(value, dict):
101
+ sanitized_value = BaseObjectConfiguration._sanitize_document(
102
+ self, document=value
103
+ )
104
+ if sanitized_value:
105
+ sanitized_document[key] = sanitized_value
106
+ elif value:
107
+ sanitized_document[key] = value
108
+ return sanitized_document
109
+
110
+ def _should_create_history_object(self):
111
+ try:
112
+ from policy_factory import get_user_context # pyright: ignore
113
+
114
+ get_user_context()
115
+ return bool(self.crud().get("collection_history"))
116
+ except Exception:
117
+ return False
118
+
119
+ def _sort_document_keys(self, document):
120
+ def sort_keys(data):
121
+ if isinstance(data, dict):
122
+ sorted_items = {key: data.pop(key) for key in sorted(data.keys())}
123
+ for key, value in sorted_items.items():
124
+ data[key] = sort_keys(value)
125
+ return data
126
+ elif isinstance(data, list):
127
+ if all(isinstance(i, str) for i in data):
128
+ data.sort()
129
+ return data
130
+ else:
131
+ for index, item in enumerate(data):
132
+ data[index] = sort_keys(item)
133
+ return data
134
+ else:
135
+ return data
136
+
137
+ for key, value in self.document_info().get("object_lists", {}).items():
138
+ if document.get(key):
139
+ document[key] = sorted(
140
+ document[key], key=lambda property: property[value]
141
+ )
142
+ sort_keys(document)
143
+ return document
144
+
145
+ def __build_nested_matcher(self, object_lists, keys_info, value, index=0):
146
+ if index == 0 and not any(info["object_list"] for info in keys_info):
147
+ if value in ["ANY_MATCH", "NONE_MATCH"]:
148
+ value = {"$exists": value == "ANY_MATCH"}
149
+ return {".".join(info["key"] for info in keys_info): value}
150
+
151
+ info = keys_info[index]
152
+
153
+ if info["object_list"]:
154
+ elem_match = {
155
+ "$elemMatch": {
156
+ object_lists[info["object_list"]]: info["object_key"],
157
+ **self.__build_nested_matcher(
158
+ object_lists, keys_info[index + 1 :], value, 0
159
+ ),
160
+ }
161
+ }
162
+ if value in ["ANY_MATCH", "NONE_MATCH"]:
163
+ elem_match_with_exists_operator = deepcopy(elem_match)
164
+ del elem_match["$elemMatch"][keys_info[index + 1]["key"]]
165
+ if value == "NONE_MATCH":
166
+ return {
167
+ "NOR_MATCHER": [
168
+ {info["key"]: {"$all": [elem_match]}},
169
+ {info["key"]: {"$all": [elem_match_with_exists_operator]}},
170
+ ]
171
+ }
172
+ return elem_match if index > 0 else {info["key"]: {"$all": [elem_match]}}
173
+
174
+ raise Exception(f"Unable to build nested matcher. See keys_info: {keys_info}")
@@ -0,0 +1,144 @@
1
+ from datetime import datetime, timezone
2
+ from elody.object_configurations.base_object_configuration import (
3
+ BaseObjectConfiguration,
4
+ )
5
+ from elody.util import flatten_dict
6
+ from uuid import uuid4
7
+
8
+
9
+ class ElodyConfiguration(BaseObjectConfiguration):
10
+ SCHEMA_TYPE = "elody"
11
+ SCHEMA_VERSION = 1
12
+
13
+ def crud(self):
14
+ crud = {
15
+ "collection": "entities",
16
+ "collection_history": "history",
17
+ "creator": lambda post_body, **kwargs: self._creator(post_body, **kwargs),
18
+ "post_crud_hook": lambda **kwargs: self._post_crud_hook(**kwargs),
19
+ "pre_crud_hook": lambda **kwargs: self._pre_crud_hook(**kwargs),
20
+ }
21
+ return {**super().crud(), **crud}
22
+
23
+ def document_info(self):
24
+ return {"object_lists": {"metadata": "key", "relations": "type"}}
25
+
26
+ def logging(self, flat_document, **kwargs):
27
+ return super().logging(flat_document, **kwargs)
28
+
29
+ def migration(self):
30
+ return super().migration()
31
+
32
+ def serialization(self, from_format, to_format):
33
+ return super().serialization(from_format, to_format)
34
+
35
+ def validation(self):
36
+ return super().validation()
37
+
38
+ def _creator(
39
+ self,
40
+ post_body,
41
+ *,
42
+ flat_post_body={},
43
+ document_defaults={},
44
+ ):
45
+ if not flat_post_body:
46
+ flat_post_body = flatten_dict(
47
+ self.document_info()["object_lists"], post_body
48
+ )
49
+ _id = document_defaults.get("_id", str(uuid4()))
50
+
51
+ identifiers = []
52
+ for property in self.document_info().get("identifier_properties", []):
53
+ if identifier := flat_post_body.get(f"metadata.{property}.value"):
54
+ identifiers.append(identifier)
55
+
56
+ template = {
57
+ "_id": _id,
58
+ "computed_values": {
59
+ "created_at": datetime.now(timezone.utc),
60
+ "event": "create",
61
+ },
62
+ "identifiers": list(
63
+ set([_id, *identifiers, *document_defaults.pop("identifiers", [])])
64
+ ),
65
+ "metadata": [],
66
+ "relations": [],
67
+ "schema": {"type": self.SCHEMA_TYPE, "version": self.SCHEMA_VERSION},
68
+ }
69
+ if user_context_id := self._get_user_context_id():
70
+ template["computed_values"]["created_by"] = user_context_id
71
+
72
+ for key, object_list_key in self.document_info()["object_lists"].items():
73
+ if not key.startswith("lookup.virtual_relations"):
74
+ post_body[key] = self._merge_object_lists(
75
+ document_defaults.get(key, []),
76
+ post_body.get(key, []),
77
+ object_list_key,
78
+ )
79
+ document = {**template, **document_defaults, **post_body}
80
+
81
+ document = self._sanitize_document(
82
+ document=document,
83
+ object_list_name="metadata",
84
+ object_list_value_field_name="value",
85
+ )
86
+ document = self._sort_document_keys(document)
87
+ return document
88
+
89
+ def _document_content_patcher(
90
+ self, *, document, content, overwrite=False, **kwargs
91
+ ):
92
+ object_lists = self.document_info().get("object_lists", {})
93
+ if overwrite:
94
+ document = content
95
+ else:
96
+ for key, value in content.items():
97
+ if key in object_lists:
98
+ if key != "relations":
99
+ for value_element in value:
100
+ for item_element in document[key]:
101
+ if (
102
+ item_element[object_lists[key]]
103
+ == value_element[object_lists[key]]
104
+ ):
105
+ document[key].remove(item_element)
106
+ break
107
+ document[key].extend(value)
108
+ else:
109
+ document[key] = value
110
+
111
+ return document
112
+
113
+ def _post_crud_hook(self, **kwargs):
114
+ pass
115
+
116
+ def _pre_crud_hook(self, *, crud, document={}, **kwargs):
117
+ if document:
118
+ document = self._sanitize_document(
119
+ document=document,
120
+ object_list_name="metadata",
121
+ object_list_value_field_name="value",
122
+ )
123
+ document = self.__patch_document_computed_values(crud, document)
124
+ document = self._sort_document_keys(document)
125
+ return document
126
+
127
+ def _sanitize_document(
128
+ self, *, document, object_list_name, object_list_value_field_name, **kwargs
129
+ ):
130
+ sanitized_document = super()._sanitize_document(document=document)
131
+ object_list = document[object_list_name]
132
+ for element in object_list:
133
+ if not element[object_list_value_field_name]:
134
+ sanitized_document[object_list_name].remove(element)
135
+ return sanitized_document
136
+
137
+ def __patch_document_computed_values(self, crud, document):
138
+ if not document.get("computed_values"):
139
+ document["computed_values"] = {}
140
+ document["computed_values"].update({"event": crud})
141
+ document["computed_values"].update({"modified_at": datetime.now(timezone.utc)})
142
+ if email := self._get_user_context_id():
143
+ document["computed_values"].update({"modified_by": email})
144
+ return document
@@ -0,0 +1,65 @@
1
+ from elody.object_configurations.elody_configuration import (
2
+ ElodyConfiguration,
3
+ )
4
+ from elody.util import send_cloudevent
5
+ from os import getenv
6
+
7
+
8
+ class JobConfiguration(ElodyConfiguration):
9
+ SCHEMA_TYPE = "elody"
10
+ SCHEMA_VERSION = 1
11
+
12
+ def crud(self):
13
+ crud = {"collection": "jobs", "collection_history": ""}
14
+ return {**super().crud(), **crud}
15
+
16
+ def document_info(self):
17
+ return super().document_info()
18
+
19
+ def logging(self, flat_document, **kwargs):
20
+ return super().logging(flat_document, **kwargs)
21
+
22
+ def migration(self):
23
+ return super().migration()
24
+
25
+ def serialization(self, from_format, to_format):
26
+ return super().serialization(from_format, to_format)
27
+
28
+ def validation(self):
29
+ return super().validation()
30
+
31
+ def _creator(self, post_body, *, get_user_context={}, **_):
32
+ document = super()._creator(post_body)
33
+ if email := get_user_context().email:
34
+ document["computed_values"]["created_by"] = email
35
+ return document
36
+
37
+ def _post_crud_hook(self, *, crud, document, get_rabbit, **kwargs):
38
+ if crud == "create":
39
+ send_cloudevent(
40
+ get_rabbit(),
41
+ getenv("MQ_EXCHANGE", "dams"),
42
+ "dams.job_created",
43
+ document,
44
+ )
45
+ if parent_id := kwargs.get("parent_id"):
46
+ send_cloudevent(
47
+ get_rabbit(),
48
+ getenv("MQ_EXCHANGE", "dams"),
49
+ "dams.job_changed",
50
+ {
51
+ "id": parent_id,
52
+ "patch": {
53
+ "relations": [
54
+ {"key": document["_id"], "type": "isParentJobOf"}
55
+ ]
56
+ },
57
+ },
58
+ )
59
+ elif crud == "update":
60
+ send_cloudevent(
61
+ get_rabbit(),
62
+ getenv("MQ_EXCHANGE", "dams"),
63
+ "dams.job_changed",
64
+ document,
65
+ )
@@ -1,10 +1,15 @@
1
+ import re as regex
2
+
1
3
  from abc import ABC, abstractmethod
4
+ from configuration import get_object_configuration_mapper # pyright: ignore
5
+ from copy import deepcopy
6
+ from elody.util import get_raw_id
2
7
  from inuits_policy_based_auth.contexts.user_context import ( # pyright: ignore
3
8
  UserContext,
4
9
  )
5
10
  from inuits_policy_based_auth.helpers.tenant import Tenant # pyright: ignore
6
11
  from storage.storagemanager import StorageManager # pyright: ignore
7
- from werkzeug.exceptions import Unauthorized # pyright: ignore
12
+ from werkzeug.exceptions import Forbidden # pyright: ignore
8
13
 
9
14
 
10
15
  class BaseUserTenantValidationPolicy(ABC):
@@ -14,16 +19,16 @@ class BaseUserTenantValidationPolicy(ABC):
14
19
  self.user = {}
15
20
 
16
21
  @abstractmethod
17
- def get_user(self, id: str) -> dict:
18
- pass
22
+ def get_user(self, id: str, user_context: UserContext) -> dict:
23
+ user_context.bag["roles_from_idp"] = deepcopy(user_context.x_tenant.roles)
19
24
 
20
25
  @abstractmethod
21
- def build_user_context(self, request, user_context: UserContext, user: dict):
26
+ def build_user_context_for_authenticated_user(
27
+ self, request, user_context: UserContext, user: dict
28
+ ):
22
29
  self.user = user
23
30
  user_context.x_tenant = Tenant()
24
- user_context.x_tenant.id = request.headers.get(
25
- "X-tenant-id", self.super_tenant_id
26
- )
31
+ user_context.x_tenant.id = self._determine_tenant_id(request, user)
27
32
  user_context.x_tenant.roles = self.__get_tenant_roles(
28
33
  user_context.x_tenant.id, request
29
34
  )
@@ -33,20 +38,45 @@ class BaseUserTenantValidationPolicy(ABC):
33
38
  user_context.bag["tenant_defining_entity_id"] = user_context.x_tenant.id
34
39
  user_context.bag["tenant_relation_type"] = "isIn"
35
40
  user_context.bag["user_ids"] = self.user["identifiers"]
41
+ user_context.bag["http_method"] = request.method
42
+ user_context.bag["requested_endpoint"] = request.endpoint
43
+ user_context.bag["full_path"] = request.full_path
44
+
45
+ @abstractmethod
46
+ def build_user_context_for_anonymous_user(
47
+ self, user_context: UserContext, user: dict
48
+ ):
49
+ self.user = user
50
+ user_context.id = get_raw_id(user)
51
+ user_context.x_tenant = Tenant()
52
+ user_context.x_tenant.id = self.super_tenant_id
53
+ user_context.x_tenant.roles = ["anonymous"]
54
+ user_context.x_tenant.raw = self.__get_x_tenant_raw(user_context.x_tenant.id)
55
+ user_context.tenants = [user_context.x_tenant]
56
+ user_context.bag["x_tenant_id"] = user_context.x_tenant.id
57
+ user_context.bag["tenant_defining_entity_id"] = user_context.x_tenant.id
58
+ user_context.bag["tenant_relation_type"] = "isIn"
59
+ user_context.bag["user_ids"] = self.user["identifiers"]
60
+
61
+ @abstractmethod
62
+ def _determine_tenant_id(self, request, user) -> str:
63
+ pass
36
64
 
37
65
  def __get_tenant_roles(self, x_tenant_id: str, request) -> list[str]:
38
66
  roles = self.__get_user_tenant_relation(self.super_tenant_id).get("roles", [])
39
67
  if x_tenant_id != self.super_tenant_id:
40
68
  try:
41
69
  user_tenant_relation = self.__get_user_tenant_relation(x_tenant_id)
42
- except Unauthorized as error:
70
+ except Forbidden as error:
43
71
  user_tenant_relation = {}
44
72
  if len(roles) == 0:
45
- raise Unauthorized(error.description)
73
+ raise Forbidden(error.description)
46
74
  roles.extend(user_tenant_relation.get("roles", []))
47
75
 
48
- if len(roles) == 0 and request.path != "/tenants":
49
- raise Unauthorized("User has no global roles, switch to a specific tenant.")
76
+ if len(roles) == 0 and not regex.match(
77
+ "(/[^/]+/v[0-9]+)?/tenants$", request.path
78
+ ):
79
+ raise Forbidden("User has no global roles, switch to a specific tenant.")
50
80
  return roles
51
81
 
52
82
  def __get_user_tenant_relation(self, x_tenant_id: str) -> dict:
@@ -58,18 +88,21 @@ class BaseUserTenantValidationPolicy(ABC):
58
88
 
59
89
  if not user_tenant_relation:
60
90
  if x_tenant_id != self.super_tenant_id:
61
- raise Unauthorized(f"User is not a member of tenant {x_tenant_id}.")
91
+ raise Forbidden(f"User is not a member of tenant {x_tenant_id}.")
62
92
  else:
63
93
  return {}
64
94
 
65
95
  return user_tenant_relation
66
96
 
67
97
  def __get_x_tenant_raw(self, x_tenant_id: str) -> dict:
98
+ collection = (
99
+ get_object_configuration_mapper().get("tenant").crud()["collection"]
100
+ )
68
101
  x_tenant_raw = (
69
- self.storage.get_item_from_collection_by_id("entities", x_tenant_id) or {}
102
+ self.storage.get_item_from_collection_by_id(collection, x_tenant_id) or {}
70
103
  )
71
104
  if x_tenant_raw.get("type") != "tenant":
72
- raise Unauthorized(f"No tenant {x_tenant_id} exists.")
105
+ raise Forbidden(f"No tenant {x_tenant_id} exists.")
73
106
 
74
107
  return x_tenant_raw
75
108
 
@@ -85,7 +118,7 @@ class BaseUserTenantValidationPolicy(ABC):
85
118
  tenant_defining_entity_id = relation["key"]
86
119
  break
87
120
  if not tenant_defining_entity_id:
88
- raise Unauthorized(
121
+ raise Forbidden(
89
122
  f"{x_tenant_raw['_id']} has no relation with a tenant defining entity."
90
123
  )
91
124