elody 0.0.226__tar.gz → 0.0.227__tar.gz

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 (53) hide show
  1. {elody-0.0.226 → elody-0.0.227}/PKG-INFO +1 -1
  2. {elody-0.0.226 → elody-0.0.227}/pyproject.toml +1 -1
  3. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/filter_generic_objects_policy_v2.py +5 -52
  4. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/permission_handler.py +143 -4
  5. {elody-0.0.226 → elody-0.0.227}/src/elody.egg-info/PKG-INFO +1 -1
  6. {elody-0.0.226 → elody-0.0.227}/LICENSE +0 -0
  7. {elody-0.0.226 → elody-0.0.227}/README.md +0 -0
  8. {elody-0.0.226 → elody-0.0.227}/setup.cfg +0 -0
  9. {elody-0.0.226 → elody-0.0.227}/src/__init__.py +0 -0
  10. {elody-0.0.226 → elody-0.0.227}/src/elody/__init__.py +0 -0
  11. {elody-0.0.226 → elody-0.0.227}/src/elody/client.py +0 -0
  12. {elody-0.0.226 → elody-0.0.227}/src/elody/csv.py +0 -0
  13. {elody-0.0.226 → elody-0.0.227}/src/elody/error_codes.py +0 -0
  14. {elody-0.0.226 → elody-0.0.227}/src/elody/exceptions.py +0 -0
  15. {elody-0.0.226 → elody-0.0.227}/src/elody/job.py +0 -0
  16. {elody-0.0.226 → elody-0.0.227}/src/elody/loader.py +0 -0
  17. {elody-0.0.226 → elody-0.0.227}/src/elody/migration/__init__.py +0 -0
  18. {elody-0.0.226 → elody-0.0.227}/src/elody/migration/base_object_migrator.py +0 -0
  19. {elody-0.0.226 → elody-0.0.227}/src/elody/object_configurations/__init__.py +0 -0
  20. {elody-0.0.226 → elody-0.0.227}/src/elody/object_configurations/base_object_configuration.py +0 -0
  21. {elody-0.0.226 → elody-0.0.227}/src/elody/object_configurations/elody_configuration.py +0 -0
  22. {elody-0.0.226 → elody-0.0.227}/src/elody/object_configurations/job_configuration.py +0 -0
  23. {elody-0.0.226 → elody-0.0.227}/src/elody/object_configurations/saved_search_configuration.py +0 -0
  24. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/__init__.py +0 -0
  25. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authentication/__init__.py +0 -0
  26. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authentication/base_user_tenant_validation_policy.py +0 -0
  27. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authentication/multi_tenant_policy.py +0 -0
  28. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authentication/x_user_headers_policy.py +0 -0
  29. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/__init__.py +0 -0
  30. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/filter_generic_objects_policy.py +0 -0
  31. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/generic_object_detail_policy.py +0 -0
  32. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/generic_object_mediafiles_policy.py +0 -0
  33. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/generic_object_metadata_policy.py +0 -0
  34. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/generic_object_relations_policy.py +0 -0
  35. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/generic_object_request_policy.py +0 -0
  36. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/generic_object_request_policy_v2.py +0 -0
  37. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/mediafile_derivatives_policy.py +0 -0
  38. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/mediafile_download_policy.py +0 -0
  39. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/multi_tenant_policy.py +0 -0
  40. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/authorization/tenant_request_policy.py +0 -0
  41. {elody-0.0.226 → elody-0.0.227}/src/elody/policies/helpers.py +0 -0
  42. {elody-0.0.226 → elody-0.0.227}/src/elody/schemas.py +0 -0
  43. {elody-0.0.226 → elody-0.0.227}/src/elody/util.py +0 -0
  44. {elody-0.0.226 → elody-0.0.227}/src/elody/validator.py +0 -0
  45. {elody-0.0.226 → elody-0.0.227}/src/elody.egg-info/SOURCES.txt +0 -0
  46. {elody-0.0.226 → elody-0.0.227}/src/elody.egg-info/dependency_links.txt +0 -0
  47. {elody-0.0.226 → elody-0.0.227}/src/elody.egg-info/requires.txt +0 -0
  48. {elody-0.0.226 → elody-0.0.227}/src/elody.egg-info/top_level.txt +0 -0
  49. {elody-0.0.226 → elody-0.0.227}/src/tests/__init_.py +0 -0
  50. {elody-0.0.226 → elody-0.0.227}/src/tests/data.py +0 -0
  51. {elody-0.0.226 → elody-0.0.227}/src/tests/unit/__init__.py +0 -0
  52. {elody-0.0.226 → elody-0.0.227}/src/tests/unit/test_csv.py +0 -0
  53. {elody-0.0.226 → elody-0.0.227}/src/tests/unit/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: elody
3
- Version: 0.0.226
3
+ Version: 0.0.227
4
4
  Summary: elody SDK for Python
5
5
  Author-email: Inuits <developers@inuits.eu>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "elody"
