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.
- elody/client.py +70 -23
- elody/csv.py +118 -21
- elody/error_codes.py +112 -0
- elody/exceptions.py +14 -0
- elody/job.py +95 -0
- elody/loader.py +33 -5
- elody/migration/__init__.py +0 -0
- elody/migration/base_object_migrator.py +18 -0
- elody/object_configurations/__init__.py +0 -0
- elody/object_configurations/base_object_configuration.py +174 -0
- elody/object_configurations/elody_configuration.py +144 -0
- elody/object_configurations/job_configuration.py +65 -0
- elody/policies/authentication/base_user_tenant_validation_policy.py +48 -15
- elody/policies/authorization/filter_generic_objects_policy.py +68 -22
- elody/policies/authorization/filter_generic_objects_policy_v2.py +166 -0
- elody/policies/authorization/generic_object_detail_policy.py +10 -27
- elody/policies/authorization/generic_object_mediafiles_policy.py +82 -0
- elody/policies/authorization/generic_object_metadata_policy.py +8 -27
- elody/policies/authorization/generic_object_relations_policy.py +12 -29
- elody/policies/authorization/generic_object_request_policy.py +56 -55
- elody/policies/authorization/generic_object_request_policy_v2.py +133 -0
- elody/policies/authorization/mediafile_derivatives_policy.py +92 -0
- elody/policies/authorization/mediafile_download_policy.py +71 -0
- elody/policies/authorization/multi_tenant_policy.py +14 -6
- elody/policies/authorization/tenant_request_policy.py +3 -1
- elody/policies/helpers.py +37 -0
- elody/policies/permission_handler.py +217 -199
- elody/policies/tenant_id_resolver.py +375 -0
- elody/schemas.py +0 -3
- elody/util.py +165 -11
- {elody-0.0.63.dist-info → elody-0.0.162.dist-info}/METADATA +16 -11
- elody-0.0.162.dist-info/RECORD +47 -0
- {elody-0.0.63.dist-info → elody-0.0.162.dist-info}/WHEEL +1 -1
- {elody-0.0.63.dist-info → elody-0.0.162.dist-info}/top_level.txt +1 -0
- tests/__init_.py +0 -0
- tests/data.py +74 -0
- tests/unit/__init__.py +0 -0
- tests/unit/test_csv.py +410 -0
- tests/unit/test_utils.py +293 -0
- elody-0.0.63.dist-info/RECORD +0 -27
- {elody-0.0.63.dist-info → elody-0.0.162.dist-info}/LICENSE +0 -0
|
@@ -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
|
-
|
|
7
|
+
handle_single_item_request,
|
|
8
|
+
mask_protected_content_post_request_hook,
|
|
6
9
|
)
|
|
7
|
-
from elody.util import
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
54
|
-
if
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
86
|
+
"object_restrictions", {}
|
|
85
87
|
)
|
|
86
|
-
for
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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":
|
|
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
|
-
|
|
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")
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from elody.error_codes import ErrorCode, get_error_code, get_read, get_write
|
|
2
|
+
from configuration import get_collection_mapper
|
|
1
3
|
from flask_restful import abort
|
|
2
4
|
from inuits_policy_based_auth import BaseAuthorizationPolicy, RequestContext
|
|
3
5
|
from inuits_policy_based_auth.contexts import UserContext, PolicyContext
|
|
@@ -41,18 +43,24 @@ class MultiTenantPolicy(BaseAuthorizationPolicy):
|
|
|
41
43
|
policy_context.access_verdict = True
|
|
42
44
|
if item_id:
|
|
43
45
|
storage = StorageManager().get_db_engine()
|
|
44
|
-
|
|
46
|
+
request_name = request.path.split("/")[1]
|
|
47
|
+
collection = get_collection_mapper().get(request_name, request_name)
|
|
45
48
|
item = storage.get_item_from_collection_by_id(collection, item_id)
|
|
46
49
|
if not item:
|
|
47
50
|
abort(
|
|
48
51
|
404,
|
|
49
|
-
message=f"Item with id {id} doesn't exist in collection {collection}",
|
|
52
|
+
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}",
|
|
50
53
|
)
|
|
51
54
|
item_relations = storage.get_collection_item_relations(collection, item_id)
|
|
52
|
-
if
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
if (
|
|
56
|
+
item.get("type") != "ticket"
|
|
57
|
+
and not any(
|
|
58
|
+
x
|
|
59
|
+
for x in item_relations
|
|
60
|
+
if x["type"] == "isIn"
|
|
61
|
+
and x["key"] == user_context.x_tenant.raw["_id"]
|
|
62
|
+
)
|
|
63
|
+
and collection != "mediafiles"
|
|
56
64
|
):
|
|
57
65
|
policy_context.access_verdict = False
|
|
58
66
|
if "/filter" in request.path:
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import re as regex
|
|
2
|
+
|
|
1
3
|
from elody.policies.permission_handler import get_permissions
|
|
2
4
|
from flask import Request # pyright: ignore
|
|
3
5
|
from inuits_policy_based_auth import BaseAuthorizationPolicy # pyright: ignore
|
|
@@ -6,7 +8,7 @@ from inuits_policy_based_auth import BaseAuthorizationPolicy # pyright: ignore
|
|
|
6
8
|
class TenantRequestPolicy(BaseAuthorizationPolicy):
|
|
7
9
|
def authorize(self, policy_context, user_context, request_context):
|
|
8
10
|
request: Request = request_context.http_request
|
|
9
|
-
if not
|
|
11
|
+
if not regex.match("^(/[^/]+/v[0-9]+)?/tenants$", request.path):
|
|
10
12
|
return policy_context
|
|
11
13
|
|
|
12
14
|
set_restricting_filter = True
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from elody.error_codes import ErrorCode, get_error_code, get_read
|
|
2
|
+
from configuration import get_object_configuration_mapper # pyright: ignore
|
|
3
|
+
from flask_restful import abort # pyright: ignore
|
|
4
|
+
from serialization.serialize import serialize # pyright: ignore
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_content(item, request, content):
|
|
8
|
+
return serialize(
|
|
9
|
+
content,
|
|
10
|
+
type=item.get("type"),
|
|
11
|
+
from_format=serialize.get_format(
|
|
12
|
+
(request.view_args or {}).get("spec", "elody"), request.args
|
|
13
|
+
),
|
|
14
|
+
to_format=get_object_configuration_mapper().get(item["type"]).SCHEMA_TYPE,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_item(storage_manager, user_context_bag, view_args):
|
|
19
|
+
view_args = view_args or {}
|
|
20
|
+
id = view_args.get("id")
|
|
21
|
+
resolve_collections = user_context_bag.get("collection_resolver")
|
|
22
|
+
if not resolve_collections:
|
|
23
|
+
abort(
|
|
24
|
+
403,
|
|
25
|
+
message=f"{get_error_code(ErrorCode.UNDEFINED_COLLECTION_RESOLVER, get_read())} - Collection resolver not defined for user.",
|
|
26
|
+
)
|
|
27
|
+
collections = resolve_collections(collection=view_args.get("collection"), id=id)
|
|
28
|
+
for collection in collections:
|
|
29
|
+
if item := storage_manager.get_db_engine().get_item_from_collection_by_id(
|
|
30
|
+
collection, id
|
|
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
|
+
)
|