elody 0.0.63__py3-none-any.whl → 0.0.163__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 +128 -33
  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.163.dist-info}/METADATA +16 -11
  32. elody-0.0.163.dist-info/RECORD +47 -0
  33. {elody-0.0.63.dist-info → elody-0.0.163.dist-info}/WHEEL +1 -1
  34. {elody-0.0.63.dist-info → elody-0.0.163.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.163.dist-info}/LICENSE +0 -0
@@ -1,12 +1,11 @@
1
1
  import re as regex
2
2
 
3
+ from elody.policies.helpers import get_content, get_item
3
4
  from elody.policies.permission_handler import (
4
- get_mask_protected_content_post_request_hook,
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,25 +21,10 @@ class GenericObjectMetadataPolicy(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 user_context.auth_objects.get("token") or not regex.match(
26
- "^/[^/]+/[^/]+/metadata$", request.path
27
- ):
24
+ if not regex.match("^(/elody/v[0-9]+)?/[^/]+/[^/]+/metadata$", request.path):
28
25
  return policy_context
29
26
 
30
- view_args = request.view_args or {}
31
- collection = view_args.get("collection", request.path.split("/")[1])
32
- id = view_args.get("id")
33
- item = (
34
- StorageManager()
35
- .get_db_engine()
36
- .get_item_from_collection_by_id(collection, id)
37
- )
38
- if not item:
39
- abort(
40
- 404,
41
- message=f"Item with id {id} doesn't exist in collection {collection}",
42
- )
43
-
27
+ item = get_item(StorageManager(), user_context.bag, request.view_args)
44
28
  for role in user_context.x_tenant.roles:
45
29
  permissions = get_permissions(role, user_context)
46
30
  if not permissions:
@@ -53,7 +37,7 @@ class GenericObjectMetadataPolicy(BaseAuthorizationPolicy):
53
37
  if access_verdict != None:
54
38
  policy_context.access_verdict = access_verdict
55
39
  if not policy_context.access_verdict:
56
- return policy_context
40
+ break
57
41
 
58
42
  if policy_context.access_verdict:
59
43
  return policy_context
@@ -68,11 +52,6 @@ class GetRequestRules:
68
52
  if request.method != "GET":
69
53
  return None
70
54
 
71
- user_context.access_restrictions.post_request_hook = (
72
- get_mask_protected_content_post_request_hook(
73
- user_context, permissions, item["type"]
74
- )
75
- )
76
55
  return handle_single_item_request(user_context, item, permissions, "read")
77
56
 
78
57
 
@@ -83,8 +62,9 @@ class PutRequestRules:
83
62
  if request.method != "PUT":
84
63
  return None
85
64
 
65
+ content = get_content(item, request, {"metadata": request.json})
86
66
  return handle_single_item_request(
87
- user_context, item, permissions, "update", {"metadata": request.json}
67
+ user_context, item, permissions, "update", content
88
68
  )
89
69
 
90
70
 
@@ -95,6 +75,7 @@ class PatchRequestRules:
95
75
  if request.method != "PATCH":
96
76
  return None
97
77
 
78
+ content = get_content(item, request, {"metadata": request.json})
98
79
  return handle_single_item_request(
99
- user_context, item, permissions, "update", {"metadata": request.json}
80
+ user_context, item, permissions, "update", content
100
81
  )
@@ -1,12 +1,11 @@
1
1
  import re as regex
2
2
 
3
+ from elody.policies.helpers import get_content, get_item
3
4
  from elody.policies.permission_handler import (
4
- get_mask_protected_content_post_request_hook,
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,25 +21,10 @@ class GenericObjectRelationsPolicy(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 user_context.auth_objects.get("token") or not regex.match(
26
- "^/[^/]+/[^/]+/relations$", request.path
27
- ):
24
+ if not regex.match("^(/elody/v[0-9]+)?/[^/]+/[^/]+/relations$", request.path):
28
25
  return policy_context
29
26
 
30
- view_args = request.view_args or {}
31
- collection = view_args.get("collection", request.path.split("/")[1])
32
- id = view_args.get("id")
33
- item = (
34
- StorageManager()
35
- .get_db_engine()
36
- .get_item_from_collection_by_id(collection, id)
37
- )
38
- if not item:
39
- abort(
40
- 404,
41
- message=f"Item with id {id} doesn't exist in collection {collection}",
42
- )
43
-
27
+ item = get_item(StorageManager(), user_context.bag, request.view_args)
44
28
  for role in user_context.x_tenant.roles:
45
29
  permissions = get_permissions(role, user_context)
46
30
  if not permissions:
@@ -59,7 +43,7 @@ class GenericObjectRelationsPolicy(BaseAuthorizationPolicy):
59
43
  if access_verdict != None:
60
44
  policy_context.access_verdict = access_verdict
61
45
  if not policy_context.access_verdict:
62
- return policy_context
46
+ break
63
47
 
64
48
  if policy_context.access_verdict:
65
49
  return policy_context
@@ -74,8 +58,9 @@ class PostRequestRules:
74
58
  if request.method != "POST":
75
59
  return None
76
60
 
61
+ content = get_content(item, request, {"relations": request.json})
77
62
  return handle_single_item_request(
78
- user_context, item, permissions, "create", {"relations": request.json}
63
+ user_context, item, permissions, "create", content
79
64
  )
80
65
 
81
66
 
@@ -86,11 +71,6 @@ class GetRequestRules:
86
71
  if request.method != "GET":
87
72
  return None
88
73
 
89
- user_context.access_restrictions.post_request_hook = (
90
- get_mask_protected_content_post_request_hook(
91
- user_context, permissions, item["type"]
92
- )
93
- )
94
74
  return handle_single_item_request(user_context, item, permissions, "read")
95
75
 
96
76
 
@@ -101,8 +81,9 @@ class PutRequestRules:
101
81
  if request.method != "PUT":
102
82
  return None
103
83
 
84
+ content = get_content(item, request, {"relations": request.json})
104
85
  return handle_single_item_request(
105
- user_context, item, permissions, "update", {"relations": request.json}
86
+ user_context, item, permissions, "update", content
106
87
  )
107
88
 
108
89
 
@@ -113,8 +94,9 @@ class PatchRequestRules:
113
94
  if request.method != "PATCH":
114
95
  return None
115
96
 
97
+ content = get_content(item, request, {"relations": request.json})
116
98
  return handle_single_item_request(
117
- user_context, item, permissions, "update", {"relations": request.json}
99
+ user_context, item, permissions, "update", content
118
100
  )
119
101
 
120
102
 
@@ -125,6 +107,7 @@ class DeleteRequestRules:
125
107
  if request.method != "DELETE":
126
108
  return None
127
109
 
110
+ content = get_content(item, request, {"relations": request.json})
128
111
  return handle_single_item_request(
129
- user_context, item, permissions, "delete", {"relations": request.json}
112
+ user_context, item, permissions, "delete", content
130
113
  )
@@ -1,10 +1,13 @@
1
1
  import re as regex
2
2
 
3
+ from elody.policies.helpers import get_content
4
+ from configuration import get_object_configuration_mapper # pyright: ignore
3
5
  from elody.policies.permission_handler import (
4
6
  get_permissions,
5
- get_mask_protected_content_post_request_hook,
7
+ handle_single_item_request,
8
+ mask_protected_content_post_request_hook,
6
9
  )
7
- from elody.util import get_item_metadata_value
10
+ from elody.util import interpret_flat_key
8
11
  from flask import Request # pyright: ignore
9
12
  from inuits_policy_based_auth import BaseAuthorizationPolicy # pyright: ignore
10
13
  from inuits_policy_based_auth.contexts.policy_context import ( # pyright: ignore
@@ -20,9 +23,7 @@ class GenericObjectRequestPolicy(BaseAuthorizationPolicy):
20
23
  self, policy_context: PolicyContext, user_context: UserContext, request_context
21
24
  ):
22
25
  request: Request = request_context.http_request
23
- if not user_context.auth_objects.get("token") or not regex.match(
24
- "^/[^/]+$|^/ngsi-ld/v1/entities$", request.path
25
- ):
26
+ if not regex.match("^(/[^/]+/v[0-9]+)?/[^/]+$", request.path):
26
27
  return policy_context
27
28
 
28
29
  for role in user_context.x_tenant.roles:
@@ -37,7 +38,7 @@ class GenericObjectRequestPolicy(BaseAuthorizationPolicy):
37
38
  if access_verdict != None:
38
39
  policy_context.access_verdict = access_verdict
39
40
  if not policy_context.access_verdict:
40
- return policy_context
41
+ break
41
42
 
42
43
  if policy_context.access_verdict:
43
44
  return policy_context
@@ -46,25 +47,20 @@ class GenericObjectRequestPolicy(BaseAuthorizationPolicy):
46
47
 
47
48
 
48
49
  class PostRequestRules:
49
- def apply(self, _, request: Request, permissions) -> bool | None:
50
+ def apply(
51
+ self, user_context: UserContext, request: Request, permissions
52
+ ) -> bool | None:
50
53
  if request.method != "POST":
51
54
  return None
52
-
53
- item = request.json or {}
54
- if item["type"] in permissions["create"].keys():
55
- restrictions = permissions["create"][item["type"]].get("restrictions", {})
56
- for metadata in restrictions.get("metadata", []):
57
- value = get_item_metadata_value(item, metadata["key"])
58
- if isinstance(value, str):
59
- if value not in metadata["value"]:
60
- return None
61
- elif isinstance(value, list):
62
- for expected_value in metadata["value"]:
63
- if expected_value not in value:
64
- return None
55
+ if request.args.get("dry_run", False):
56
+ return True
57
+ if regex.match(r"^/batch?$", request.path):
65
58
  return True
66
59
 
67
- return None
60
+ content = get_content(request.json, request, request.json)
61
+ return handle_single_item_request(
62
+ user_context, request.json, permissions, "create", content
63
+ )
68
64
 
69
65
 
70
66
  class GetRequestRules:
@@ -73,45 +69,27 @@ class GetRequestRules:
73
69
  ) -> bool | None:
74
70
  if request.method != "GET":
75
71
  return None
76
-
77
- type_query_parameter = request.args.get("type")
72
+ type_query_parameter = (
73
+ "mediafile"
74
+ if regex.match(r"^/mediafiles(?:\?(.*))?$", request.path)
75
+ else request.args.get("type")
76
+ )
78
77
  allowed_item_types = list(permissions["read"].keys())
79
78
  filters = []
80
79
 
81
80
  if type_query_parameter:
82
81
  if type_query_parameter in allowed_item_types:
82
+ config = get_object_configuration_mapper().get(type_query_parameter)
83
+ object_lists = config.document_info()["object_lists"]
84
+
83
85
  restrictions = permissions["read"][type_query_parameter].get(
84
- "restrictions", {}
86
+ "object_restrictions", {}
85
87
  )
86
- for parent_key in restrictions.keys():
87
- all_matches = []
88
- if parent_key == "metadata":
89
- for metadata in restrictions[parent_key]:
90
- all_matches.append(
91
- {
92
- "$elemMatch": {
93
- "key": metadata["key"],
94
- "value": {"$in": metadata["value"]},
95
- }
96
- }
97
- )
98
- filters.append({parent_key: {"$all": all_matches}})
99
- elif parent_key == "relations":
100
- for relation in restrictions[parent_key]:
101
- all_matches.append(
102
- {
103
- "$elemMatch": {
104
- "type": relation["key"],
105
- "key": {"$in": relation["value"]},
106
- }
107
- }
108
- )
109
- filters.append({parent_key: {"$all": all_matches}})
110
- elif parent_key == "root":
111
- for restriction in restrictions[parent_key]:
112
- filters.append(
113
- {restriction["key"]: {"$in": restriction["value"]}}
114
- )
88
+ for key, value in restrictions.items():
89
+ keys_info = interpret_flat_key(key, object_lists)
90
+ filters.append(
91
+ _build_nested_matcher(object_lists, keys_info, value)
92
+ )
115
93
  else:
116
94
  return None
117
95
  else:
@@ -123,7 +101,10 @@ class GetRequestRules:
123
101
  "key": user_context.bag.get(
124
102
  "tenant_defining_entity_id", user_context.x_tenant.id
125
103
  ),
126
- "type": user_context.bag["tenant_relation_type"],
104
+ "type": [
105
+ user_context.bag["tenant_relation_type"],
106
+ "belongsTo",
107
+ ],
127
108
  }
128
109
  }
129
110
  },
