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.
- elody/client.py +70 -23
- elody/csv.py +128 -33
- 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.163.dist-info}/METADATA +16 -11
- elody-0.0.163.dist-info/RECORD +47 -0
- {elody-0.0.63.dist-info → elody-0.0.163.dist-info}/WHEEL +1 -1
- {elody-0.0.63.dist-info → elody-0.0.163.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.163.dist-info}/LICENSE +0 -0
|
@@ -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
|
+
)
|
|
@@ -1,61 +1,34 @@
|
|
|
1
|
+
import re as regex
|
|
2
|
+
|
|
3
|
+
from configuration import get_object_configuration_mapper # pyright: ignore
|
|
1
4
|
from copy import deepcopy
|
|
2
|
-
from elody.
|
|
5
|
+
from elody.error_codes import ErrorCode, get_error_code, get_read
|
|
6
|
+
from elody.util import flatten_dict, interpret_flat_key
|
|
3
7
|
from inuits_policy_based_auth.contexts.user_context import UserContext
|
|
8
|
+
from logging_elody.log import log # pyright: ignore
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
_permissions = {}
|
|
12
|
+
_placeholders = ["X_TENANT_ID", "TENANT_DEFINING_ENTITY_ID"]
|
|
7
13
|
|
|
8
14
|
|
|
9
|
-
def set_permissions(permissions: dict):
|
|
15
|
+
def set_permissions(permissions: dict, placeholders: list[str] = []):
|
|
10
16
|
global _permissions
|
|
11
17
|
_permissions = permissions
|
|
18
|
+
_placeholders.extend(placeholders)
|
|
12
19
|
|
|
13
20
|
|
|
14
21
|
def get_permissions(role: str, user_context: UserContext):
|
|
15
22
|
permissions = deepcopy(_permissions)
|
|
16
|
-
placeholders = ["X_TENANT_ID", "TENANT_DEFINING_ENTITY_ID"]
|
|
17
23
|
|
|
18
|
-
for
|
|
24
|
+
for placeholder_key in _placeholders:
|
|
25
|
+
placeholder_value = user_context.bag.get(placeholder_key.lower())
|
|
19
26
|
permissions = __replace_permission_placeholders(
|
|
20
|
-
permissions,
|
|
27
|
+
permissions, placeholder_key, placeholder_value
|
|
21
28
|
)
|
|
22
29
|
return permissions.get(role, {}) # pyright: ignore
|
|
23
30
|
|
|
24
31
|
|
|
25
|
-
def handle_single_item_request(
|
|
26
|
-
user_context: UserContext, item, permissions, crud, request_body={}
|
|
27
|
-
):
|
|
28
|
-
is_allowed_to_crud_item = __is_allowed_to_crud_item(item, permissions, crud)
|
|
29
|
-
if not is_allowed_to_crud_item:
|
|
30
|
-
return is_allowed_to_crud_item
|
|
31
|
-
|
|
32
|
-
return __is_allowed_to_crud_item_keys(
|
|
33
|
-
user_context, item, permissions, crud, request_body
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def get_mask_protected_content_post_request_hook(
|
|
38
|
-
user_context: UserContext, permissions, item_type=None
|
|
39
|
-
):
|
|
40
|
-
def __post_request_hook(
|
|
41
|
-
response, *, is_single_item_response=False, single_item_sub_route_key=""
|
|
42
|
-
):
|
|
43
|
-
if is_single_item_response:
|
|
44
|
-
if single_item_sub_route_key in ["metadata", "relations"]:
|
|
45
|
-
items = [{single_item_sub_route_key: response[0], "type": item_type}]
|
|
46
|
-
else:
|
|
47
|
-
items = [response[0]]
|
|
48
|
-
else:
|
|
49
|
-
items = response[0]["results"]
|
|
50
|
-
|
|
51
|
-
for item in items:
|
|
52
|
-
__is_allowed_to_crud_item_keys(user_context, item, permissions, "read")
|
|
53
|
-
|
|
54
|
-
return response
|
|
55
|
-
|
|
56
|
-
return __post_request_hook
|
|
57
|
-
|
|
58
|
-
|
|
59
32
|
def __replace_permission_placeholders(data, placeholder_key, placeholder_value):
|
|
60
33
|
if isinstance(data, dict):
|
|
61
34
|
for key, value in data.items():
|
|
@@ -68,183 +41,228 @@ def __replace_permission_placeholders(data, placeholder_key, placeholder_value):
|
|
|
68
41
|
for item in data
|
|
69
42
|
]
|
|
70
43
|
elif isinstance(data, str):
|
|
71
|
-
|
|
44
|
+
if isinstance(placeholder_value, str):
|
|
45
|
+
data = data.replace(placeholder_key, placeholder_value)
|
|
46
|
+
elif isinstance(placeholder_value, list) and data == placeholder_key:
|
|
47
|
+
data = placeholder_value
|
|
72
48
|
return data
|
|
73
49
|
|
|
74
50
|
|
|
75
|
-
def
|
|
76
|
-
|
|
77
|
-
|
|
51
|
+
def handle_single_item_request(
|
|
52
|
+
user_context: UserContext, item, permissions, crud, request_body: dict = {}
|
|
53
|
+
):
|
|
54
|
+
try:
|
|
55
|
+
item_in_storage_format, flat_item, object_lists, restrictions_schema = (
|
|
56
|
+
__prepare_item_for_permission_check(item, permissions, crud)
|
|
57
|
+
)
|
|
78
58
|
|
|
79
|
-
|
|
59
|
+
is_allowed_to_crud_item = (
|
|
60
|
+
__is_allowed_to_crud_item(flat_item, restrictions_schema)
|
|
61
|
+
if flat_item
|
|
62
|
+
else None
|
|
63
|
+
)
|
|
64
|
+
if not is_allowed_to_crud_item:
|
|
65
|
+
return is_allowed_to_crud_item
|
|
66
|
+
|
|
67
|
+
return __is_allowed_to_crud_item_keys(
|
|
68
|
+
user_context,
|
|
69
|
+
item_in_storage_format,
|
|
70
|
+
flat_item,
|
|
71
|
+
restrictions_schema,
|
|
72
|
+
crud,
|
|
73
|
+
object_lists,
|
|
74
|
+
flatten_dict(object_lists, request_body),
|
|
75
|
+
)
|
|
76
|
+
except Exception as exception:
|
|
77
|
+
log.debug(
|
|
78
|
+
f"{exception.__class__.__name__}: {str(exception)}",
|
|
79
|
+
item.get("storage_format", item),
|
|
80
|
+
)
|
|
81
|
+
if crud != "read":
|
|
82
|
+
log.debug(f"Request body: {request_body}", {})
|
|
83
|
+
raise exception
|
|
80
84
|
|
|
81
|
-
for metadata in restrictions.get("metadata", []):
|
|
82
|
-
value = get_item_metadata_value(item, metadata["key"])
|
|
83
|
-
if isinstance(value, str):
|
|
84
|
-
if value not in metadata["value"]:
|
|
85
|
-
return None
|
|
86
|
-
elif isinstance(value, list):
|
|
87
|
-
for expected_value in metadata["value"]:
|
|
88
|
-
if expected_value in value:
|
|
89
|
-
return True
|
|
90
|
-
return None
|
|
91
85
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
86
|
+
def mask_protected_content_post_request_hook(user_context: UserContext, permissions):
|
|
87
|
+
def __post_request_hook(response):
|
|
88
|
+
items = []
|
|
89
|
+
for item in response["results"]:
|
|
90
|
+
try:
|
|
91
|
+
(
|
|
92
|
+
item_in_storage_format,
|
|
93
|
+
flat_item,
|
|
94
|
+
object_lists,
|
|
95
|
+
restrictions_schema,
|
|
96
|
+
) = __prepare_item_for_permission_check(item, permissions, "read")
|
|
97
|
+
if not flat_item:
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
__is_allowed_to_crud_item_keys(
|
|
101
|
+
user_context,
|
|
102
|
+
item_in_storage_format,
|
|
103
|
+
flat_item,
|
|
104
|
+
restrictions_schema,
|
|
105
|
+
"read",
|
|
106
|
+
object_lists,
|
|
107
|
+
)
|
|
108
|
+
items.append(user_context.bag["requested_item"])
|
|
109
|
+
except Exception as exception:
|
|
110
|
+
log.debug(
|
|
111
|
+
f"{exception.__class__.__name__}: {str(exception)}",
|
|
112
|
+
item.get("storage_format", item),
|
|
113
|
+
)
|
|
114
|
+
raise exception
|
|
115
|
+
|
|
116
|
+
response["results"] = items
|
|
117
|
+
return response
|
|
98
118
|
|
|
99
|
-
return
|
|
119
|
+
return __post_request_hook
|
|
100
120
|
|
|
101
121
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if relation["type"] == relation_type
|
|
107
|
-
]
|
|
122
|
+
def __prepare_item_for_permission_check(item, permissions, crud):
|
|
123
|
+
item = deepcopy(item.get("storage_format", item))
|
|
124
|
+
if item.get("type", "") not in permissions[crud].keys():
|
|
125
|
+
return item, None, None, None
|
|
108
126
|
|
|
127
|
+
config = get_object_configuration_mapper().get(item["type"])
|
|
128
|
+
object_lists = config.document_info().get("object_lists", {})
|
|
129
|
+
flat_item = flatten_dict(object_lists, item)
|
|
109
130
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
permissions[crud][item["type"]]
|
|
131
|
+
return (
|
|
132
|
+
item,
|
|
133
|
+
flat_item,
|
|
134
|
+
object_lists,
|
|
135
|
+
__get_restrictions_schema(flat_item, permissions, crud),
|
|
116
136
|
)
|
|
117
137
|
|
|
118
|
-
if keys_permissions:
|
|
119
|
-
initial_item = deepcopy(item)
|
|
120
|
-
for key in item.keys():
|
|
121
|
-
data_key = ""
|
|
122
|
-
if key == "metadata":
|
|
123
|
-
data_key = "key"
|
|
124
|
-
(
|
|
125
|
-
permission_key_data_map,
|
|
126
|
-
data_value_key,
|
|
127
|
-
) = __determine_data_per_permission_key(item, key, data_key, "value")
|
|
128
|
-
elif key == "relations":
|
|
129
|
-
data_key = "type"
|
|
130
|
-
(
|
|
131
|
-
permission_key_data_map,
|
|
132
|
-
data_value_key,
|
|
133
|
-
) = __determine_data_per_permission_key(item, key, data_key, "key")
|
|
134
|
-
else:
|
|
135
|
-
(
|
|
136
|
-
permission_key_data_map,
|
|
137
|
-
data_value_key,
|
|
138
|
-
) = __determine_data_per_permission_key(item, key)
|
|
139
|
-
|
|
140
|
-
for permission_key, data in permission_key_data_map.items():
|
|
141
|
-
if __is_not_valid_request_on_key(
|
|
142
|
-
initial_item,
|
|
143
|
-
keys_permissions,
|
|
144
|
-
negate_condition,
|
|
145
|
-
key,
|
|
146
|
-
permission_key,
|
|
147
|
-
data_value_key,
|
|
148
|
-
):
|
|
149
|
-
if crud == "read":
|
|
150
|
-
data[data_value_key] = "[protected content]"
|
|
151
|
-
else:
|
|
152
|
-
if key == data_value_key:
|
|
153
|
-
if request_body.get(key):
|
|
154
|
-
user_context.bag["soft_call_response_body"].append(key)
|
|
155
|
-
else:
|
|
156
|
-
for element in request_body.get(key, []):
|
|
157
|
-
if f"{key}.{element[data_key]}" == permission_key:
|
|
158
|
-
user_context.bag["soft_call_response_body"].append(
|
|
159
|
-
permission_key
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
return len(user_context.bag["soft_call_response_body"]) == 0
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def __get_keys_permissions(item_permissions):
|
|
166
|
-
if item_permissions.get("keys"):
|
|
167
|
-
negate_condition = False
|
|
168
|
-
keys_permissions = item_permissions["keys"].get("allowed_only", {})
|
|
169
|
-
if not keys_permissions:
|
|
170
|
-
negate_condition = True
|
|
171
|
-
keys_permissions = item_permissions["keys"].get("disallowed_only", {})
|
|
172
|
-
|
|
173
|
-
return keys_permissions, negate_condition
|
|
174
|
-
|
|
175
|
-
return None, None
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def __determine_data_per_permission_key(item, root_key, data_key="", data_value_key=""):
|
|
179
|
-
key_data_map = {}
|
|
180
|
-
|
|
181
|
-
if data_key and data_value_key:
|
|
182
|
-
for data in item[root_key]:
|
|
183
|
-
key_data_map.update({f"{root_key}.{data[data_key]}": data})
|
|
184
|
-
value_key = data_value_key
|
|
185
|
-
else:
|
|
186
|
-
key_data_map.update({root_key: item})
|
|
187
|
-
value_key = root_key
|
|
188
|
-
|
|
189
|
-
return key_data_map, value_key
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def __is_not_valid_request_on_key(
|
|
193
|
-
initial_item,
|
|
194
|
-
keys_permissions,
|
|
195
|
-
negate_condition,
|
|
196
|
-
root_key,
|
|
197
|
-
key,
|
|
198
|
-
data_value_key,
|
|
199
|
-
):
|
|
200
|
-
if root_key == data_value_key:
|
|
201
|
-
is_in_keys_permissions = lambda: key in keys_permissions.keys()
|
|
202
|
-
else:
|
|
203
|
-
is_in_keys_permissions = (
|
|
204
|
-
lambda: key in keys_permissions.keys()
|
|
205
|
-
or f"{root_key}.*" in keys_permissions.keys()
|
|
206
|
-
)
|
|
207
138
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
139
|
+
def __get_restrictions_schema(flat_item, permissions, crud):
|
|
140
|
+
schema_type = flat_item.get("schema.type", "elody")
|
|
141
|
+
schema_version = flat_item.get("schema.version", "1")
|
|
142
|
+
schema = f"{schema_type}:{schema_version}"
|
|
143
|
+
|
|
144
|
+
schemas = permissions[crud][flat_item["type"]]
|
|
145
|
+
if restrictions_schema := schemas.get(schema):
|
|
146
|
+
return restrictions_schema
|
|
147
|
+
|
|
148
|
+
for schema in reversed(schemas.keys()):
|
|
149
|
+
if regex.match(f"^{schema_type}:[0-9]{1,3}?$", schema):
|
|
150
|
+
break
|
|
151
|
+
schema = None
|
|
152
|
+
return schemas[schema] if schemas and schema else {}
|
|
217
153
|
|
|
218
|
-
return False
|
|
219
154
|
|
|
155
|
+
def __is_allowed_to_crud_item(flat_item, restrictions_schema):
|
|
156
|
+
restrictions = restrictions_schema.get("object_restrictions", {})
|
|
157
|
+
|
|
158
|
+
for restricted_key, restricting_values in restrictions.items():
|
|
159
|
+
restricted_key = restricted_key.split(":")[1]
|
|
160
|
+
item_value_in_restricting_values = __item_value_in_values(
|
|
161
|
+
flat_item, restricted_key, restricting_values
|
|
162
|
+
)
|
|
163
|
+
if not item_value_in_restricting_values:
|
|
164
|
+
return None
|
|
220
165
|
|
|
221
|
-
|
|
222
|
-
|
|
166
|
+
return True
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def __is_allowed_to_crud_item_keys(
|
|
170
|
+
user_context: UserContext,
|
|
171
|
+
item_in_storage_format,
|
|
172
|
+
flat_item,
|
|
173
|
+
restrictions_schema,
|
|
174
|
+
crud,
|
|
175
|
+
object_lists,
|
|
176
|
+
flat_request_body: dict = {},
|
|
223
177
|
):
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
178
|
+
user_context.bag["restricted_keys"] = []
|
|
179
|
+
restrictions = restrictions_schema.get("key_restrictions", {})
|
|
180
|
+
|
|
181
|
+
for restricted_key, restricting_conditions in restrictions.items():
|
|
182
|
+
restricted_key = restricted_key.split(":")[1]
|
|
183
|
+
condition_match = True
|
|
184
|
+
for condition_key, condition_values in restricting_conditions.items():
|
|
185
|
+
condition_match = __item_value_in_values(
|
|
186
|
+
flat_item, condition_key, condition_values, flat_request_body
|
|
187
|
+
)
|
|
188
|
+
if not condition_match:
|
|
189
|
+
break
|
|
190
|
+
|
|
191
|
+
if condition_match:
|
|
192
|
+
if crud == "read":
|
|
193
|
+
keys_info = interpret_flat_key(restricted_key, object_lists)
|
|
194
|
+
for info in keys_info:
|
|
195
|
+
if info["object_list"]:
|
|
196
|
+
element = __get_element_from_object_list_of_item(
|
|
197
|
+
item_in_storage_format,
|
|
198
|
+
info["key"],
|
|
199
|
+
info["object_key"],
|
|
200
|
+
object_lists,
|
|
201
|
+
)
|
|
202
|
+
item_in_storage_format[info["key"]].remove(element)
|
|
203
|
+
break
|
|
204
|
+
else:
|
|
205
|
+
try:
|
|
206
|
+
del item_in_storage_format[keys_info[0]["key"]][
|
|
207
|
+
keys_info[1]["key"]
|
|
208
|
+
]
|
|
209
|
+
except KeyError:
|
|
210
|
+
pass
|
|
211
|
+
else:
|
|
212
|
+
if flat_request_body.get(restricted_key):
|
|
213
|
+
user_context.bag["restricted_keys"].append(restricted_key)
|
|
214
|
+
|
|
215
|
+
user_context.bag["requested_item"] = item_in_storage_format
|
|
216
|
+
return len(user_context.bag["restricted_keys"]) == 0
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def __item_value_in_values(flat_item, key, values: list, flat_request_body: dict = {}):
|
|
220
|
+
negate_condition = False
|
|
221
|
+
is_optional = False
|
|
222
|
+
|
|
223
|
+
if key[0] == "!":
|
|
224
|
+
key = key[1:]
|
|
225
|
+
negate_condition = True
|
|
226
|
+
if key[0] == "?":
|
|
227
|
+
key = key[1:]
|
|
228
|
+
is_optional = True
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
item_value = flat_request_body.get(key, flat_item[key])
|
|
232
|
+
except KeyError:
|
|
233
|
+
if not is_optional:
|
|
234
|
+
raise Exception(
|
|
235
|
+
f"{get_error_code(ErrorCode.METADATA_KEY_UNDEFINED, get_read())} | key:{key} | document:{flat_item.get('_id', flat_item["type"])} - Key {key} not found in document {flat_item.get('_id', flat_item["type"])}. Either prefix the key with '?' in your permission configuration to make it an optional restriction, or patch the document to include the key. '?' will allow access if key does not exist, '!?' will deny access if key does not exist."
|
|
236
236
|
)
|
|
237
|
+
return not negate_condition
|
|
238
|
+
|
|
239
|
+
expected_values = []
|
|
240
|
+
for value in values:
|
|
241
|
+
if flat_item_key_value := flat_item.get(value):
|
|
242
|
+
value = flat_item_key_value
|
|
243
|
+
if isinstance(value, list):
|
|
244
|
+
expected_values.extend(value)
|
|
245
|
+
else:
|
|
246
|
+
expected_values.append(value)
|
|
247
|
+
|
|
248
|
+
if isinstance(item_value, (str, int, float, bool)):
|
|
249
|
+
if negate_condition:
|
|
250
|
+
return item_value not in expected_values
|
|
251
|
+
else:
|
|
252
|
+
return item_value in expected_values
|
|
253
|
+
elif isinstance(item_value, list):
|
|
254
|
+
for expected_value in expected_values:
|
|
255
|
+
if expected_value in item_value:
|
|
256
|
+
return True != negate_condition
|
|
257
|
+
return False != negate_condition
|
|
237
258
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return
|
|
247
|
-
|
|
248
|
-
return False
|
|
249
|
-
|
|
250
|
-
return not return_value
|
|
259
|
+
raise Exception(f"Invalid item_value: {item_value}")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def __get_element_from_object_list_of_item(
|
|
263
|
+
item: dict, object_list: str, key: str, object_lists: dict
|
|
264
|
+
):
|
|
265
|
+
for element in item[object_list]:
|
|
266
|
+
if element[object_lists[object_list]] == key:
|
|
267
|
+
return element
|
|
268
|
+
return {}
|