elody 0.0.193__py3-none-any.whl → 0.0.195__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.
- elody/object_configurations/base_object_configuration.py +2 -0
- elody/policies/authentication/base_user_tenant_validation_policy.py +93 -86
- elody/policies/authorization/filter_generic_objects_policy_v2.py +2 -2
- elody/policies/authorization/mediafile_derivatives_policy.py +15 -33
- elody/policies/authorization/mediafile_download_policy.py +6 -20
- elody/policies/helpers.py +15 -21
- {elody-0.0.193.dist-info → elody-0.0.195.dist-info}/METADATA +1 -1
- {elody-0.0.193.dist-info → elody-0.0.195.dist-info}/RECORD +11 -12
- {elody-0.0.193.dist-info → elody-0.0.195.dist-info}/WHEEL +1 -1
- elody/policies/tenant_id_resolver.py +0 -375
- {elody-0.0.193.dist-info → elody-0.0.195.dist-info}/licenses/LICENSE +0 -0
- {elody-0.0.193.dist-info → elody-0.0.195.dist-info}/top_level.txt +0 -0
|
@@ -109,6 +109,8 @@ class BaseObjectConfiguration(ABC):
|
|
|
109
109
|
for element in value
|
|
110
110
|
if element
|
|
111
111
|
]
|
|
112
|
+
if all(isinstance(element, str) for element in sanitized_document[key]):
|
|
113
|
+
sanitized_document[key] = list(set(sanitized_document[key]))
|
|
112
114
|
elif isinstance(value, str):
|
|
113
115
|
lines = value.splitlines()
|
|
114
116
|
value = "\n".join(line.strip() for line in lines).strip()
|
|
@@ -3,123 +3,130 @@ import re as regex
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from configuration import get_object_configuration_mapper # pyright: ignore
|
|
5
5
|
from copy import deepcopy
|
|
6
|
-
from elody.util import get_raw_id
|
|
7
6
|
from inuits_policy_based_auth.contexts.user_context import ( # pyright: ignore
|
|
8
7
|
UserContext,
|
|
9
8
|
)
|
|
10
9
|
from inuits_policy_based_auth.helpers.tenant import Tenant # pyright: ignore
|
|
11
|
-
from storage.storagemanager import StorageManager # pyright: ignore
|
|
12
10
|
from werkzeug.exceptions import Forbidden # pyright: ignore
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
class BaseUserTenantValidationPolicy(ABC):
|
|
16
|
-
def __init__(self) -> None:
|
|
17
|
-
self.storage = StorageManager().get_db_engine()
|
|
18
|
-
self.super_tenant_id = "tenant:super"
|
|
19
|
-
self.user = {}
|
|
20
|
-
|
|
21
14
|
@abstractmethod
|
|
22
|
-
def get_user(
|
|
15
|
+
def get_user(
|
|
16
|
+
self,
|
|
17
|
+
id: str,
|
|
18
|
+
user_context: UserContext,
|
|
19
|
+
storage,
|
|
20
|
+
*,
|
|
21
|
+
user_metadata_key_for_global_roles="roles",
|
|
22
|
+
user_tenant_relation_type="hasTenant",
|
|
23
|
+
) -> dict:
|
|
24
|
+
config = get_object_configuration_mapper().get("user")
|
|
25
|
+
collection = config.crud()["collection"]
|
|
26
|
+
serialize = config.serialization(config.SCHEMA_TYPE, "elody")
|
|
27
|
+
|
|
28
|
+
user = storage.get_item_from_collection_by_id(collection, id) or {}
|
|
29
|
+
self.user = serialize(user)
|
|
23
30
|
user_context.bag["roles_from_idp"] = deepcopy(user_context.x_tenant.roles)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def build_user_context_for_authenticated_user(
|
|
27
|
-
self, request, user_context: UserContext, user: dict
|
|
28
|
-
):
|
|
29
|
-
self.user = user
|
|
30
|
-
user_context.x_tenant = Tenant()
|
|
31
|
-
user_context.x_tenant.id = self._determine_tenant_id(request, user)
|
|
32
|
-
user_context.x_tenant.roles = self.__get_tenant_roles(
|
|
33
|
-
user_context.x_tenant.id, request
|
|
31
|
+
user_context.bag["user_metadata_key_for_global_roles"] = (
|
|
32
|
+
user_metadata_key_for_global_roles
|
|
34
33
|
)
|
|
35
|
-
user_context.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
user_context.bag["tenant_defining_entity_id"] = user_context.x_tenant.id
|
|
39
|
-
user_context.bag["tenant_relation_type"] = "isIn"
|
|
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
|
|
34
|
+
user_context.bag["user_tenant_relation_type"] = user_tenant_relation_type
|
|
35
|
+
|
|
36
|
+
return self.user
|
|
44
37
|
|
|
45
38
|
@abstractmethod
|
|
46
39
|
def build_user_context_for_anonymous_user(
|
|
47
|
-
self, user_context: UserContext
|
|
48
|
-
):
|
|
49
|
-
self.
|
|
50
|
-
user_context.id =
|
|
40
|
+
self, request, user_context: UserContext
|
|
41
|
+
) -> UserContext:
|
|
42
|
+
user_context = self.__build_user_context(request, user_context)
|
|
43
|
+
user_context.id = "anonymous"
|
|
51
44
|
user_context.x_tenant = Tenant()
|
|
52
|
-
user_context.x_tenant.id =
|
|
45
|
+
user_context.x_tenant.id = ""
|
|
53
46
|
user_context.x_tenant.roles = ["anonymous"]
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
user_context
|
|
59
|
-
|
|
47
|
+
return user_context
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def build_user_context_for_authenticated_user(
|
|
51
|
+
self, request, user_context: UserContext, user: dict
|
|
52
|
+
) -> UserContext:
|
|
53
|
+
user_context = self.__build_user_context(request, user_context)
|
|
54
|
+
user_context.x_tenant = Tenant()
|
|
55
|
+
user_context.x_tenant.id = self._determine_tenant_id(request, user_context)
|
|
56
|
+
user_context.x_tenant.roles = self.__get_tenant_roles(request, user_context)
|
|
57
|
+
return user_context
|
|
60
58
|
|
|
61
59
|
@abstractmethod
|
|
62
|
-
def _determine_tenant_id(self, request,
|
|
60
|
+
def _determine_tenant_id(self, request, user_context: UserContext) -> str:
|
|
63
61
|
pass
|
|
64
62
|
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
def __build_user_context(self, request, user_context: UserContext):
|
|
64
|
+
user_context.bag["http_method"] = request.method
|
|
65
|
+
user_context.bag["requested_endpoint"] = request.endpoint
|
|
66
|
+
user_context.bag["full_path"] = request.full_path
|
|
67
|
+
user_context.bag["collection_resolver"] = (
|
|
68
|
+
self._resolve_collections # pyright: ignore
|
|
69
|
+
)
|
|
70
|
+
return user_context
|
|
71
|
+
|
|
72
|
+
def __get_tenant_roles(self, request, user_context: UserContext) -> list[str]:
|
|
73
|
+
"""
|
|
74
|
+
Gathering multiple tenants are supported, but there are some important notes:
|
|
75
|
+
- all roles are stored in a combined way in user_context.x_tenant.roles
|
|
76
|
+
- those combination of different tenant roles will work, as long as:
|
|
77
|
+
- the roles are the same
|
|
78
|
+
- the object restrictions allow all tenants that are linked with the user
|
|
79
|
+
=> to make it fully work, see #143185
|
|
80
|
+
- with combined roles, when combaring rol_a vs rol_b
|
|
81
|
+
- if rol_a allow and role_b does not allow access: the role allowing access will always win
|
|
82
|
+
- if both roles allow, but just have different restrictions: the first allowing role will win
|
|
83
|
+
- this is completely random
|
|
84
|
+
=> currently no logic in policies to determine which allowing role to apply
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
roles = []
|
|
88
|
+
for metadata in self.user.get("metadata", []):
|
|
89
|
+
if (
|
|
90
|
+
metadata["key"]
|
|
91
|
+
== user_context.bag["user_metadata_key_for_global_roles"]
|
|
92
|
+
):
|
|
93
|
+
roles.extend(metadata["value"])
|
|
94
|
+
|
|
95
|
+
if user_context.x_tenant.id:
|
|
96
|
+
tenant_ids = user_context.x_tenant.id.split(",")
|
|
97
|
+
for tenant_id in tenant_ids:
|
|
98
|
+
try:
|
|
99
|
+
user_tenant_relation = self.__get_user_tenant_relation(
|
|
100
|
+
tenant_id, user_context.bag["user_tenant_relation_type"]
|
|
101
|
+
)
|
|
102
|
+
except Forbidden as error:
|
|
103
|
+
user_tenant_relation = {}
|
|
104
|
+
if len(roles) == 0:
|
|
105
|
+
raise Forbidden(error.description)
|
|
106
|
+
roles.extend(user_tenant_relation.get("roles", []))
|
|
75
107
|
|
|
76
108
|
if len(roles) == 0 and not regex.match(
|
|
77
109
|
"(/[^/]+/v[0-9]+)?/tenants$", request.path
|
|
78
110
|
):
|
|
79
111
|
raise Forbidden("User has no global roles, switch to a specific tenant.")
|
|
80
|
-
return roles
|
|
81
112
|
|
|
82
|
-
|
|
113
|
+
return list(set(roles))
|
|
114
|
+
|
|
115
|
+
def __get_user_tenant_relation(
|
|
116
|
+
self, tenant_id: str, user_tenant_relation_type: str
|
|
117
|
+
) -> dict:
|
|
83
118
|
user_tenant_relation = None
|
|
84
119
|
for relation in self.user.get("relations", []):
|
|
85
|
-
if
|
|
120
|
+
if (
|
|
121
|
+
relation["key"] == tenant_id
|
|
122
|
+
and relation["type"] == user_tenant_relation_type
|
|
123
|
+
):
|
|
86
124
|
user_tenant_relation = relation
|
|
87
125
|
break
|
|
88
126
|
|
|
89
127
|
if not user_tenant_relation:
|
|
90
|
-
if
|
|
91
|
-
raise Forbidden(f"User is not a member of tenant {
|
|
92
|
-
|
|
93
|
-
return {}
|
|
128
|
+
if tenant_id:
|
|
129
|
+
raise Forbidden(f"User is not a member of tenant {tenant_id}.")
|
|
130
|
+
return {}
|
|
94
131
|
|
|
95
132
|
return user_tenant_relation
|
|
96
|
-
|
|
97
|
-
def __get_x_tenant_raw(self, x_tenant_id: str) -> dict:
|
|
98
|
-
collection = (
|
|
99
|
-
get_object_configuration_mapper().get("tenant").crud()["collection"]
|
|
100
|
-
)
|
|
101
|
-
x_tenant_raw = (
|
|
102
|
-
self.storage.get_item_from_collection_by_id(collection, x_tenant_id) or {}
|
|
103
|
-
)
|
|
104
|
-
if x_tenant_raw.get("type") != "tenant":
|
|
105
|
-
raise Forbidden(f"No tenant {x_tenant_id} exists.")
|
|
106
|
-
|
|
107
|
-
return x_tenant_raw
|
|
108
|
-
|
|
109
|
-
def _get_tenant_defining_entity_id(
|
|
110
|
-
self, x_tenant_id: str, x_tenant_raw: dict
|
|
111
|
-
) -> str:
|
|
112
|
-
if x_tenant_id == self.super_tenant_id:
|
|
113
|
-
return x_tenant_id.removeprefix("tenant:")
|
|
114
|
-
|
|
115
|
-
tenant_defining_entity_id = None
|
|
116
|
-
for relation in x_tenant_raw.get("relations", []):
|
|
117
|
-
if relation["type"] == "definedBy":
|
|
118
|
-
tenant_defining_entity_id = relation["key"]
|
|
119
|
-
break
|
|
120
|
-
if not tenant_defining_entity_id:
|
|
121
|
-
raise Forbidden(
|
|
122
|
-
f"{x_tenant_raw['_id']} has no relation with a tenant defining entity."
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
return tenant_defining_entity_id
|
|
@@ -5,7 +5,7 @@ from elody.policies.permission_handler import (
|
|
|
5
5
|
get_permissions,
|
|
6
6
|
mask_protected_content_post_request_hook,
|
|
7
7
|
)
|
|
8
|
-
from flask import Request # pyright: ignore
|
|
8
|
+
from flask import g, Request # pyright: ignore
|
|
9
9
|
from inuits_policy_based_auth import BaseAuthorizationPolicy # pyright: ignore
|
|
10
10
|
from inuits_policy_based_auth.contexts.policy_context import ( # pyright: ignore
|
|
11
11
|
PolicyContext,
|
|
@@ -26,7 +26,7 @@ class FilterGenericObjectsPolicyV2(BaseAuthorizationPolicy):
|
|
|
26
26
|
if not isinstance(user_context.access_restrictions.filters, list):
|
|
27
27
|
user_context.access_restrictions.filters = []
|
|
28
28
|
type_filter, filters = self.__split_type_filter(
|
|
29
|
-
user_context, deepcopy(request.json or [])
|
|
29
|
+
user_context, deepcopy(g.get("content") or request.json or [])
|
|
30
30
|
)
|
|
31
31
|
if not type_filter:
|
|
32
32
|
policy_context.access_verdict = True
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import re as regex
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from configuration import get_object_configuration_mapper # pyright: ignore
|
|
4
|
+
from elody.policies.helpers import get_content, get_item
|
|
4
5
|
from elody.policies.permission_handler import (
|
|
5
6
|
get_permissions,
|
|
6
7
|
handle_single_item_request,
|
|
7
8
|
)
|
|
8
|
-
from flask import Request # pyright: ignore
|
|
9
|
-
from flask_restful import abort # pyright: ignore
|
|
9
|
+
from flask import g, Request # pyright: ignore
|
|
10
10
|
from inuits_policy_based_auth import BaseAuthorizationPolicy # pyright: ignore
|
|
11
11
|
from inuits_policy_based_auth.contexts.policy_context import ( # pyright: ignore
|
|
12
12
|
PolicyContext,
|
|
@@ -22,32 +22,18 @@ class MediafileDerivativesPolicy(BaseAuthorizationPolicy):
|
|
|
22
22
|
self, policy_context: PolicyContext, user_context: UserContext, request_context
|
|
23
23
|
):
|
|
24
24
|
request: Request = request_context.http_request
|
|
25
|
-
if not regex.match(
|
|
25
|
+
if not regex.match(
|
|
26
|
+
"^(/elody/v[0-9]+)?/mediafiles/[^/]+/derivatives$", request.path
|
|
27
|
+
):
|
|
26
28
|
return policy_context
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
collection = view_args.get("collection", request.path.split("/")[-3])
|
|
30
|
-
id = view_args.get("id")
|
|
31
|
-
item = (
|
|
32
|
-
StorageManager()
|
|
33
|
-
.get_db_engine()
|
|
34
|
-
.get_item_from_collection_by_id(collection, id)
|
|
35
|
-
)
|
|
36
|
-
if not item:
|
|
37
|
-
abort(
|
|
38
|
-
404,
|
|
39
|
-
message=f"{get_error_code(ErrorCode.ITEM_NOT_FOUND_IN_COLLECTION, get_read())} | id:{id} | collection: {collection} - Item with id {id} doesn't exist in collection {collection}",
|
|
40
|
-
)
|
|
41
|
-
|
|
30
|
+
item = get_item(StorageManager(), user_context.bag, request.view_args)
|
|
42
31
|
for role in user_context.x_tenant.roles:
|
|
43
32
|
permissions = get_permissions(role, user_context)
|
|
44
33
|
if not permissions:
|
|
45
34
|
continue
|
|
46
35
|
|
|
47
|
-
rules = [
|
|
48
|
-
PostRequestRules,
|
|
49
|
-
GetRequestRules,
|
|
50
|
-
]
|
|
36
|
+
rules = [PostRequestRules, GetRequestRules]
|
|
51
37
|
access_verdict = None
|
|
52
38
|
for rule in rules:
|
|
53
39
|
access_verdict = rule().apply(item, user_context, request, permissions)
|
|
@@ -69,7 +55,13 @@ class PostRequestRules:
|
|
|
69
55
|
if request.method != "POST":
|
|
70
56
|
return None
|
|
71
57
|
|
|
72
|
-
|
|
58
|
+
content = g.get("content") or request.json
|
|
59
|
+
content = get_content(content, request, content)
|
|
60
|
+
schema_type = get_object_configuration_mapper().get(content["type"]).SCHEMA_TYPE
|
|
61
|
+
item = {**content, "schema": {"type": schema_type}}
|
|
62
|
+
return handle_single_item_request(
|
|
63
|
+
user_context, item, permissions, "create", content
|
|
64
|
+
)
|
|
73
65
|
|
|
74
66
|
|
|
75
67
|
class GetRequestRules:
|
|
@@ -80,13 +72,3 @@ class GetRequestRules:
|
|
|
80
72
|
return None
|
|
81
73
|
|
|
82
74
|
return handle_single_item_request(user_context, item, permissions, "read")
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class DeleteRequestRules:
|
|
86
|
-
def apply(
|
|
87
|
-
self, item, user_context: UserContext, request: Request, permissions
|
|
88
|
-
) -> bool | None:
|
|
89
|
-
if request.method != "DELETE":
|
|
90
|
-
return None
|
|
91
|
-
|
|
92
|
-
return handle_single_item_request(user_context, item, permissions, "delete")
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import re as regex
|
|
2
2
|
|
|
3
|
-
from elody.
|
|
3
|
+
from elody.policies.helpers import get_item
|
|
4
4
|
from elody.policies.permission_handler import (
|
|
5
5
|
get_permissions,
|
|
6
6
|
handle_single_item_request,
|
|
7
7
|
)
|
|
8
8
|
from flask import Request # pyright: ignore
|
|
9
|
-
from flask_restful import abort # pyright: ignore
|
|
10
9
|
from inuits_policy_based_auth import BaseAuthorizationPolicy # pyright: ignore
|
|
11
10
|
from inuits_policy_based_auth.contexts.policy_context import ( # pyright: ignore
|
|
12
11
|
PolicyContext,
|
|
@@ -22,31 +21,18 @@ class MediafileDownloadPolicy(BaseAuthorizationPolicy):
|
|
|
22
21
|
self, policy_context: PolicyContext, user_context: UserContext, request_context
|
|
23
22
|
):
|
|
24
23
|
request: Request = request_context.http_request
|
|
25
|
-
if not regex.match(
|
|
24
|
+
if not regex.match(
|
|
25
|
+
"^(/elody/v[0-9]+)?/mediafiles/[^/]+/download$", request.path
|
|
26
|
+
):
|
|
26
27
|
return policy_context
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
collection = view_args.get("collection", request.path.split("/")[-3])
|
|
30
|
-
id = view_args.get("id")
|
|
31
|
-
item = (
|
|
32
|
-
StorageManager()
|
|
33
|
-
.get_db_engine()
|
|
34
|
-
.get_item_from_collection_by_id(collection, id)
|
|
35
|
-
)
|
|
36
|
-
if not item:
|
|
37
|
-
abort(
|
|
38
|
-
404,
|
|
39
|
-
message=f"{get_error_code(ErrorCode.ITEM_NOT_FOUND_IN_COLLECTION, get_read())} | id:{id} | collection:{collection} - Item with id {id} doesn't exist in collection {collection}",
|
|
40
|
-
)
|
|
41
|
-
|
|
29
|
+
item = get_item(StorageManager(), user_context.bag, request.view_args)
|
|
42
30
|
for role in user_context.x_tenant.roles:
|
|
43
31
|
permissions = get_permissions(role, user_context)
|
|
44
32
|
if not permissions:
|
|
45
33
|
continue
|
|
46
34
|
|
|
47
|
-
rules = [
|
|
48
|
-
GetRequestRules,
|
|
49
|
-
]
|
|
35
|
+
rules = [GetRequestRules]
|
|
50
36
|
access_verdict = None
|
|
51
37
|
for rule in rules:
|
|
52
38
|
access_verdict = rule().apply(item, user_context, request, permissions)
|
elody/policies/helpers.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from elody.error_codes import ErrorCode, get_error_code, get_read
|
|
2
1
|
from configuration import get_object_configuration_mapper # pyright: ignore
|
|
3
|
-
from
|
|
2
|
+
from elody.error_codes import ErrorCode, get_error_code, get_read
|
|
4
3
|
from serialization.serialize import serialize # pyright: ignore
|
|
4
|
+
from werkzeug.exceptions import NotFound
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def get_content(item, request, content):
|
|
@@ -15,23 +15,17 @@ def get_content(item, request, content):
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def get_item(storage_manager, user_context_bag, view_args):
|
|
18
|
+
def get_item(storage_manager, user_context_bag, view_args) -> dict:
|
|
19
19
|
view_args = view_args or {}
|
|
20
|
-
id
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return item
|
|
33
|
-
else:
|
|
34
|
-
abort(
|
|
35
|
-
404,
|
|
36
|
-
message=f"{get_error_code(ErrorCode.ITEM_NOT_FOUND, get_read())} | id:{id} - Item with id {id} does not exist.",
|
|
37
|
-
)
|
|
20
|
+
if id := view_args.get("id"):
|
|
21
|
+
resolve_collections = user_context_bag.get("collection_resolver")
|
|
22
|
+
collections = resolve_collections(collection=view_args.get("collection"), id=id)
|
|
23
|
+
for collection in collections:
|
|
24
|
+
if item := storage_manager.get_db_engine().get_item_from_collection_by_id(
|
|
25
|
+
collection, id
|
|
26
|
+
):
|
|
27
|
+
return item
|
|
28
|
+
|
|
29
|
+
raise NotFound(
|
|
30
|
+
f"{get_error_code(ErrorCode.ITEM_NOT_FOUND, get_read())} | id:{id} - Item with id {id} does not exist."
|
|
31
|
+
)
|
|
@@ -12,36 +12,35 @@ elody/validator.py,sha256=G7Ya538EJHCFzOxEri2OcFMabfLBCtTKxuf4os_KuNw,260
|
|
|
12
12
|
elody/migration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
elody/migration/base_object_migrator.py,sha256=n8uvgGfjEUy60G47RD7Y-oxp1vHLOauwPMDl87LcxtU,436
|
|
14
14
|
elody/object_configurations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
elody/object_configurations/base_object_configuration.py,sha256=
|
|
15
|
+
elody/object_configurations/base_object_configuration.py,sha256=8wyUq_zqRkGb4Mp198pyxOaGdz2WMZzVOO65s1SDCRw,7393
|
|
16
16
|
elody/object_configurations/elody_configuration.py,sha256=H3iqNXPhYX6scR7C57L_hCWQH2MnWKwnH3vegLg-UGw,7645
|
|
17
17
|
elody/object_configurations/job_configuration.py,sha256=HMDxaRUyfqhIy0q3yQDDMH9uW5iCd7VCmqknQofXNt0,2039
|
|
18
18
|
elody/policies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
-
elody/policies/helpers.py,sha256=
|
|
19
|
+
elody/policies/helpers.py,sha256=FFpCRNITO-Y9yzTBJEV70FeFwx6xTG93ddYHL5QAl10,1215
|
|
20
20
|
elody/policies/permission_handler.py,sha256=C5wwgAplxmrkrseA-oNjX_Ff1PfVGP5NIvpx0F5PYho,9869
|
|
21
|
-
elody/policies/tenant_id_resolver.py,sha256=BIl6lr9AH6Q_aZSsxF24B7ramkIZH-R-8bpLrTvYLtM,13796
|
|
22
21
|
elody/policies/authentication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
elody/policies/authentication/base_user_tenant_validation_policy.py,sha256=
|
|
22
|
+
elody/policies/authentication/base_user_tenant_validation_policy.py,sha256=yyGiYy-MM63itQYwUc99FPeXt6TBW5B95FeLMfjMEok,5368
|
|
24
23
|
elody/policies/authentication/multi_tenant_policy.py,sha256=g4ZYUQMmCjgLg09wj0-0lGKsJsRt7h4ppI25o1VdZHw,4039
|
|
25
24
|
elody/policies/authorization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
25
|
elody/policies/authorization/filter_generic_objects_policy.py,sha256=mF32moh8hRetBgG8vQW-rz4xjoRQD2yOxdI740SFSUo,6522
|
|
27
|
-
elody/policies/authorization/filter_generic_objects_policy_v2.py,sha256=
|
|
26
|
+
elody/policies/authorization/filter_generic_objects_policy_v2.py,sha256=RrGFARQJnIQUvUSN8i9PvhNxzEVpNInkLGf5mK1Ch0Y,6474
|
|
28
27
|
elody/policies/authorization/generic_object_detail_policy.py,sha256=y6g1i3vdKMKY4xS4H2m0e1DRztrivMEomx6NkDqA0Pk,3672
|
|
29
28
|
elody/policies/authorization/generic_object_mediafiles_policy.py,sha256=1-DMsV-FDkcrQCE4KL-SGlVHjTZSMPwYq1bWln2nXE4,2887
|
|
30
29
|
elody/policies/authorization/generic_object_metadata_policy.py,sha256=xwtOVYmiCKgf75CydfWnV7DLI9X1yVfXPQ4-Ux0Htqk,2787
|
|
31
30
|
elody/policies/authorization/generic_object_relations_policy.py,sha256=hRLeA5gXB44ufiVVaxWtAuwnXBRY1U4bLWgIadzKtmw,3712
|
|
32
31
|
elody/policies/authorization/generic_object_request_policy.py,sha256=kuLczjnK5omMF2Gw5ViY_Z9MNPx_w6bNwexiMzvLiUU,4867
|
|
33
32
|
elody/policies/authorization/generic_object_request_policy_v2.py,sha256=zaoCdV7KVAcMYNNXHYTi8Vtjkw_E8_w8aMqxJ62QqBE,5099
|
|
34
|
-
elody/policies/authorization/mediafile_derivatives_policy.py,sha256=
|
|
35
|
-
elody/policies/authorization/mediafile_download_policy.py,sha256=
|
|
33
|
+
elody/policies/authorization/mediafile_derivatives_policy.py,sha256=OwNpbS8i7-LzcQDPddMWTrXk_Y4wqZxrB12Lw1dhkr0,2671
|
|
34
|
+
elody/policies/authorization/mediafile_download_policy.py,sha256=XMsKavBucmTh4W1kWOzpFWxJ_ZXgHVK1RS7JB4HjtQo,1979
|
|
36
35
|
elody/policies/authorization/multi_tenant_policy.py,sha256=SA9H7SBjzuh8mY3gYN7pDG8TV7hdI3GEUtNeiZeNL3M,3164
|
|
37
36
|
elody/policies/authorization/tenant_request_policy.py,sha256=dEgblwRAqwWVcE-O7Jn8hVL3OnwDlQhDEOcPlcElBrk,1185
|
|
38
|
-
elody-0.0.
|
|
37
|
+
elody-0.0.195.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
|
|
39
38
|
tests/__init_.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
39
|
tests/data.py,sha256=Q3oxduf-E3m-Z5G_p3fcs8jVy6g10I7zXKL1m94UVMI,2906
|
|
41
40
|
tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
41
|
tests/unit/test_csv.py,sha256=NQaOhehfQ4GuXku0Y1SA8DYjJeqqidbF50zEHAi8RZA,15923
|
|
43
42
|
tests/unit/test_utils.py,sha256=g63szcEZyHhCOtrW4BnNbcgVca3oYPIOLjBdIzNwwN0,8784
|
|
44
|
-
elody-0.0.
|
|
45
|
-
elody-0.0.
|
|
46
|
-
elody-0.0.
|
|
47
|
-
elody-0.0.
|
|
43
|
+
elody-0.0.195.dist-info/METADATA,sha256=ryafZPjxZIEOt-tgv23aB6yqYkXrP1MtBztr4AxsrVI,23358
|
|
44
|
+
elody-0.0.195.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
45
|
+
elody-0.0.195.dist-info/top_level.txt,sha256=E0mImupLj0KmtUUCXRYEoLDRaSkuiGaOIIseAa0oQ-M,21
|
|
46
|
+
elody-0.0.195.dist-info/RECORD,,
|
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
import re as regex
|
|
2
|
-
|
|
3
|
-
from elody.error_codes import ErrorCode, get_error_code, get_read, get_write
|
|
4
|
-
from flask import Request
|
|
5
|
-
from flask_restful import abort
|
|
6
|
-
from storage.storagemanager import StorageManager # pyright: ignore
|
|
7
|
-
from elody.util import get_item_metadata_value
|
|
8
|
-
from elody.exceptions import NotFoundException, NoTenantException
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class TenantIdResolver:
|
|
12
|
-
def resolve(self, request, user):
|
|
13
|
-
endpoints = [
|
|
14
|
-
EntityGetRequest,
|
|
15
|
-
EntityPostRequest,
|
|
16
|
-
EntityDetailGetRequest,
|
|
17
|
-
EntityDetailUpdateRequest,
|
|
18
|
-
EntityDetailDeleteRequest,
|
|
19
|
-
EntityDetailGetRelationsRequest,
|
|
20
|
-
EntityDetailUpdateRelationsRequest,
|
|
21
|
-
EntityDetailCreateRelationsRequest,
|
|
22
|
-
EntityDetailDeleteRelationsRequest,
|
|
23
|
-
EntityDetailGetMetadataRequest,
|
|
24
|
-
EntityDetailUpdateMetadataRequest,
|
|
25
|
-
EntityDetailCreateMetadataRequest,
|
|
26
|
-
EntityDetailGetMediafilesRequest,
|
|
27
|
-
EntityDetailCreateMediafilesRequest,
|
|
28
|
-
MediafileGetRequest,
|
|
29
|
-
MediafilePostRequest,
|
|
30
|
-
MediafileDetailGetRequest,
|
|
31
|
-
MediafileDetailUpdateRequest,
|
|
32
|
-
MediafileDetailDeleteRequest,
|
|
33
|
-
MediafileDetailGetDerivativesRequest,
|
|
34
|
-
MediafileDetailCreateDerivativesRequest,
|
|
35
|
-
MediafileDetailDeleteDerivativesRequest,
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
for endpoint in endpoints:
|
|
39
|
-
tenant_id = endpoint().get_tenant_id(request)
|
|
40
|
-
if tenant_id != None:
|
|
41
|
-
relations = user.get("relations", [])
|
|
42
|
-
if len(relations) < 1:
|
|
43
|
-
raise NoTenantException(f"User is not attached to a tenant")
|
|
44
|
-
has_tenant_relation = any(
|
|
45
|
-
relation.get("type") == "hasTenant"
|
|
46
|
-
and relation.get("key") == tenant_id
|
|
47
|
-
for relation in relations
|
|
48
|
-
)
|
|
49
|
-
if has_tenant_relation:
|
|
50
|
-
return tenant_id
|
|
51
|
-
elif len(relations) == 1 and relations[0].get("key") == "tenant:super":
|
|
52
|
-
return "tenant:super"
|
|
53
|
-
else:
|
|
54
|
-
return relations[0].get("key")
|
|
55
|
-
return "tenant:super"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class BaseRequest:
|
|
59
|
-
def __init__(self) -> None:
|
|
60
|
-
self.storage = StorageManager().get_db_engine()
|
|
61
|
-
self.super_tenant_id = "tenant:super"
|
|
62
|
-
# TODO refactor this in a more generic way
|
|
63
|
-
self.global_types = [
|
|
64
|
-
"language",
|
|
65
|
-
"type",
|
|
66
|
-
"collectionForm",
|
|
67
|
-
"institution",
|
|
68
|
-
"tag",
|
|
69
|
-
"triple",
|
|
70
|
-
"person",
|
|
71
|
-
"externalRecord",
|
|
72
|
-
"verzameling",
|
|
73
|
-
"arches_record",
|
|
74
|
-
"manufacturer",
|
|
75
|
-
"photographer",
|
|
76
|
-
"creator",
|
|
77
|
-
"assetPart",
|
|
78
|
-
"set",
|
|
79
|
-
"download",
|
|
80
|
-
"license",
|
|
81
|
-
"share_link"
|
|
82
|
-
# TODO Mediafile should have a link to an asset
|
|
83
|
-
"mediafile",
|
|
84
|
-
"savedSearch",
|
|
85
|
-
"original_data",
|
|
86
|
-
"user",
|
|
87
|
-
]
|
|
88
|
-
|
|
89
|
-
def _get_tenant_id_from_entity(self, entity_id):
|
|
90
|
-
entity_relations = self.storage.get_collection_item_relations(
|
|
91
|
-
"entities", entity_id
|
|
92
|
-
)
|
|
93
|
-
entity = self.storage.get_item_from_collection_by_id("entities", entity_id)
|
|
94
|
-
if not entity:
|
|
95
|
-
abort(
|
|
96
|
-
404,
|
|
97
|
-
message=f"{get_error_code(ErrorCode.ITEM_NOT_FOUND, get_read())} | id:{entity_id} - Item with id {entity_id} doesn't exist",
|
|
98
|
-
)
|
|
99
|
-
type = entity.get("type")
|
|
100
|
-
if type in self.global_types:
|
|
101
|
-
return "tenant:super"
|
|
102
|
-
if entity_relations:
|
|
103
|
-
for entity_relation in entity_relations:
|
|
104
|
-
if entity_relation.get("type") == "isIn":
|
|
105
|
-
tenant = self.storage.get_item_from_collection_by_id(
|
|
106
|
-
"entities", entity_relation.get("key")
|
|
107
|
-
)
|
|
108
|
-
return tenant["_id"]
|
|
109
|
-
if entity_relation.get("type") == "hasInstitution":
|
|
110
|
-
instution_relations = self.storage.get_collection_item_relations(
|
|
111
|
-
"entities", entity_relation.get("key")
|
|
112
|
-
)
|
|
113
|
-
for institution_relation in instution_relations:
|
|
114
|
-
if institution_relation.get("type") == "defines":
|
|
115
|
-
return institution_relation.get("key")
|
|
116
|
-
|
|
117
|
-
if entity and get_item_metadata_value(entity, "institution"):
|
|
118
|
-
return f"tenant:{get_item_metadata_value(entity, 'institution')}"
|
|
119
|
-
abort(
|
|
120
|
-
400,
|
|
121
|
-
message=f"{get_error_code(ErrorCode.ENTITY_HAS_NO_TENANT, get_read())} - Entity has no tenant, and is suppose to have one.",
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
def _get_tenant_id_from_mediafile(self, mediafile_id):
|
|
125
|
-
mediafile_relations = self.storage.get_collection_item_relations(
|
|
126
|
-
"mediafiles", mediafile_id
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
for relation in mediafile_relations:
|
|
130
|
-
if relation.get("type") == "belongsTo":
|
|
131
|
-
return self._get_tenant_id_from_entity(relation.get("key"))
|
|
132
|
-
|
|
133
|
-
# TODO Mediafile should have a link to an asset
|
|
134
|
-
def _get_tenant_id_from_body(self, item, soft_call=False):
|
|
135
|
-
if soft_call:
|
|
136
|
-
return "tenant:super"
|
|
137
|
-
if item.get("type") in self.global_types or item.get("type") == "institution":
|
|
138
|
-
return "tenant:super"
|
|
139
|
-
if item.get("type") == "asset":
|
|
140
|
-
for relation in item.get("relations", []):
|
|
141
|
-
if relation.get("type") in [
|
|
142
|
-
"hasArchesLink",
|
|
143
|
-
"hasAdlibLink",
|
|
144
|
-
"hasBrocadeLink",
|
|
145
|
-
]:
|
|
146
|
-
return "tenant:super"
|
|
147
|
-
institution_id = get_item_metadata_value(item, "institution")
|
|
148
|
-
if not institution_id:
|
|
149
|
-
for relation in item.get("relations", []):
|
|
150
|
-
if relation.get("type") == "hasInstitution":
|
|
151
|
-
institution_id = relation.get("key")
|
|
152
|
-
if institution_id:
|
|
153
|
-
return f"tenant:{institution_id}"
|
|
154
|
-
abort(
|
|
155
|
-
400,
|
|
156
|
-
message=f"{get_error_code(ErrorCode.ENTITY_HAS_NO_TENANT, get_read())} - Item in body doesn't have an institution.",
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
class EntityGetRequest(BaseRequest):
|
|
161
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
162
|
-
if (
|
|
163
|
-
regex.match(r"^/entities(?:\?(.*))?$", request.path)
|
|
164
|
-
and request.method == "GET"
|
|
165
|
-
):
|
|
166
|
-
return self.super_tenant_id
|
|
167
|
-
return None
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
class EntityPostRequest(BaseRequest):
|
|
171
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
172
|
-
if (
|
|
173
|
-
regex.match(r"^/entities(?:\?(.*))?$", request.path)
|
|
174
|
-
and request.method == "POST"
|
|
175
|
-
):
|
|
176
|
-
is_soft_call = request.args.get("soft") is not None
|
|
177
|
-
return self._get_tenant_id_from_body(request.json, soft_call=is_soft_call)
|
|
178
|
-
return None
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
class EntityDetailGetRequest(BaseRequest):
|
|
182
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
183
|
-
if regex.match(r"/entities/(.+)$", request.path) and request.method == "GET":
|
|
184
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
185
|
-
return None
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
class EntityDetailUpdateRequest(BaseRequest):
|
|
189
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
190
|
-
if regex.match(r"^/entities/(.+)$", request.path) and request.method in [
|
|
191
|
-
"PUT",
|
|
192
|
-
"PATCH",
|
|
193
|
-
]:
|
|
194
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
195
|
-
return None
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
class EntityDetailDeleteRequest(BaseRequest):
|
|
199
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
200
|
-
if (
|
|
201
|
-
regex.match(r"^/entities/([^/]+)$", request.path)
|
|
202
|
-
and request.method == "DELETE"
|
|
203
|
-
):
|
|
204
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
205
|
-
return None
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
class EntityDetailGetRelationsRequest(BaseRequest):
|
|
209
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
210
|
-
if (
|
|
211
|
-
regex.match(r"^/entities/(.+)/relations$", request.path)
|
|
212
|
-
and request.method == "GET"
|
|
213
|
-
):
|
|
214
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
215
|
-
return None
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
class EntityDetailUpdateRelationsRequest(BaseRequest):
|
|
219
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
220
|
-
if regex.match(
|
|
221
|
-
r"^/entities/(.+)/relations$", request.path
|
|
222
|
-
) and request.method in ["PUT", "PATCH"]:
|
|
223
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
224
|
-
return None
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
class EntityDetailCreateRelationsRequest(BaseRequest):
|
|
228
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
229
|
-
if (
|
|
230
|
-
regex.match(r"^/entities/(.+)/relations$", request.path)
|
|
231
|
-
and request.method == "POST"
|
|
232
|
-
):
|
|
233
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
234
|
-
return None
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
class EntityDetailDeleteRelationsRequest(BaseRequest):
|
|
238
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
239
|
-
if (
|
|
240
|
-
regex.match(r"^/entities/(.+)/relations$", request.path)
|
|
241
|
-
and request.method == "DELETE"
|
|
242
|
-
):
|
|
243
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
244
|
-
return None
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
class EntityDetailGetMetadataRequest(BaseRequest):
|
|
248
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
249
|
-
if (
|
|
250
|
-
regex.match(r"^/entities/(.+)/metadata$", request.path)
|
|
251
|
-
and request.method == "GET"
|
|
252
|
-
):
|
|
253
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
254
|
-
return None
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
class EntityDetailUpdateMetadataRequest(BaseRequest):
|
|
258
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
259
|
-
if regex.match(
|
|
260
|
-
r"^/entities/(.+)/metadata$", request.path
|
|
261
|
-
) and request.method in ["PUT", "PATCH"]:
|
|
262
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
263
|
-
return None
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
class EntityDetailCreateMetadataRequest(BaseRequest):
|
|
267
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
268
|
-
if (
|
|
269
|
-
regex.match(r"^/entities/(.+)/metadata$", request.path)
|
|
270
|
-
and request.method == "POST"
|
|
271
|
-
):
|
|
272
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
273
|
-
return None
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
class EntityDetailGetMediafilesRequest(BaseRequest):
|
|
277
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
278
|
-
if (
|
|
279
|
-
regex.match(r"^/entities/(.+)/mediafiles$", request.path)
|
|
280
|
-
and request.method == "GET"
|
|
281
|
-
):
|
|
282
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
283
|
-
return None
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
class EntityDetailCreateMediafilesRequest(BaseRequest):
|
|
287
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
288
|
-
if (
|
|
289
|
-
regex.match(r"^/entities/(.+)/mediafiles$", request.path)
|
|
290
|
-
and request.method == "POST"
|
|
291
|
-
):
|
|
292
|
-
return self._get_tenant_id_from_entity(request.view_args.get("id"))
|
|
293
|
-
return None
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
# Mediafiles
|
|
297
|
-
class MediafileGetRequest(BaseRequest):
|
|
298
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
299
|
-
if (
|
|
300
|
-
regex.match(r"^/mediafiles(?:\?(.*))?$", request.path)
|
|
301
|
-
and request.method == "GET"
|
|
302
|
-
):
|
|
303
|
-
return self.super_tenant_id
|
|
304
|
-
return None
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
class MediafilePostRequest(BaseRequest):
|
|
308
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
309
|
-
if (
|
|
310
|
-
regex.match(r"^/mediafiles(?:\?(.*))?$", request.path)
|
|
311
|
-
and request.method == "POST"
|
|
312
|
-
):
|
|
313
|
-
is_soft_call = request.args.get("soft") is not None
|
|
314
|
-
return self._get_tenant_id_from_body(request.json, soft_call=is_soft_call)
|
|
315
|
-
return None
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
class MediafileDetailGetRequest(BaseRequest):
|
|
319
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
320
|
-
if (
|
|
321
|
-
regex.match(r"^/mediafiles/([^/]+)$", request.path)
|
|
322
|
-
and request.method == "GET"
|
|
323
|
-
):
|
|
324
|
-
return self.super_tenant_id
|
|
325
|
-
return None
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
class MediafileDetailUpdateRequest(BaseRequest):
|
|
329
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
330
|
-
if regex.match(r"^/mediafiles/(.+)$", request.path) and request.method in [
|
|
331
|
-
"PUT",
|
|
332
|
-
"PATCH",
|
|
333
|
-
]:
|
|
334
|
-
return self._get_tenant_id_from_mediafile(request.view_args.get("id"))
|
|
335
|
-
return None
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
class MediafileDetailDeleteRequest(BaseRequest):
|
|
339
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
340
|
-
if (
|
|
341
|
-
regex.match(r"^/mediafiles/(.+)$", request.path)
|
|
342
|
-
and request.method == "DELETE"
|
|
343
|
-
):
|
|
344
|
-
return self._get_tenant_id_from_mediafile(request.view_args.get("id"))
|
|
345
|
-
return None
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
class MediafileDetailGetDerivativesRequest(BaseRequest):
|
|
349
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
350
|
-
if (
|
|
351
|
-
regex.match(r"^/mediafiles/(.+)/derivatives$", request.path)
|
|
352
|
-
and request.method == "GET"
|
|
353
|
-
):
|
|
354
|
-
return self.super_tenant_id
|
|
355
|
-
return None
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
class MediafileDetailCreateDerivativesRequest(BaseRequest):
|
|
359
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
360
|
-
if (
|
|
361
|
-
regex.match(r"^/mediafiles/(.+)/derivatives$", request.path)
|
|
362
|
-
and request.method == "POST"
|
|
363
|
-
):
|
|
364
|
-
return self._get_tenant_id_from_mediafile(request.view_args.get("id"))
|
|
365
|
-
return None
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
class MediafileDetailDeleteDerivativesRequest(BaseRequest):
|
|
369
|
-
def get_tenant_id(self, request: Request) -> str | None:
|
|
370
|
-
if (
|
|
371
|
-
regex.match(r"^/mediafiles/(.+)/derivatives$", request.path)
|
|
372
|
-
and request.method == "DELETE"
|
|
373
|
-
):
|
|
374
|
-
return self._get_tenant_id_from_mediafile(request.view_args.get("id"))
|
|
375
|
-
return None
|
|
File without changes
|
|
File without changes
|