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
ckanext/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
import ckan.model as model
|
|
4
|
+
|
|
5
|
+
import ckanext.permissions.const as perm_const
|
|
6
|
+
from ckanext.permissions import model as perm_model
|
|
7
|
+
from ckanext.permissions import utils
|
|
8
|
+
|
|
9
|
+
__all__ = ["permissions"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
def permissions():
|
|
14
|
+
"""Permissions management commands."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@permissions.command()
|
|
18
|
+
def init_default_roles():
|
|
19
|
+
"""Create default roles (anonymous, authenticated, administrator) in the database"""
|
|
20
|
+
created_count = utils.ensure_default_roles()
|
|
21
|
+
|
|
22
|
+
if created_count > 0:
|
|
23
|
+
click.secho(f"{created_count} role(s) created successfully", fg="green")
|
|
24
|
+
else:
|
|
25
|
+
click.secho("All default roles already exist", fg="yellow")
|
|
26
|
+
|
|
27
|
+
return created_count
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@permissions.command()
|
|
31
|
+
@click.argument("role", default=perm_const.Roles.Authenticated.value, required=False)
|
|
32
|
+
def assign_default_user_roles(role: str):
|
|
33
|
+
"""Assign automatic roles to users (initializes default roles if needed)"""
|
|
34
|
+
# Ensure default roles exist
|
|
35
|
+
click.echo("Checking default roles...")
|
|
36
|
+
created_count = utils.ensure_default_roles()
|
|
37
|
+
|
|
38
|
+
if created_count > 0:
|
|
39
|
+
click.secho(f"{created_count} role(s) created", fg="green")
|
|
40
|
+
click.echo() # Empty line for readability
|
|
41
|
+
|
|
42
|
+
# Check if the specified role exists
|
|
43
|
+
if not perm_model.Role.get(role):
|
|
44
|
+
click.secho(f"Error: Role '{role}' does not exist", fg="red")
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
users = model.Session.query(model.User).filter(model.User.state == "active").all()
|
|
48
|
+
|
|
49
|
+
for user in users:
|
|
50
|
+
utils.assign_role_to_user(user.id, role)
|
|
51
|
+
|
|
52
|
+
click.secho(f"Role '{role}' assigned to {len(users)} active user(s)", fg="green")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@permissions.command()
|
|
56
|
+
@click.argument("role", default=perm_const.Roles.Authenticated.value, required=False)
|
|
57
|
+
@click.option(
|
|
58
|
+
"--user-ids",
|
|
59
|
+
"-u",
|
|
60
|
+
multiple=True,
|
|
61
|
+
help="User IDs to remove role from (if not specified, removes from all users)",
|
|
62
|
+
)
|
|
63
|
+
def remove_role_from_users(role: str, user_ids: tuple[str, ...]):
|
|
64
|
+
"""Remove automatic roles from users"""
|
|
65
|
+
if user_ids:
|
|
66
|
+
users = (
|
|
67
|
+
model.Session.query(model.User).filter(model.User.id.in_(user_ids)).all()
|
|
68
|
+
)
|
|
69
|
+
else:
|
|
70
|
+
users = model.User.all()
|
|
71
|
+
|
|
72
|
+
for user in users:
|
|
73
|
+
utils.remove_role_from_user(user.id, role)
|
|
74
|
+
|
|
75
|
+
if user_ids:
|
|
76
|
+
click.secho(
|
|
77
|
+
f"Role '{role}' removed from {len(users)} specified user(s)", fg="green"
|
|
78
|
+
)
|
|
79
|
+
else:
|
|
80
|
+
click.secho(f"Role '{role}' removed from all users", fg="green")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ckanext.permissions import const, model, utils
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_registered_roles() -> dict[str, str]:
|
|
7
|
+
"""Get the registered roles
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
The registered roles
|
|
11
|
+
"""
|
|
12
|
+
return utils.get_registered_roles()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_role_permissions(role_id: str, permission: str) -> bool:
|
|
16
|
+
"""Check if a role has a permission
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
role_id (str): The id of the role
|
|
20
|
+
permission (str): The permission to check
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
True if the role has the permission, False otherwise
|
|
24
|
+
"""
|
|
25
|
+
return model.RolePermission.get(role_id, permission) is not None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_user_roles(user_id: str, scope: str = "global", scope_id: str | None = None) -> list[str]:
|
|
29
|
+
"""Get the roles of a user
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
user_id (str): The id of the user
|
|
33
|
+
scope (str): The scope of the role
|
|
34
|
+
scope_id (str | None): The id of the scope
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
The roles of the user
|
|
38
|
+
"""
|
|
39
|
+
return [str(role.role_id) for role in model.UserRole.get(user_id, scope, scope_id)]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def is_default_role(role_id: str) -> bool:
|
|
43
|
+
"""Check if the role is a default role
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
role_id (str): The id of the role to check
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
True if the role is a default role, False otherwise
|
|
50
|
+
"""
|
|
51
|
+
return any(role_id == role.value for role in const.Roles)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ckan.plugins as p
|
|
4
|
+
from ckan import model
|
|
5
|
+
from ckan.lib.plugins import DefaultPermissionLabels
|
|
6
|
+
|
|
7
|
+
import ckanext.permissions.utils as perm_utils
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PermissionLabels(p.SingletonPlugin, DefaultPermissionLabels):
|
|
11
|
+
p.implements(p.IPermissionLabels)
|
|
12
|
+
|
|
13
|
+
def get_dataset_labels(self, dataset_obj: model.Package) -> list[str]:
|
|
14
|
+
labels: list[str] = super().get_dataset_labels(dataset_obj)
|
|
15
|
+
|
|
16
|
+
labels.append("permission-allowed")
|
|
17
|
+
|
|
18
|
+
if dataset_obj.owner_org:
|
|
19
|
+
labels.append(f"permission-allowed-org:{dataset_obj.owner_org}")
|
|
20
|
+
|
|
21
|
+
return labels
|
|
22
|
+
|
|
23
|
+
def get_user_dataset_labels(self, user_obj: model.User | None) -> list[str]:
|
|
24
|
+
"""Get permission labels for a user.
|
|
25
|
+
|
|
26
|
+
Extends the default CKAN permission labels by checking if the user has
|
|
27
|
+
special read permissions (read_any_dataset or read_private_dataset).
|
|
28
|
+
If they do, adds a 'permission-allowed' label that grants access to
|
|
29
|
+
the dataset.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
user_obj: The user to get labels for. Can be None for anonymous users.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
List of permission labels for this user
|
|
36
|
+
"""
|
|
37
|
+
labels: list[str] = super().get_user_dataset_labels(user_obj) # type: ignore
|
|
38
|
+
|
|
39
|
+
user = user_obj or model.AnonymousUser()
|
|
40
|
+
|
|
41
|
+
for permission in ["read_any_dataset", "read_private_dataset"]:
|
|
42
|
+
if perm_utils.check_permission(permission, user):
|
|
43
|
+
labels.append("permission-allowed")
|
|
44
|
+
|
|
45
|
+
return labels
|
|
File without changes
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import cast
|
|
4
|
+
|
|
5
|
+
import ckan.plugins.toolkit as tk
|
|
6
|
+
from ckan import model
|
|
7
|
+
from ckan.logic import validate
|
|
8
|
+
from ckan.types import Context, DataDict
|
|
9
|
+
|
|
10
|
+
from ckanext.permissions import model as perm_model
|
|
11
|
+
from ckanext.permissions import types as perm_types
|
|
12
|
+
from ckanext.permissions import utils as perm_utils
|
|
13
|
+
from ckanext.permissions.logic import schema
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@validate(schema.role_create)
|
|
17
|
+
def permission_role_create(context: Context, data_dict: DataDict) -> perm_types.Role:
|
|
18
|
+
tk.check_access("manage_user_roles", context, data_dict)
|
|
19
|
+
return perm_model.Role.create(**data_dict).dictize(context)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@validate(schema.role_delete)
|
|
23
|
+
def permission_role_delete(context: Context, data_dict: DataDict) -> None:
|
|
24
|
+
tk.check_access("manage_user_roles", context, data_dict)
|
|
25
|
+
|
|
26
|
+
if role := perm_model.Role.get(data_dict["id"]):
|
|
27
|
+
role.delete()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@validate(schema.role_update)
|
|
31
|
+
def permission_role_update(context: Context, data_dict: DataDict) -> perm_types.Role:
|
|
32
|
+
tk.check_access("manage_user_roles", context, data_dict)
|
|
33
|
+
|
|
34
|
+
role = cast(perm_model.Role, perm_model.Role.get(data_dict["id"]))
|
|
35
|
+
|
|
36
|
+
role.update(data_dict["description"])
|
|
37
|
+
|
|
38
|
+
return role.dictize(context)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@validate(schema.permissions_update)
|
|
42
|
+
def permissions_update(context: Context, data_dict: DataDict) -> DataDict:
|
|
43
|
+
"""Update the permissions for a given permission key.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
A dictionary with the updated permissions and the missing permissions
|
|
47
|
+
"""
|
|
48
|
+
tk.check_access("manage_permissions", context, data_dict)
|
|
49
|
+
|
|
50
|
+
_validate_permission_data(data_dict)
|
|
51
|
+
registered_permissions = perm_utils.get_permissions()
|
|
52
|
+
|
|
53
|
+
updated_permissions = {}
|
|
54
|
+
missing_permissions = []
|
|
55
|
+
|
|
56
|
+
for permission_key, roles_data in data_dict["permissions"].items():
|
|
57
|
+
if permission_key not in registered_permissions:
|
|
58
|
+
missing_permissions.append(permission_key)
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
permission_data = {}
|
|
62
|
+
|
|
63
|
+
for role_id, flag in roles_data.items():
|
|
64
|
+
role_permission = perm_model.RolePermission.get(role_id, permission_key)
|
|
65
|
+
|
|
66
|
+
if flag and role_permission:
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
if not flag and not role_permission:
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
if not flag and role_permission:
|
|
73
|
+
role_permission.delete()
|
|
74
|
+
else:
|
|
75
|
+
perm_model.RolePermission.create(role_id, permission_key)
|
|
76
|
+
|
|
77
|
+
permission_data[role_id] = flag
|
|
78
|
+
|
|
79
|
+
updated_permissions[permission_key] = permission_data
|
|
80
|
+
|
|
81
|
+
model.Session.commit()
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
"updated_permissions": updated_permissions,
|
|
85
|
+
"missing_permissions": missing_permissions,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _validate_permission_data(data: DataDict) -> None:
|
|
90
|
+
for permission_key, roles_data in data["permissions"].items():
|
|
91
|
+
if not isinstance(permission_key, str):
|
|
92
|
+
raise tk.ValidationError("Invalid permission key")
|
|
93
|
+
|
|
94
|
+
if not isinstance(roles_data, dict):
|
|
95
|
+
raise tk.ValidationError("Invalid permission mapping")
|
|
96
|
+
|
|
97
|
+
for role_id, flag in roles_data.items():
|
|
98
|
+
if not isinstance(flag, bool):
|
|
99
|
+
raise tk.ValidationError("Invalid permission value")
|
|
100
|
+
|
|
101
|
+
data, errors = tk.navl_validate(
|
|
102
|
+
{"id": role_id},
|
|
103
|
+
{
|
|
104
|
+
"id": [
|
|
105
|
+
tk.get_validator("not_empty"),
|
|
106
|
+
tk.get_validator("unicode_safe"),
|
|
107
|
+
tk.get_validator("permission_role_exists"),
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if errors:
|
|
113
|
+
raise tk.ValidationError(errors)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ckan.plugins.toolkit as tk
|
|
4
|
+
from ckan import model, types
|
|
5
|
+
|
|
6
|
+
import ckanext.permissions.utils as perm_utils
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@tk.chained_auth_function
|
|
10
|
+
@tk.auth_allow_anonymous_access
|
|
11
|
+
def package_show(
|
|
12
|
+
next_: types.AuthFunction,
|
|
13
|
+
context: types.Context,
|
|
14
|
+
data_dict: types.DataDict | None,
|
|
15
|
+
) -> types.AuthResult:
|
|
16
|
+
user = model.User.get(context.get("user")) or model.AnonymousUser()
|
|
17
|
+
package = context.get("package") # type: ignore
|
|
18
|
+
|
|
19
|
+
if not package:
|
|
20
|
+
return next_(context, data_dict or {})
|
|
21
|
+
|
|
22
|
+
# Check permissions in order of precedence
|
|
23
|
+
permission_checks = [
|
|
24
|
+
("read_any_dataset", None),
|
|
25
|
+
("read_private_dataset", lambda: package.private),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
for permission, condition in permission_checks:
|
|
29
|
+
if condition is not None and not condition():
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
if perm_utils.check_permission(permission, user):
|
|
33
|
+
return {"success": True}
|
|
34
|
+
|
|
35
|
+
return next_(context, data_dict or {})
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@tk.chained_auth_function
|
|
39
|
+
@tk.auth_allow_anonymous_access
|
|
40
|
+
def package_update(
|
|
41
|
+
next_: types.AuthFunction, context: types.Context, data_dict: types.DataDict | None
|
|
42
|
+
) -> types.AuthResult:
|
|
43
|
+
user = model.User.get(context.get("user")) or model.AnonymousUser()
|
|
44
|
+
|
|
45
|
+
if perm_utils.check_permission("update_any_dataset", user):
|
|
46
|
+
return {"success": True}
|
|
47
|
+
|
|
48
|
+
return next_(context, data_dict or {})
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@tk.chained_auth_function
|
|
52
|
+
@tk.auth_allow_anonymous_access
|
|
53
|
+
def package_delete(
|
|
54
|
+
next_: types.AuthFunction, context: types.Context, data_dict: types.DataDict | None
|
|
55
|
+
) -> types.AuthResult:
|
|
56
|
+
user = model.User.get(context.get("user")) or model.AnonymousUser()
|
|
57
|
+
|
|
58
|
+
if perm_utils.check_permission("delete_any_dataset", user):
|
|
59
|
+
return {"success": True}
|
|
60
|
+
|
|
61
|
+
return next_(context, data_dict or {})
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@tk.chained_auth_function
|
|
65
|
+
@tk.auth_allow_anonymous_access
|
|
66
|
+
def resource_delete(
|
|
67
|
+
next_: types.AuthFunction, context: types.Context, data_dict: types.DataDict | None
|
|
68
|
+
) -> types.AuthResult:
|
|
69
|
+
user = model.User.get(context.get("user")) or model.AnonymousUser()
|
|
70
|
+
|
|
71
|
+
if perm_utils.check_permission("delete_any_resource", user):
|
|
72
|
+
return {"success": True}
|
|
73
|
+
|
|
74
|
+
return next_(context, data_dict or {})
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def manage_user_roles(context: types.Context, data_dict: types.DataDict) -> types.AuthResult:
|
|
78
|
+
return {"success": False}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def manage_permissions(context: types.Context, data_dict: types.DataDict) -> types.AuthResult:
|
|
82
|
+
return {"success": False}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ckan import types
|
|
4
|
+
from ckan.logic.schema import validator_args
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@validator_args
|
|
8
|
+
def role_create(not_empty, unicode_safe, role_id_validator, role_doesnt_exists, ignore) -> types.Schema:
|
|
9
|
+
return {
|
|
10
|
+
"id": [not_empty, unicode_safe, role_id_validator, role_doesnt_exists],
|
|
11
|
+
"label": [not_empty, unicode_safe],
|
|
12
|
+
"description": [not_empty, unicode_safe],
|
|
13
|
+
"__extras": [ignore],
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@validator_args
|
|
18
|
+
def role_delete(not_empty, unicode_safe, permission_role_exists, not_default_role) -> types.Schema:
|
|
19
|
+
return {
|
|
20
|
+
"id": [not_empty, unicode_safe, permission_role_exists, not_default_role],
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@validator_args
|
|
25
|
+
def role_update(not_empty, unicode_safe, permission_role_exists) -> types.Schema:
|
|
26
|
+
return {
|
|
27
|
+
"id": [not_empty, unicode_safe, permission_role_exists],
|
|
28
|
+
"description": [not_empty, unicode_safe],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@validator_args
|
|
33
|
+
def permissions_update(default, convert_to_json_if_string, dict_only) -> types.Schema:
|
|
34
|
+
return {
|
|
35
|
+
"permissions": [default("{}"), convert_to_json_if_string, dict_only],
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@validator_args
|
|
40
|
+
def permission_group_schema(not_empty, unicode_safe) -> types.Schema:
|
|
41
|
+
return {
|
|
42
|
+
"name": [not_empty, unicode_safe],
|
|
43
|
+
"description": [not_empty, unicode_safe],
|
|
44
|
+
"permissions": permission_schema(),
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@validator_args
|
|
49
|
+
def permission_schema(not_empty, unicode_safe, ignore_missing) -> types.Schema:
|
|
50
|
+
return {
|
|
51
|
+
"key": [not_empty, unicode_safe],
|
|
52
|
+
"label": [not_empty, unicode_safe],
|
|
53
|
+
"description": [ignore_missing, unicode_safe],
|
|
54
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
import ckan.plugins.toolkit as tk
|
|
6
|
+
|
|
7
|
+
import ckanext.permissions.const as perm_const
|
|
8
|
+
import ckanext.permissions.model as perm_model
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def role_doesnt_exists(role: str) -> str:
|
|
12
|
+
"""Ensure that a role doesn't exists.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
role (str): role name
|
|
16
|
+
|
|
17
|
+
Raises:
|
|
18
|
+
tk.Invalid: if the role exists
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
role name
|
|
22
|
+
"""
|
|
23
|
+
if perm_model.Role.get(role) is not None:
|
|
24
|
+
raise tk.Invalid(f"Role {role} is already exists")
|
|
25
|
+
|
|
26
|
+
return role
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def permission_role_exists(role: str) -> str:
|
|
30
|
+
"""Ensure that a role exists.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
role (str): role name
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
tk.Invalid: if the role doesn't exists
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
role name
|
|
40
|
+
"""
|
|
41
|
+
if perm_model.Role.get(role) is None:
|
|
42
|
+
raise tk.Invalid(f"Role {role} doesn't exists")
|
|
43
|
+
|
|
44
|
+
return role
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def roles_exists(roles: list[str]) -> list[str]:
|
|
48
|
+
"""Ensure that all roles exists.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
roles (list[str]): list of roles
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
tk.Invalid: if a role doesn't exists
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
list of roles
|
|
58
|
+
"""
|
|
59
|
+
for role in roles:
|
|
60
|
+
permission_role_exists(role)
|
|
61
|
+
|
|
62
|
+
return roles
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def role_id_validator(value: str) -> str:
|
|
66
|
+
"""Validate a role ID.
|
|
67
|
+
|
|
68
|
+
Ensures that:
|
|
69
|
+
- the role ID is a string
|
|
70
|
+
- is at least N characters long
|
|
71
|
+
- is at most N characters long
|
|
72
|
+
- contains only lowercase alpha (ascii) characters and these symbols: -_
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
value (str): role ID
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
tk.Invalid: if the role ID is invalid
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
role ID
|
|
82
|
+
"""
|
|
83
|
+
name_match = re.compile(r"[a-z_\-]*$")
|
|
84
|
+
|
|
85
|
+
if len(value) < perm_const.ROLE_ID_MIN_LENGTH:
|
|
86
|
+
raise tk.Invalid(f"Role ID must be at least {perm_const.ROLE_ID_MIN_LENGTH} characters long.")
|
|
87
|
+
|
|
88
|
+
if len(value) > perm_const.ROLE_ID_MAX_LENGTH:
|
|
89
|
+
raise tk.Invalid(f"Role ID must be a maximum of {perm_const.ROLE_ID_MAX_LENGTH} characters long.")
|
|
90
|
+
|
|
91
|
+
if not name_match.match(value):
|
|
92
|
+
raise tk.Invalid("Role ID must be purely lowercase alpha(ascii) characters and these symbols: -_")
|
|
93
|
+
|
|
94
|
+
return value
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def not_default_role(role_id: str) -> str:
|
|
98
|
+
"""Ensure that the role is not a default role.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
role_id (str): role ID
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
tk.Invalid: if the role is a default role
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
role ID
|
|
108
|
+
"""
|
|
109
|
+
if role_id in [role.value for role in perm_const.Roles]:
|
|
110
|
+
raise tk.Invalid(f"Role {role_id} is a default role.")
|
|
111
|
+
|
|
112
|
+
return role_id
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# A generic, single database configuration.
|
|
2
|
+
|
|
3
|
+
[alembic]
|
|
4
|
+
# path to migration scripts
|
|
5
|
+
script_location = %(here)s
|
|
6
|
+
|
|
7
|
+
# template used to generate migration files
|
|
8
|
+
# file_template = %%(rev)s_%%(slug)s
|
|
9
|
+
|
|
10
|
+
# timezone to use when rendering the date
|
|
11
|
+
# within the migration file as well as the filename.
|
|
12
|
+
# string value is passed to dateutil.tz.gettz()
|
|
13
|
+
# leave blank for localtime
|
|
14
|
+
# timezone =
|
|
15
|
+
|
|
16
|
+
# max length of characters to apply to the
|
|
17
|
+
# "slug" field
|
|
18
|
+
#truncate_slug_length = 40
|
|
19
|
+
|
|
20
|
+
# set to 'true' to run the environment during
|
|
21
|
+
# the 'revision' command, regardless of autogenerate
|
|
22
|
+
# revision_environment = false
|
|
23
|
+
|
|
24
|
+
# set to 'true' to allow .pyc and .pyo files without
|
|
25
|
+
# a source .py file to be detected as revisions in the
|
|
26
|
+
# versions/ directory
|
|
27
|
+
# sourceless = false
|
|
28
|
+
|
|
29
|
+
# version location specification; this defaults
|
|
30
|
+
# to /home/berry/projects/master/ckanext-permissions/ckanext/permissions/migration/permissions/versions. When using multiple version
|
|
31
|
+
# directories, initial revisions must be specified with --version-path
|
|
32
|
+
# version_locations = %(here)s/bar %(here)s/bat /home/berry/projects/master/ckanext-permissions/ckanext/permissions/migration/permissions/versions
|
|
33
|
+
|
|
34
|
+
# the output encoding used when revision files
|
|
35
|
+
# are written from script.py.mako
|
|
36
|
+
# output_encoding = utf-8
|
|
37
|
+
|
|
38
|
+
sqlalchemy.url = driver://user:pass@localhost/dbname
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Logging configuration
|
|
42
|
+
[loggers]
|
|
43
|
+
keys = root,sqlalchemy,alembic
|
|
44
|
+
|
|
45
|
+
[handlers]
|
|
46
|
+
keys = console
|
|
47
|
+
|
|
48
|
+
[formatters]
|
|
49
|
+
keys = generic
|
|
50
|
+
|
|
51
|
+
[logger_root]
|
|
52
|
+
level = WARN
|
|
53
|
+
handlers = console
|
|
54
|
+
qualname =
|
|
55
|
+
|
|
56
|
+
[logger_sqlalchemy]
|
|
57
|
+
level = WARN
|
|
58
|
+
handlers =
|
|
59
|
+
qualname = sqlalchemy.engine
|
|
60
|
+
|
|
61
|
+
[logger_alembic]
|
|
62
|
+
level = INFO
|
|
63
|
+
handlers =
|
|
64
|
+
qualname = alembic
|
|
65
|
+
|
|
66
|
+
[handler_console]
|
|
67
|
+
class = StreamHandler
|
|
68
|
+
args = (sys.stderr,)
|
|
69
|
+
level = NOTSET
|
|
70
|
+
formatter = generic
|
|
71
|
+
|
|
72
|
+
[formatter_generic]
|
|
73
|
+
format = %(levelname)-5.5s [%(name)s] %(message)s
|
|
74
|
+
datefmt = %H:%M:%S
|