9
- version = "0.0.226"
9
+ version = "0.0.227"
10
10
  description = "elody SDK for Python"
11
11
  readme = "README.md"
12
12
  authors = [{ name = "Inuits", email = "developers@inuits.eu" }]
@@ -1,9 +1,9 @@
1
1
  import re as regex
2
2
 
3
3
  from copy import deepcopy
4
- from elody.policies.helpers import generate_filter_key_and_lookup_from_restricted_key
5
4
  from elody.policies.permission_handler import (
6
5
  get_permissions,
6
+ handle_item_overview_request,
7
7
  mask_protected_content_post_request_hook,
8
8
  )
9
9
  from flask import g, Request # pyright: ignore
@@ -108,58 +108,11 @@ class PostRequestRules:
108
108
  type_filter_values.remove(type_filter_value)
109
109
  continue
110
110
 
111
- restrictions_grouped_by_index = {}
112
111
  schemas = permissions["read"][type_filter_value]
113
- for schema in schemas.keys():
114
- restrictions = schemas[schema].get("object_restrictions", {})
115
- for restricted_key, restricting_value in restrictions.items():
116
- index, restricted_key = restricted_key.split(":")
117
- restricted_key, lookup = (
118
- generate_filter_key_and_lookup_from_restricted_key(
119
- restricted_key
120
- )
121
- )
122
- key = f"{schema}|{restricted_key}"
123
- if group := restrictions_grouped_by_index.get(index):
124
- group["key"].append(key)
125
- else:
126
- restrictions_grouped_by_index.update(
127
- {
128
- index: {
129
- "lookup": lookup,
130
- "key": [key],
131
- "value": restricting_value,
132
- }
133
- }
134
- )
135
-
136
- # support false soft call read responses
137
- for filter in filters:
138
- key = filter.get("key", "")
139
- if isinstance(key, list):
140
- key = ",".join(key)
141
- for restriction in restrictions_grouped_by_index.values():
142
- if key not in ",".join(restriction["key"]):
143
- continue
144
- values = (
145
- filter["value"]
146
- if isinstance(filter["value"], list)
147
- else [filter["value"]]
148
- )
149
- for value in values:
150
- if value not in restriction["value"] and value not in ["", "*"]:
151
- return False
152
-
153
- for restriction in restrictions_grouped_by_index.values():
154
- user_context.access_restrictions.filters.append( # pyright: ignore
155
- {
156
- "lookup": restriction["lookup"],
157
- "type": "selection",
158
- "key": restriction["key"],
159
- "value": restriction["value"],
160
- "match_exact": True,
161
- }
162
- )
112
+ result = handle_item_overview_request(schemas, filters)
113
+ if not isinstance(result, list):
114
+ return result
115
+ user_context.access_restrictions.filters.extend(result) # pyright: ignore
163
116
 
164
117
  if len(type_filter_values) == 0:
165
118
  return False
@@ -2,7 +2,11 @@ import re as regex
2
2
 
3
3
  from copy import deepcopy
4
4
  from elody.error_codes import ErrorCode, get_error_code, get_read
5
- from elody.policies.helpers import get_flat_item_and_object_lists, get_item
5
+ from elody.policies.helpers import (
6
+ generate_filter_key_and_lookup_from_restricted_key,
7
+ get_flat_item_and_object_lists,
8
+ get_item,
9
+ )
6
10
  from elody.util import flatten_dict, interpret_flat_key
7
11
  from flask import g
8
12
  from inuits_policy_based_auth.contexts.user_context import UserContext
@@ -51,6 +55,109 @@ def __replace_permission_placeholders(data, placeholder_key, placeholder_value):
51
55
  return data
52
56
 
53
57
 
