forked from openedx/openedx-authz
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
183 lines (144 loc) · 5.62 KB
/
utils.py
File metadata and controls
183 lines (144 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
"""Utility functions for the Open edX AuthZ REST API."""
from openedx_authz.api.data import (
GLOBAL_SCOPE_WILDCARD,
ScopeData,
)
from openedx_authz.rest_api.data import (
AssignmentSortField,
BaseEnum,
SearchField,
SortField,
SortOrder,
UserAssignmentSortField,
)
def get_generic_scope(scope: ScopeData) -> ScopeData:
"""
Create a generic scope from a given scope by replacing its key with a wildcard.
This function preserves the namespace of the original scope but replaces the specific
key with a wildcard, allowing for broader permission checks across all scopes within
the same namespace.
Args:
scope (ScopeData): The specific scope to generalize.
Returns:
ScopeData: A new scope with the same namespace but a wildcard key.
Examples:
>>> scope = ScopeData(namespaced_key="lib^lib:DemoX:CSPROB")
>>> get_generic_scope(scope)
ScopeData(namespaced_key="lib^*")
"""
return ScopeData(namespaced_key=f"{scope.NAMESPACE}{ScopeData.SEPARATOR}{GLOBAL_SCOPE_WILDCARD}")
def sort_users(
users: list[dict],
sort_by: SortField = SortField.USERNAME,
order: SortOrder = SortOrder.ASC,
) -> list[dict]:
"""
Sort users by a given field and order.
Args:
users (list[dict]): The users to sort.
sort_by (SortField, optional): The field to sort by. Defaults to SortField.USERNAME.
order (SortOrder, optional): The order to sort by. Defaults to SortOrder.ASC.
Raises:
ValueError: If the sort field is invalid.
ValueError: If the sort order is invalid.
Returns:
list[dict]: The sorted users.
"""
if sort_by not in SortField.values():
raise ValueError(f"Invalid field: '{sort_by}'. Must be one of {SortField.values()}")
if order not in SortOrder.values():
raise ValueError(f"Invalid order: '{order}'. Must be one of {SortOrder.values()}")
sorted_users = sorted(
users,
key=lambda user: (user.get(sort_by) or "").lower(),
reverse=order == SortOrder.DESC,
)
return sorted_users
def filter_users(users: list[dict], search: str | None, roles: list[str] | None) -> list[dict]:
"""
Filter users by a case-insensitive search string and/or by roles.
Args:
users (list[dict]): The users to filter.
search (str | None): Optional search term matched against fields in ``SearchField``.
roles (list[str] | None): Optional list of roles; include users that have any of these roles.
Returns:
list[dict]: The filtered users, preserving the original order.
"""
if not search and not roles:
return users
filtered_users = []
for user in users:
if search:
matches_search = any(search in (user.get(field) or "").lower() for field in SearchField.values())
if not matches_search:
continue
if roles:
matches_role = any(role in user.get("roles", []) for role in roles)
if not matches_role:
continue
filtered_users.append(user)
return filtered_users
def _sort_by_field(
items: list[dict],
sort_by: str,
order: str,
allowed_fields: type[BaseEnum],
) -> list[dict]:
"""
Sort a list of dicts by a given field and order, validating against the provided enum.
Args:
items (list[dict]): The items to sort.
sort_by (str): The field to sort by.
order (str): The order to sort by.
allowed_fields (type[BaseEnum]): The enum class whose values are the valid sort fields.
Raises:
ValueError: If the sort field is invalid.
ValueError: If the sort order is invalid.
Returns:
list[dict]: The sorted items.
"""
if sort_by not in allowed_fields.values():
raise ValueError(f"Invalid field: '{sort_by}'. Must be one of {allowed_fields.values()}")
if order not in SortOrder.values():
raise ValueError(f"Invalid order: '{order}'. Must be one of {SortOrder.values()}")
return sorted(
items,
key=lambda item: (item.get(sort_by) or "").lower(),
reverse=order == SortOrder.DESC,
)
def sort_assignments(
assignments: list[dict],
sort_by: AssignmentSortField = AssignmentSortField.ROLE,
order: SortOrder = SortOrder.ASC,
) -> list[dict]:
"""
Sort role assignments by a given field and order.
Args:
assignments (list[dict]): The assignments to sort.
sort_by (AssignmentSortField, optional): The field to sort by. Defaults to AssignmentSortField.ROLE.
order (SortOrder, optional): The order to sort by. Defaults to SortOrder.ASC.
Raises:
ValueError: If the sort field is invalid.
ValueError: If the sort order is invalid.
Returns:
list[dict]: The sorted assignments.
"""
return _sort_by_field(assignments, sort_by, order, AssignmentSortField)
def sort_user_assignments(
assignments: list[dict],
sort_by: UserAssignmentSortField = UserAssignmentSortField.ROLE,
order: SortOrder = SortOrder.ASC,
) -> list[dict]:
"""
Sort role assignments by a given field and order.
Args:
assignments (list[dict]): The assignments to sort.
sort_by (UserAssignmentSortField, optional): The field to sort by. Defaults to UserAssignmentSortField.ROLE.
order (SortOrder, optional): The order to sort by. Defaults to SortOrder.ASC.
Raises:
ValueError: If the sort field is invalid.
ValueError: If the sort order is invalid.
Returns:
list[dict]: The sorted assignments.
"""
return _sort_by_field(assignments, sort_by, order, UserAssignmentSortField)