@@ -131,6 +112,26 @@ class GetRequestRules:
131
112
 
132
113
  user_context.access_restrictions.filters = filters
133
114
  user_context.access_restrictions.post_request_hook = (
134
- get_mask_protected_content_post_request_hook(user_context, permissions)
115
+ mask_protected_content_post_request_hook(user_context, permissions)
135
116
  )
136
117
  return True
118
+
119
+
120
+ def _build_nested_matcher(object_lists, keys_info, value, index=0):
121
+ info = keys_info[index]
122
+
123
+ if info["object_list"]:
124
+ nested_matcher = _build_nested_matcher(
125
+ object_lists, keys_info, value, index + 1
126
+ )
127
+ elem_match = {
128
+ "$elemMatch": {
129
+ object_lists[info["key"]]: info["object_key"],
130
+ keys_info[index + 1]["key"]: nested_matcher,
131
+ }
132
+ }
133
+ return elem_match if index > 0 else {info["key"]: elem_match}
134
+
135
+ if isinstance(value, list):
136
+ value = {"$in": value}
137
+ return value if index > 0 else {info["key"]: value}
@@ -0,0 +1,133 @@
1
+ import re as regex
2
+
3
+ from configuration import get_object_configuration_mapper # pyright: ignore
4
+ from elody.policies.helpers import get_content
5
+ from elody.policies.permission_handler import (
6
+ get_permissions,
7
+ handle_single_item_request,
8
+ mask_protected_content_post_request_hook,
9
+ )
10
+ from flask import Request # pyright: ignore
11
+ from inuits_policy_based_auth import BaseAuthorizationPolicy # pyright: ignore
12
+ from inuits_policy_based_auth.contexts.policy_context import ( # pyright: ignore
13
+ PolicyContext,
14
+ )
15
+ from inuits_policy_based_auth.contexts.user_context import ( # pyright: ignore
16
+ UserContext,
17
+ )
18
+
19
+
20
+ class GenericObjectRequestPolicyV2(BaseAuthorizationPolicy):
21
+ def authorize(
22
+ self, policy_context: PolicyContext, user_context: UserContext, request_context
23
+ ):
24
+ request: Request = request_context.http_request
25
+ if not regex.match("^(/[^/]+/v[0-9]+)?/[^/]+$", request.path):
26
+ return policy_context
27
+
28
+ for role in user_context.x_tenant.roles:
29
+ permissions = get_permissions(role, user_context)
30
+ if not permissions:
31
+ continue
32
+
33
+ rules = [PostRequestRules, GetRequestRules]
34
+ access_verdict = None
35
+ for rule in rules:
36
+ access_verdict = rule().apply(user_context, request, permissions)
37
+ if access_verdict != None:
38
+ policy_context.access_verdict = access_verdict
39
+ if not policy_context.access_verdict:
40
+ break
41
+
42
+ if policy_context.access_verdict:
43
+ return policy_context
44
+
45
+ return policy_context
46
+
47
+
48
+ class PostRequestRules:
49
+ def apply(
50
+ self, user_context: UserContext, request: Request, permissions
51
+ ) -> bool | None:
52
+ if request.method != "POST":
53
+ return None
54
+
55
+ content = get_content(request.json, request, request.json)
56
+ schema_type = get_object_configuration_mapper().get(content["type"]).SCHEMA_TYPE
57
+ item = {**content, "schema": {"type": schema_type}}
58
+ return handle_single_item_request(
59
+ user_context, item, permissions, "create", content
60
+ )
61
+
62
+
63
+ class GetRequestRules:
64
+ def apply(
65
+ self, user_context: UserContext, request: Request, permissions
66
+ ) -> bool | None:
67
+ if request.method != "GET":
68
+ return None
69
+
70
+ type_query_parameter = request.args.get("type")
71
+ allowed_item_types = list(permissions["read"].keys())
72
+ filters = []
73
+
74
+ if type_query_parameter:
75
+ if type_query_parameter not in allowed_item_types:
76
+ return None
77
+ type_permissions = permissions["read"][type_query_parameter]
78
+ schemas = list(type_permissions.keys())
79
+ if len(schemas) > 0:
80
+ number_of_object_restrictions = len(
81
+ type_permissions[schemas[0]].get("object_restrictions", {}).keys()
82
+ )
83
+ for i in range(number_of_object_restrictions):
84
+ keys, values = [], []
85
+ for schema in schemas:
86
+ object_restrictions = type_permissions[schema].get(
87
+ "object_restrictions", {}
88
+ )
89
+ key = [
90
+ key
91
+ for key in object_restrictions.keys()
92
+ if key.startswith(f"{i}:")
93
+ ][0]
94
+ keys.append(f"{schema}|{key.split(':')[1]}")
95
+ values = object_restrictions[key]
96
+ filters.append(
97
+ {
98
+ "type": "selection",
99
+ "key": keys,
100
+ "value": values,
101
+ "match_exact": True,
102
+ }
103
+ )
104
+ filters.insert(0, {"type": "type", "value": type_query_parameter})
105
+ else:
106
+ filters.append(
107
+ {
108
+ "type": "selection",
109
+ "key": "type",
110
+ "value": allowed_item_types,
111
+ "match_exact": True,
112
+ }
113
+ )
114
+ if tenant_relation_type := user_context.bag.get("tenant_relation_type"):
115
+ filters.append(
116
+ {
117
+ "type": "selection",
118
+ "key": tenant_relation_type,
119
+ "value": [
120
+ "tenant:super",
121
+ user_context.bag.get(
122
+ "tenant_defining_entity_id", user_context.x_tenant.id
123
+ ),
124
+ ],
125
+ "match_exact": True,
126
+ }
127
+ )
128
+
129
+ user_context.access_restrictions.filters = filters
130
+ user_context.access_restrictions.post_request_hook = (
131
+ mask_protected_content_post_request_hook(user_context, permissions)
132
+ )
133
+ return True
@@ -0,0 +1,92 @@
1
+ import re as regex
2
+
3
+ from elody.error_codes import ErrorCode, get_error_code, get_read, get_write
4
+ from elody.policies.permission_handler import (
5
+ get_permissions,
6
+ handle_single_item_request,
7
+ )
8
+ from flask import Request # pyright: ignore
9
+ from flask_restful import abort # pyright: ignore
10
+ from inuits_policy_based_auth import BaseAuthorizationPolicy # pyright: ignore
11
+ from inuits_policy_based_auth.contexts.policy_context import ( # pyright: ignore
12
+ PolicyContext,
13
+ )
14
+ from inuits_policy_based_auth.contexts.user_context import ( # pyright: ignore
15
+ UserContext,
16
+ )
17
+ from storage.storagemanager import StorageManager # pyright: ignore
18
+
19
+
20
+ class MediafileDerivativesPolicy(BaseAuthorizationPolicy):
21
+ def authorize(
22
+ self, policy_context: PolicyContext, user_context: UserContext, request_context
23
+ ):
24
+ request: Request = request_context.http_request
25
+ if not regex.match(r"^/mediafiles/(.+)/derivatives$", request.path):
26
+ return policy_context
27
+
28
+ view_args = request.view_args or {}
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
+
42
+ for role in user_context.x_tenant.roles:
43
+ permissions = get_permissions(role, user_context)
44
+ if not permissions:
45
+ continue
46
+
47
+ rules = [
48
+ PostRequestRules,
49
+ GetRequestRules,
50
+ ]
51
+ access_verdict = None
52
+ for rule in rules:
53
+ access_verdict = rule().apply(item, user_context, request, permissions)
54
+ if access_verdict != None:
55
+ policy_context.access_verdict = access_verdict
56
+ if not policy_context.access_verdict:
57
+ break
58
+
59
+ if policy_context.access_verdict:
60
+ return policy_context
61
+
62
+ return policy_context
63
+
64
+
65
+ class PostRequestRules:
66
+ def apply(
67
+ self, item, user_context: UserContext, request: Request, permissions
68
+ ) -> bool | None:
69
+ if request.method != "POST":
70
+ return None
71
+
72
+ return handle_single_item_request(user_context, item, permissions, "create")
73
+
74
+
75
+ class GetRequestRules:
76
+ def apply(
77
+ self, item, user_context: UserContext, request: Request, permissions
78
+ ) -> bool | None:
79
+ if request.method != "GET":
80
+ return None
81
+
82
+ 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")
@@ -0,0 +1,71 @@
1
+ import re as regex
2
+
3
+ from elody.error_codes import ErrorCode, get_error_code, get_read
4
+ from elody.policies.permission_handler import (
5
+ get_permissions,
6
+ handle_single_item_request,
7
+ )
8
+ from flask import Request # pyright: ignore
9
+ from flask_restful import abort # pyright: ignore
10
+ from inuits_policy_based_auth import BaseAuthorizationPolicy # pyright: ignore
11
+ from inuits_policy_based_auth.contexts.policy_context import ( # pyright: ignore
12
+ PolicyContext,
13
+ )
14
+ from inuits_policy_based_auth.contexts.user_context import ( # pyright: ignore
15
+ UserContext,
16
+ )
17
+ from storage.storagemanager import StorageManager # pyright: ignore
18
+
19
+
20
+ class MediafileDownloadPolicy(BaseAuthorizationPolicy):
21
+ def authorize(
22
+ self, policy_context: PolicyContext, user_context: UserContext, request_context
23
+ ):
24
+ request: Request = request_context.http_request
25
+ if not regex.match(r"^/mediafiles/(.+)/download$", request.path):
26
+ return policy_context
27
+
28
+ view_args = request.view_args or {}
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
+
42
+ for role in user_context.x_tenant.roles:
43
+ permissions = get_permissions(role, user_context)
44
+ if not permissions:
45
+ continue
46
+
47
+ rules = [
48
+ GetRequestRules,
49
+ ]
50
+ access_verdict = None
51
+ for rule in rules:
52
+ access_verdict = rule().apply(item, user_context, request, permissions)
53
+ if access_verdict != None:
54
+ policy_context.access_verdict = access_verdict
55
+ if not policy_context.access_verdict:
56
+ break
57
+
58
+ if policy_context.access_verdict:
59
+ return policy_context
60
+
61
+ return policy_context
62
+
63
+
64
+ class GetRequestRules:
65
+ def apply(
66
+ self, item, user_context: UserContext, request: Request, permissions
67
+ ) -> bool | None:
68
+ if request.method != "GET":
69
+ return None
70
+
71
+ return handle_single_item_request(user_context, item, permissions, "read")