58
+ def handle_item_overview_request(schemas, filters):
59
+ restrictions_grouped_by_index = __group_restrictions_by_index(schemas)
60
+ short_circuit = __short_circuit_item_overview_soft_call(
61
+ filters, restrictions_grouped_by_index
62
+ )
63
+ if short_circuit is not None:
64
+ return short_circuit
65
+ return __generate_restriction_filters(restrictions_grouped_by_index)
66
+
67
+
68
+ def __group_restrictions_by_index(schemas):
69
+ restrictions_grouped_by_index = {}
70
+ for schema in schemas.keys():
71
+ restrictions = schemas[schema].get("object_restrictions", {})
72
+ for restricted_key, restricting_value in restrictions.items():
73
+ index, restricted_key = restricted_key.split(":")
74
+ restricted_key, lookup = generate_filter_key_and_lookup_from_restricted_key(
75
+ restricted_key
76
+ )
77
+ key = f"{schema}|{restricted_key}"
78
+ if group := restrictions_grouped_by_index.get(index):
79
+ group["key"].append(key)
80
+ else:
81
+ restrictions_grouped_by_index.update(
82
+ {
83
+ index: {
84
+ "lookup": lookup,
85
+ "key": [key],
86
+ "value": restricting_value,
87
+ }
88
+ }
89
+ )
90
+ return restrictions_grouped_by_index
91
+
92
+
93
+ def __short_circuit_item_overview_soft_call(filters, restrictions_grouped_by_index):
94
+ for filter in filters:
95
+ key = filter.get("key", "")
96
+ if isinstance(key, list):
97
+ key = ",".join(key)
98
+ for restriction in restrictions_grouped_by_index.values():
99
+ if key not in ",".join(restriction["key"]):
100
+ continue
101
+ values = (
102
+ filter["value"]
103
+ if isinstance(filter["value"], list)
104
+ else [filter["value"]]
105
+ )
106
+ for value in values:
107
+ if value not in restriction["value"] and value not in ["", "*"]:
108
+ return False
109
+ return None
110
+
111
+
112
+ def __generate_restriction_filters(restrictions_grouped_by_index):
113
+ filters = []
114
+ for restriction in restrictions_grouped_by_index.values():
115
+ try:
116
+ combined_restrictions = [
117
+ value for value in restriction["value"] if isinstance(value, list)
118
+ ][0]
119
+ except IndexError:
120
+ combined_restrictions = []
121
+
122
+ filter = {
123
+ "lookup": restriction["lookup"],
124
+ "type": "selection",
125
+ "key": restriction["key"],
126
+ "value": [
127
+ value for value in restriction["value"] if not isinstance(value, list)
128
+ ],
129
+ "match_exact": True,
130
+ "or": [],
131
+ }
132
+
133
+ for combined_restriction in combined_restrictions:
134
+ combination = []
135
+ for value, combinations in combined_restriction.items():
136
+ combination.append(
137
+ {
138
+ "type": "selection",
139
+ "key": restriction["key"],
140
+ "value": [value],
141
+ "match_exact": True,
142
+ }
143
+ )
144
+ for key, value in combinations.items():
145
+ key = [f"{restriction['key'][0].split('|')[0]}|{key}"]
146
+ combination.append(
147
+ {
148
+ "lookup": restriction["lookup"],
149
+ "type": "selection",
150
+ "key": key,
151
+ "value": value,
152
+ "match_exact": True,
153
+ }
154
+ )
155
+ filter["or"].append(combination)
156
+
157
+ filters.append(filter)
158
+ return filters
159
+
160
+
54
161
  def handle_single_item_request(
55
162
  user_context: UserContext,
56
163
  item,
@@ -234,20 +341,24 @@ def __is_allowed_to_crud_item_keys(
234
341
  def __item_value_in_values(
235
342
  flat_item, key, values: list, flat_request_body, user_context: UserContext
236
343
  ):
344
+ if __matches_combined_expected_values(
345
+ flat_item, key, values, flat_request_body, user_context
346
+ ):
347
+ return True
348
+
237
349
  negate_condition = False
238
350
  is_optional = False
239
-
351
+ key_of_relation = None
240
352
  if key[0] == "!":
241
353
  key = key[1:]
242
354
  negate_condition = True
243
355
  if key[0] == "?":
244
356
  key = key[1:]
245
357
  is_optional = True
246
-
247
- key_of_relation = None
248
358
  if (keys := key.split("@", 1)) and len(keys) == 2:
249
359
  key = keys[0]
250
360
  key_of_relation = keys[1].split("-", 1)[1]
361
+
251
362
  try:
252
363
  item_value = flat_request_body.get(key, flat_item[key])
253
364
  if is_optional:
@@ -277,6 +388,34 @@ def __item_value_in_values(
277
388
  flat_item, key_of_relation, values, flat_request_body, user_context
278
389
  )
279
390
 
391
+ return __matches_expected_values(flat_item, item_value, values, negate_condition)
392
+
393
+
394
+ def __matches_combined_expected_values(
395
+ flat_item, key, values, flat_request_body, user_context
396
+ ):
397
+ values_deepcopy = deepcopy(values)
398
+ for value_from_values in values_deepcopy:
399
+ if not isinstance(value_from_values, list):
400
+ continue
401
+ for combined_restriction in value_from_values:
402
+ for value, combinations in combined_restriction.items():
403
+ if __item_value_in_values(
404
+ flat_item, key, [value], flat_request_body, user_context
405
+ ):
406
+ for combination_key, value in combinations.items():
407
+ if __item_value_in_values(
408
+ flat_item,
409
+ combination_key,
410
+ value,
411
+ flat_request_body,
412
+ user_context,
413
+ ):
414
+ return True
415
+ values.remove(value_from_values)
416
+
417
+
418
+ def __matches_expected_values(flat_item, item_value, values, negate_condition):
280
419
  expected_values = []
281
420
  for value in values:
282
421
  if flat_item_key_value := flat_item.get(value):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: elody
3
- Version: 0.0.226
3
+ Version: 0.0.227
4
4
  Summary: elody SDK for Python
5
5
  Author-email: Inuits <developers@inuits.eu>
6
6
  License: GNU GENERAL PUBLIC LICENSE
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes