ckanext-permissions 0.2.0__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.
- ckanext/__init__.py +9 -0
- ckanext/permissions/__init__.py +0 -0
- ckanext/permissions/cli.py +80 -0
- ckanext/permissions/const.py +10 -0
- ckanext/permissions/helpers.py +51 -0
- ckanext/permissions/implementation/__init__.py +5 -0
- ckanext/permissions/implementation/permission_labels.py +45 -0
- ckanext/permissions/logic/__init__.py +0 -0
- ckanext/permissions/logic/action.py +113 -0
- ckanext/permissions/logic/auth.py +82 -0
- ckanext/permissions/logic/schema.py +54 -0
- ckanext/permissions/logic/validators.py +112 -0
- ckanext/permissions/migration/permissions/alembic.ini +74 -0
- ckanext/permissions/migration/permissions/env.py +85 -0
- ckanext/permissions/migration/permissions/script.py.mako +24 -0
- ckanext/permissions/migration/permissions/versions/a849104ccfdc_init_tables.py +69 -0
- ckanext/permissions/model.py +148 -0
- ckanext/permissions/plugin.py +67 -0
- ckanext/permissions/tests/__init__.py +0 -0
- ckanext/permissions/tests/actions/test_permission.py +68 -0
- ckanext/permissions/tests/actions/test_role.py +110 -0
- ckanext/permissions/tests/conftest.py +52 -0
- ckanext/permissions/tests/test_autoassign.py +26 -0
- ckanext/permissions/tests/test_helpers.py +67 -0
- ckanext/permissions/tests/test_permission_labels.py +28 -0
- ckanext/permissions/tests/test_utils.py +275 -0
- ckanext/permissions/tests/test_validators.py +98 -0
- ckanext/permissions/types.py +34 -0
- ckanext/permissions/utils.py +197 -0
- ckanext/permissions_manager/__init__.py +0 -0
- ckanext/permissions_manager/helpers.py +23 -0
- ckanext/permissions_manager/plugin.py +48 -0
- ckanext/permissions_manager/views.py +351 -0
- ckanext_permissions-0.2.0.dist-info/METADATA +104 -0
- ckanext_permissions-0.2.0.dist-info/RECORD +39 -0
- ckanext_permissions-0.2.0.dist-info/WHEEL +5 -0
- ckanext_permissions-0.2.0.dist-info/entry_points.txt +6 -0
- ckanext_permissions-0.2.0.dist-info/licenses/LICENSE +661 -0
- ckanext_permissions-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from ckan.lib import munge
|
|
2
|
+
|
|
3
|
+
from ckanext.permissions.utils import get_registered_roles
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def permission_munge_string(value: str) -> str:
|
|
7
|
+
"""Munge a string using CKAN's munge_name function.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
value: The string to munge
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
The munged string
|
|
14
|
+
"""
|
|
15
|
+
return munge.munge_name(value)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def permission_get_registered_roles_options() -> list[dict[str, str]]:
|
|
19
|
+
return [
|
|
20
|
+
{"value": role_id, "text": role_label}
|
|
21
|
+
for role_id, role_label in get_registered_roles().items()
|
|
22
|
+
if role_id not in ("administrator", "anonymous")
|
|
23
|
+
]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ckan.plugins as p
|
|
4
|
+
import ckan.plugins.toolkit as tk
|
|
5
|
+
import ckan.types as types
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@tk.blanket.blueprints
|
|
9
|
+
@tk.blanket.helpers
|
|
10
|
+
class PermissionsManagerPlugin(p.SingletonPlugin):
|
|
11
|
+
p.implements(p.IConfigurer)
|
|
12
|
+
p.implements(p.ISignal)
|
|
13
|
+
|
|
14
|
+
# IConfigurer
|
|
15
|
+
|
|
16
|
+
def update_config(self, config_):
|
|
17
|
+
tk.add_template_directory(config_, "templates")
|
|
18
|
+
tk.add_public_directory(config_, "public")
|
|
19
|
+
tk.add_resource("assets", "permissions_manager")
|
|
20
|
+
|
|
21
|
+
# ISignal
|
|
22
|
+
|
|
23
|
+
def get_signal_subscriptions(self) -> types.SignalMapping:
|
|
24
|
+
return {
|
|
25
|
+
tk.signals.ckanext.signal("ap_main:collect_config_sections"): [self.collect_config_sections_subs],
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def collect_config_sections_subs(sender: None):
|
|
30
|
+
return {
|
|
31
|
+
"name": "Permissions",
|
|
32
|
+
"configs": [
|
|
33
|
+
{
|
|
34
|
+
"name": "Permissions manager",
|
|
35
|
+
"blueprint": "perm_manager.permission_list",
|
|
36
|
+
"info": "Manage portal permissions",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "Roles manager",
|
|
40
|
+
"blueprint": "perm_manager.role_list",
|
|
41
|
+
"info": "Manage portal roles",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "User roles list",
|
|
45
|
+
"blueprint": "perm_manager.user_roles_list",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Union
|
|
5
|
+
|
|
6
|
+
from flask import Blueprint, Response
|
|
7
|
+
from flask.views import MethodView
|
|
8
|
+
|
|
9
|
+
import ckan.model as model
|
|
10
|
+
import ckan.plugins.toolkit as tk
|
|
11
|
+
import ckan.types as types
|
|
12
|
+
from ckan.lib.helpers import Page
|
|
13
|
+
|
|
14
|
+
from ckanext.permissions import model as perm_model
|
|
15
|
+
from ckanext.permissions import utils
|
|
16
|
+
|
|
17
|
+
log = logging.getLogger(__name__)
|
|
18
|
+
perm_manager = Blueprint("perm_manager", __name__, url_prefix="/permissions")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
USER_ROLES_PER_PAGE = 10
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@perm_manager.before_request
|
|
25
|
+
def before_request() -> None:
|
|
26
|
+
try:
|
|
27
|
+
tk.check_access("sysadmin", {"user": tk.current_user.name})
|
|
28
|
+
except tk.NotAuthorized:
|
|
29
|
+
tk.abort(403, tk._("Need to be system administrator to administer"))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PermissionManagerView(MethodView):
|
|
33
|
+
def get(self) -> Union[str, Response]:
|
|
34
|
+
return tk.render(
|
|
35
|
+
"perm_manager/list.html",
|
|
36
|
+
extra_vars={
|
|
37
|
+
"permission_groups": utils.get_permission_groups(),
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def post(self) -> Response:
|
|
42
|
+
try:
|
|
43
|
+
tk.get_action("permissions_update")({}, {"permissions": self._get_permissions()})
|
|
44
|
+
except tk.ValidationError as e:
|
|
45
|
+
tk.h.flash_error(str(e))
|
|
46
|
+
return tk.redirect_to("perm_manager.permission_list")
|
|
47
|
+
|
|
48
|
+
tk.h.flash_success("Permissions updated")
|
|
49
|
+
|
|
50
|
+
return tk.redirect_to("perm_manager.permission_list")
|
|
51
|
+
|
|
52
|
+
def _get_permissions(self) -> dict[str, dict[str, bool]]:
|
|
53
|
+
permissions = {}
|
|
54
|
+
|
|
55
|
+
for key in tk.request.form.keys():
|
|
56
|
+
if "|" not in key:
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
values = tk.request.form.getlist(key)
|
|
60
|
+
permission, role_id = key.split("|")
|
|
61
|
+
|
|
62
|
+
if permission not in permissions:
|
|
63
|
+
permissions[permission] = {}
|
|
64
|
+
|
|
65
|
+
permissions[permission][role_id] = "set" in values
|
|
66
|
+
|
|
67
|
+
return permissions
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class RoleManagerView(MethodView):
|
|
71
|
+
def get(self) -> Union[str, Response]:
|
|
72
|
+
return tk.render(
|
|
73
|
+
"perm_manager/role_list.html",
|
|
74
|
+
extra_vars={
|
|
75
|
+
"roles": sorted(perm_model.Role.all(), key=lambda x: x["label"]),
|
|
76
|
+
},
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class RoleAdd(MethodView):
|
|
81
|
+
def get(self) -> Union[str, Response]:
|
|
82
|
+
return tk.render(
|
|
83
|
+
"perm_manager/add_role.html",
|
|
84
|
+
extra_vars={"errors": {}, "data": {}},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def post(self) -> Union[str, Response]:
|
|
88
|
+
payload = dict(tk.request.form)
|
|
89
|
+
|
|
90
|
+
tk.get_or_bust(payload, ["id", "label", "description"])
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
tk.get_action("permission_role_create")({}, payload)
|
|
94
|
+
except tk.NotAuthorized as e:
|
|
95
|
+
return tk.abort(403, str(e))
|
|
96
|
+
except tk.ValidationError as e:
|
|
97
|
+
return tk.render(
|
|
98
|
+
"perm_manager/add_role.html",
|
|
99
|
+
extra_vars={"errors": e.error_dict, "data": payload},
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
tk.h.flash_success("Role has been created")
|
|
103
|
+
|
|
104
|
+
return tk.redirect_to("perm_manager.role_list")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class RoleDelete(MethodView):
|
|
108
|
+
def post(self) -> Response:
|
|
109
|
+
payload = dict(tk.request.form)
|
|
110
|
+
|
|
111
|
+
tk.get_or_bust(payload, ["id"])
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
tk.get_action("permission_role_delete")({}, payload)
|
|
115
|
+
except tk.ValidationError as e:
|
|
116
|
+
tk.h.flash_error(str(e))
|
|
117
|
+
else:
|
|
118
|
+
tk.h.flash_success("Role has been deleted")
|
|
119
|
+
|
|
120
|
+
return tk.redirect_to("perm_manager.role_list")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class RoleEdit(MethodView):
|
|
124
|
+
def get(self, role_id: str) -> Union[str, Response]:
|
|
125
|
+
return tk.render(
|
|
126
|
+
"perm_manager/edit_role.html",
|
|
127
|
+
extra_vars={"role": perm_model.Role.get(role_id), "errors": {}, "data": {}},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def post(self, role_id: str) -> Union[str, Response]:
|
|
131
|
+
payload = dict(tk.request.form)
|
|
132
|
+
|
|
133
|
+
tk.get_or_bust(payload, "description")
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
tk.get_action("permission_role_update")(
|
|
137
|
+
{},
|
|
138
|
+
{
|
|
139
|
+
"id": role_id,
|
|
140
|
+
"description": payload["description"],
|
|
141
|
+
},
|
|
142
|
+
)
|
|
143
|
+
except tk.ValidationError as e:
|
|
144
|
+
return tk.render(
|
|
145
|
+
"perm_manager/edit_role.html",
|
|
146
|
+
extra_vars={
|
|
147
|
+
"role": perm_model.Role.get(role_id),
|
|
148
|
+
"errors": e.error_dict,
|
|
149
|
+
"data": payload,
|
|
150
|
+
},
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
tk.h.flash_success("Role has been updated")
|
|
154
|
+
|
|
155
|
+
return tk.redirect_to("perm_manager.role_list")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class BaseUserRolesList(MethodView):
|
|
159
|
+
def _get_user_with_roles(self, scope: str = "global", scope_id: str | None = None) -> list[dict[str, Any]]:
|
|
160
|
+
"""
|
|
161
|
+
Get all active users and their roles.
|
|
162
|
+
"""
|
|
163
|
+
users = self._get_active_users()
|
|
164
|
+
result: list[dict[str, Any]] = []
|
|
165
|
+
|
|
166
|
+
for user in users:
|
|
167
|
+
result.append(
|
|
168
|
+
{
|
|
169
|
+
"id": user.id,
|
|
170
|
+
"display_name": user.display_name,
|
|
171
|
+
"roles": tk.h.get_user_roles(user.id, scope, scope_id),
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return self._apply_filters(result)
|
|
176
|
+
|
|
177
|
+
def _get_active_users(self) -> list[model.User]:
|
|
178
|
+
"""
|
|
179
|
+
Get all active users and sort them by display name.
|
|
180
|
+
"""
|
|
181
|
+
return sorted(
|
|
182
|
+
(model.Session.query(model.User).filter(model.User.state == model.State.ACTIVE).all()),
|
|
183
|
+
key=lambda x: x.display_name,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
def _apply_filters(self, users: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
187
|
+
"""
|
|
188
|
+
Apply filters based on username and role.
|
|
189
|
+
"""
|
|
190
|
+
q = tk.request.args.get("q", "").strip()
|
|
191
|
+
role_filter = tk.request.args.get("role", "").strip()
|
|
192
|
+
|
|
193
|
+
if q:
|
|
194
|
+
users = [user for user in users if q.lower() in user["display_name"].lower() or q.lower() in user["roles"]]
|
|
195
|
+
|
|
196
|
+
if role_filter:
|
|
197
|
+
users = [user for user in users if role_filter in user["roles"]]
|
|
198
|
+
|
|
199
|
+
return users
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class UserRolesList(BaseUserRolesList):
|
|
203
|
+
def get(self) -> Union[str, Response]:
|
|
204
|
+
users = self._get_user_with_roles()
|
|
205
|
+
page = Page(
|
|
206
|
+
collection=users,
|
|
207
|
+
page=tk.h.get_page_number(tk.request.args),
|
|
208
|
+
url=tk.h.pager_url,
|
|
209
|
+
item_count=len(users),
|
|
210
|
+
items_per_page=int(tk.request.args.get("limit", USER_ROLES_PER_PAGE)),
|
|
211
|
+
)
|
|
212
|
+
return tk.render("perm_manager/user_roles_list.html", extra_vars={"page": page})
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class OrganizationUserRolesList(BaseUserRolesList):
|
|
216
|
+
def get(self, org_id: str) -> Union[str, Response]:
|
|
217
|
+
users = self._get_user_with_roles(scope="organization", scope_id=org_id)
|
|
218
|
+
org_dict = _get_org_dict(org_id)
|
|
219
|
+
|
|
220
|
+
def _pager_url(**kwargs):
|
|
221
|
+
return tk.h.url_for("perm_manager.organization_user_roles_list", org_id=org_id, **kwargs)
|
|
222
|
+
|
|
223
|
+
page = Page(
|
|
224
|
+
collection=users,
|
|
225
|
+
page=tk.h.get_page_number(tk.request.args),
|
|
226
|
+
url=_pager_url,
|
|
227
|
+
item_count=len(users),
|
|
228
|
+
items_per_page=int(tk.request.args.get("limit", USER_ROLES_PER_PAGE)),
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
return tk.render(
|
|
232
|
+
"perm_manager/organization/user_roles_list.html",
|
|
233
|
+
extra_vars={
|
|
234
|
+
"group_dict": org_dict,
|
|
235
|
+
"group_type": "organization",
|
|
236
|
+
"page": page,
|
|
237
|
+
},
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class EditUserRole(MethodView):
|
|
242
|
+
def __init__(self):
|
|
243
|
+
self.schema = {
|
|
244
|
+
"roles": [tk.get_validator(validator) for validator in "not_missing list_of_strings roles_exists".split()]
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
def get(self, user_id: str) -> Union[str, Response]:
|
|
248
|
+
user = model.User.get(user_id)
|
|
249
|
+
|
|
250
|
+
if not user:
|
|
251
|
+
return tk.abort(404, "User not found")
|
|
252
|
+
|
|
253
|
+
return tk.render(
|
|
254
|
+
"perm_manager/edit_user_roles.html",
|
|
255
|
+
extra_vars={
|
|
256
|
+
"user": user,
|
|
257
|
+
"data": {"roles": ",".join(tk.h.get_user_roles(user.id))},
|
|
258
|
+
"errors": {},
|
|
259
|
+
},
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
def post(self, user_id: str) -> Union[str, Response]:
|
|
263
|
+
return self._update_user_roles(user_id, "global")
|
|
264
|
+
|
|
265
|
+
def _update_user_roles(self, user_id: str, scope: str, scope_id: str | None = None) -> Union[str, Response]:
|
|
266
|
+
payload = {"roles": tk.request.form.getlist("roles")}
|
|
267
|
+
|
|
268
|
+
user = model.User.get(user_id)
|
|
269
|
+
|
|
270
|
+
if not user:
|
|
271
|
+
tk.abort(404, "User not found")
|
|
272
|
+
|
|
273
|
+
data, errors = tk.navl_validate(payload, self.schema)
|
|
274
|
+
|
|
275
|
+
if errors:
|
|
276
|
+
return tk.render(
|
|
277
|
+
"perm_manager/edit_user_roles.html",
|
|
278
|
+
extra_vars={"user": user, "data": data, "errors": errors},
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
perm_model.UserRole.clear_user_roles(user.id, scope, scope_id)
|
|
282
|
+
|
|
283
|
+
for role in data["roles"]:
|
|
284
|
+
perm_model.UserRole.create(user_id=user.id, role=role, scope=scope, scope_id=scope_id)
|
|
285
|
+
|
|
286
|
+
model.Session.commit()
|
|
287
|
+
|
|
288
|
+
tk.h.flash_success("User roles updated")
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
tk.redirect_to("perm_manager.user_roles_list")
|
|
292
|
+
if scope == "global"
|
|
293
|
+
else tk.redirect_to("perm_manager.organization_user_roles_list", org_id=scope_id)
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class OrganizationEditUserRole(EditUserRole):
|
|
298
|
+
def get(self, org_id: str, user_id: str) -> Union[str, Response]:
|
|
299
|
+
user = model.User.get(user_id)
|
|
300
|
+
|
|
301
|
+
if not user:
|
|
302
|
+
return tk.abort(404, "User not found")
|
|
303
|
+
|
|
304
|
+
org_dict = _get_org_dict(org_id)
|
|
305
|
+
scope = "organization"
|
|
306
|
+
|
|
307
|
+
return tk.render(
|
|
308
|
+
"perm_manager/organization/edit_user_roles.html",
|
|
309
|
+
extra_vars={
|
|
310
|
+
"user": user,
|
|
311
|
+
"data": {"roles": ",".join(tk.h.get_user_roles(user.id, scope, org_dict["id"]))},
|
|
312
|
+
"errors": {},
|
|
313
|
+
"group_dict": org_dict,
|
|
314
|
+
"group_type": scope,
|
|
315
|
+
},
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
def post(self, org_id: str, user_id: str) -> Union[str, Response]:
|
|
319
|
+
return self._update_user_roles(user_id, "organization", org_id)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _get_org_dict(org_id: str) -> dict[str, Any]:
|
|
323
|
+
context = types.Context(user=tk.current_user.name, for_view=True)
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
return tk.get_action("organization_show")(context, {"id": org_id, "include_datasets": False})
|
|
327
|
+
except (tk.ObjectNotFound, tk.NotAuthorized):
|
|
328
|
+
tk.abort(404, tk._("Organization not found"))
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
perm_manager.add_url_rule("/manage", view_func=PermissionManagerView.as_view("permission_list"))
|
|
332
|
+
|
|
333
|
+
perm_manager.add_url_rule("/roles", view_func=RoleManagerView.as_view("role_list"))
|
|
334
|
+
perm_manager.add_url_rule("/roles/add", view_func=RoleAdd.as_view("role_add"))
|
|
335
|
+
perm_manager.add_url_rule("/roles/delete", view_func=RoleDelete.as_view("role_delete"))
|
|
336
|
+
perm_manager.add_url_rule("/roles/<role_id>", view_func=RoleEdit.as_view("role_edit"))
|
|
337
|
+
|
|
338
|
+
# organization user roles
|
|
339
|
+
perm_manager.add_url_rule(
|
|
340
|
+
"/organization/user-roles/<org_id>",
|
|
341
|
+
view_func=OrganizationUserRolesList.as_view("organization_user_roles_list"),
|
|
342
|
+
)
|
|
343
|
+
perm_manager.add_url_rule(
|
|
344
|
+
"/organization/user-roles/<org_id>/<user_id>",
|
|
345
|
+
view_func=OrganizationEditUserRole.as_view("organization_edit_user_role"),
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
perm_manager.add_url_rule("/user-roles", view_func=UserRolesList.as_view("user_roles_list"))
|
|
349
|
+
perm_manager.add_url_rule("/user-roles/<user_id>", view_func=EditUserRole.as_view("edit_user_role"))
|
|
350
|
+
|
|
351
|
+
blueprints = [perm_manager]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ckanext-permissions
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Permission system for CKAN
|
|
5
|
+
Author-email: DataShades <datashades@linkdigital.com.au>, Oleksandr Cherniavskyi <mutantsan@gmail.com>
|
|
6
|
+
Maintainer-email: DataShades <datashades@linkdigital.com.au>
|
|
7
|
+
License: AGPL
|
|
8
|
+
Project-URL: Homepage, https://github.com/DataShades/ckanext-permissions
|
|
9
|
+
Keywords: CKAN
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest-ckan; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-toolbelt; extra == "dev"
|
|
20
|
+
Requires-Dist: ckanext-toolbelt; extra == "dev"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
[](https://github.com/DataShades/ckanext-permissions/actions/workflows/test.yml)
|
|
24
|
+
|
|
25
|
+
# ckanext-permissions
|
|
26
|
+
|
|
27
|
+
> [!WARNING]
|
|
28
|
+
> This extension is still under development and not ready for production use.
|
|
29
|
+
|
|
30
|
+
The extension allows you to build a Access Control List (ACL) system within CKAN.
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Roles
|
|
36
|
+
|
|
37
|
+
The extension has a 3 default roles: `anonymous`, `authenticated` and `administrator`. And allows you to define custom roles.
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
### Assigning roles to users
|
|
42
|
+
|
|
43
|
+
The extension provides a way to assign roles to users. Roles could be global and scoped to an organization.
|
|
44
|
+
|
|
45
|
+

|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Requirements
|
|
49
|
+
|
|
50
|
+
Compatibility with core CKAN versions:
|
|
51
|
+
|
|
52
|
+
| CKAN version | Compatible? |
|
|
53
|
+
| --------------- | ------------- |
|
|
54
|
+
| 2.9 and earlier | no |
|
|
55
|
+
| 2.10+ | yes |
|
|
56
|
+
| 2.11+ | yes |
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
Using GIT Clone:
|
|
62
|
+
|
|
63
|
+
1. Activate your CKAN virtual environment, for example:
|
|
64
|
+
|
|
65
|
+
. /usr/lib/ckan/default/bin/activate
|
|
66
|
+
|
|
67
|
+
2. Clone the source and install it on the virtualenv
|
|
68
|
+
|
|
69
|
+
git clone https://github.com/DataShades/ckanext-permissions.git
|
|
70
|
+
|
|
71
|
+
cd ckanext-permissions
|
|
72
|
+
|
|
73
|
+
pip install -e .
|
|
74
|
+
|
|
75
|
+
3. Add `permissions permissions_manager` to the `ckan.plugins` setting in your CKAN
|
|
76
|
+
config file (by default the config file is located at
|
|
77
|
+
`/etc/ckan/default/ckan.ini`).
|
|
78
|
+
|
|
79
|
+
4. Initialize DB tables:
|
|
80
|
+
|
|
81
|
+
ckan -c PATH_TO_CONFIG db upgrade
|
|
82
|
+
|
|
83
|
+
5. Initialize default Roles and add Authenticated default role to all existing Users:
|
|
84
|
+
|
|
85
|
+
ckan -c PATH_TO_CONFIG permissions assign-default-user-roles
|
|
86
|
+
|
|
87
|
+
7. Restart CKAN.
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
## Config settings
|
|
91
|
+
|
|
92
|
+
TBD
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
## Tests
|
|
96
|
+
|
|
97
|
+
To run the tests, do:
|
|
98
|
+
|
|
99
|
+
pytest --ckan-ini=test.ini --cov=ckanext.permissions
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
[AGPL](https://www.gnu.org/licenses/agpl-3.0.en.html)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
ckanext/__init__.py,sha256=bA4GtkniRdq2--6cjASDLRM4gCsX_ysGJkaTMSmIQY8,202
|
|
2
|
+
ckanext/permissions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
ckanext/permissions/cli.py,sha256=A48-pfK2hhLWT2MrOMJwm0NNNe43GGLsbbsXq-8B9sw,2439
|
|
4
|
+
ckanext/permissions/const.py,sha256=KwP1mrgb_SE_n1aEj7pdxC5jxe5rWIxPJPZSSZeJy88,191
|
|
5
|
+
ckanext/permissions/helpers.py,sha256=rIRSFGLT-B_p1bOYPco2FoS_5eEBUnvOrmDrByPr0ck,1334
|
|
6
|
+
ckanext/permissions/model.py,sha256=6VkUklXcte_WsSCPeMhPdBJIXrP_g00NK1X5TyMHHK0,4323
|
|
7
|
+
ckanext/permissions/plugin.py,sha256=hMWhrwpED0hwFFl0lR0vxb5k9w3Qseye3RCqDqK64oA,2098
|
|
8
|
+
ckanext/permissions/types.py,sha256=9MM5GJgnWbW8YZ1xOw-YQsVzGI05hlCUUtVd8O1vcQU,598
|
|
9
|
+
ckanext/permissions/utils.py,sha256=FUOzW7EO5AEJNmZuQ1IdK6dQSSbvhEItaVsyHti4VGo,5300
|
|
10
|
+
ckanext/permissions/implementation/__init__.py,sha256=tJ9Gz2mK8mBPXYmEwwQL8xxtCs491x5H-h8X-ZkWUZg,87
|
|
11
|
+
ckanext/permissions/implementation/permission_labels.py,sha256=mwtjW8HJFXbHeTFcxQ0QsOWqjCLu6Hkw6N56mg5IZAc,1524
|
|
12
|
+
ckanext/permissions/logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
ckanext/permissions/logic/action.py,sha256=eKaq7PQT6t03BFWOatoQPdAvkNKZFBKXjx8EvNv2PEI,3598
|
|
14
|
+
ckanext/permissions/logic/auth.py,sha256=0KQjIcxXbhzG9oZadWUF7YVl5n5TE6YnMCwL5ecKTDc,2452
|
|
15
|
+
ckanext/permissions/logic/schema.py,sha256=u7zIjeMT60Hjcm7_x8Ch6Qjyl7aK7buUE0wqOJmf_wo,1608
|
|
16
|
+
ckanext/permissions/logic/validators.py,sha256=3AQ6t-yp9bcpxLlikDhCgInwpNIKSZ_pu1EnK6Qc-Ok,2498
|
|
17
|
+
ckanext/permissions/migration/permissions/alembic.ini,sha256=TjMs29GvgS6oUMdLfXbw1onzwkRBQ9pSft78UgAAUUE,1812
|
|
18
|
+
ckanext/permissions/migration/permissions/env.py,sha256=LQT2UgozCI1O1gRtHRC9BSk9K3ks8wQxTW_j1pQ65Yw,2228
|
|
19
|
+
ckanext/permissions/migration/permissions/script.py.mako,sha256=8_xgA-gm_OhehnO7CiIijWgnm00ZlszEHtIHrAYFJl0,494
|
|
20
|
+
ckanext/permissions/migration/permissions/versions/a849104ccfdc_init_tables.py,sha256=T8TqpsPELtCRgAZp66SM-z8BnkpJbdAolh-mUaGThco,1585
|
|
21
|
+
ckanext/permissions/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
ckanext/permissions/tests/conftest.py,sha256=S8W4U9VYcOi0RIDw9bctMAjFbmdxnoIuqBfsRYAkd-w,1113
|
|
23
|
+
ckanext/permissions/tests/test_autoassign.py,sha256=U_ZA4hTaERS6uYyvVq6_aMYwh72uXW5geBM-oYuEWoM,900
|
|
24
|
+
ckanext/permissions/tests/test_helpers.py,sha256=vIvoF0Jfy4hE9QaM7tllD0B6iGXR7HpT3tD8Rpl-CPo,2405
|
|
25
|
+
ckanext/permissions/tests/test_permission_labels.py,sha256=CV42TMQwjB5BiSnosD2m455vZCyD60qeGezB4Nc-IAk,914
|
|
26
|
+
ckanext/permissions/tests/test_utils.py,sha256=V7mcswMW-Jhv26-dDcx92GJvhEXJLGFUnDUDTzggSk0,9220
|
|
27
|
+
ckanext/permissions/tests/test_validators.py,sha256=hlhLWyosSi-vY-nWDcMvNDEXaaBZPHRuyjQhvoLZ8so,3499
|
|
28
|
+
ckanext/permissions/tests/actions/test_permission.py,sha256=sqTvF3nbTNAZm1fzmsgUg7udQT_X19PEFcTbgjpbEuw,2540
|
|
29
|
+
ckanext/permissions/tests/actions/test_role.py,sha256=RaZxAQ63pNJel0GtO2fnn1MbeaYW809g0PHQ7cs6ANI,3729
|
|
30
|
+
ckanext/permissions_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
ckanext/permissions_manager/helpers.py,sha256=e_DMaAohk_bV-Z1WOsgsk19m8VGcbRiY_XDo_H4MGxY,584
|
|
32
|
+
ckanext/permissions_manager/plugin.py,sha256=ssESyXDbK43p6nINArBGNXWUvxxY9dSFmYk3a2WjhO0,1441
|
|
33
|
+
ckanext/permissions_manager/views.py,sha256=C_nL7nTJpW8NQoWpIBVZqV6sbyRasCGE2aUcEIHfzL0,11270
|
|
34
|
+
ckanext_permissions-0.2.0.dist-info/licenses/LICENSE,sha256=2lWcRAHjsQhqavGNnR30Ymxq3GJ9BaYL_dnfGO_-WFA,34500
|
|
35
|
+
ckanext_permissions-0.2.0.dist-info/METADATA,sha256=dvT8tyEhY4RPdwQzbDSwmPZzjYg0ejLacvly0VOBSsY,2801
|
|
36
|
+
ckanext_permissions-0.2.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
37
|
+
ckanext_permissions-0.2.0.dist-info/entry_points.txt,sha256=y3oO5e5vSFvReRU-bvsf6TWlKFGEM2XoMqxdjoJmTx0,213
|
|
38
|
+
ckanext_permissions-0.2.0.dist-info/top_level.txt,sha256=5yjNwq-s42weaiMMUuA5lZ45g99ANsfcRBCvac1JMS4,8
|
|
39
|
+
ckanext_permissions-0.2.0.dist-info/RECORD,,
|