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.
Files changed (39) hide show
  1. ckanext/__init__.py +9 -0
  2. ckanext/permissions/__init__.py +0 -0
  3. ckanext/permissions/cli.py +80 -0
  4. ckanext/permissions/const.py +10 -0
  5. ckanext/permissions/helpers.py +51 -0
  6. ckanext/permissions/implementation/__init__.py +5 -0
  7. ckanext/permissions/implementation/permission_labels.py +45 -0
  8. ckanext/permissions/logic/__init__.py +0 -0
  9. ckanext/permissions/logic/action.py +113 -0
  10. ckanext/permissions/logic/auth.py +82 -0
  11. ckanext/permissions/logic/schema.py +54 -0
  12. ckanext/permissions/logic/validators.py +112 -0
  13. ckanext/permissions/migration/permissions/alembic.ini +74 -0
  14. ckanext/permissions/migration/permissions/env.py +85 -0
  15. ckanext/permissions/migration/permissions/script.py.mako +24 -0
  16. ckanext/permissions/migration/permissions/versions/a849104ccfdc_init_tables.py +69 -0
  17. ckanext/permissions/model.py +148 -0
  18. ckanext/permissions/plugin.py +67 -0
  19. ckanext/permissions/tests/__init__.py +0 -0
  20. ckanext/permissions/tests/actions/test_permission.py +68 -0
  21. ckanext/permissions/tests/actions/test_role.py +110 -0
  22. ckanext/permissions/tests/conftest.py +52 -0
  23. ckanext/permissions/tests/test_autoassign.py +26 -0
  24. ckanext/permissions/tests/test_helpers.py +67 -0
  25. ckanext/permissions/tests/test_permission_labels.py +28 -0
  26. ckanext/permissions/tests/test_utils.py +275 -0
  27. ckanext/permissions/tests/test_validators.py +98 -0
  28. ckanext/permissions/types.py +34 -0
  29. ckanext/permissions/utils.py +197 -0
  30. ckanext/permissions_manager/__init__.py +0 -0
  31. ckanext/permissions_manager/helpers.py +23 -0
  32. ckanext/permissions_manager/plugin.py +48 -0
  33. ckanext/permissions_manager/views.py +351 -0
  34. ckanext_permissions-0.2.0.dist-info/METADATA +104 -0
  35. ckanext_permissions-0.2.0.dist-info/RECORD +39 -0
  36. ckanext_permissions-0.2.0.dist-info/WHEEL +5 -0
  37. ckanext_permissions-0.2.0.dist-info/entry_points.txt +6 -0
  38. ckanext_permissions-0.2.0.dist-info/licenses/LICENSE +661 -0
  39. ckanext_permissions-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,85 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import with_statement
4
+
5
+ import os
6
+ from logging.config import fileConfig
7
+
8
+ from alembic import context
9
+ from sqlalchemy import engine_from_config, pool
10
+
11
+ # this is the Alembic Config object, which provides
12
+ # access to the values within the .ini file in use.
13
+ config = context.config
14
+
15
+ # Interpret the config file for Python logging.
16
+ # This line sets up loggers basically.
17
+ fileConfig(config.config_file_name)
18
+
19
+ # add your model's MetaData object here
20
+ # for 'autogenerate' support
21
+ # from myapp import mymodel
22
+ # target_metadata = mymodel.Base.metadata
23
+ target_metadata = None
24
+
25
+ # other values from the config, defined by the needs of env.py,
26
+ # can be acquired:
27
+ # my_important_option = config.get_main_option("my_important_option")
28
+ # ... etc.
29
+
30
+ name = os.path.basename(os.path.dirname(__file__))
31
+
32
+
33
+ def run_migrations_offline():
34
+ """Run migrations in 'offline' mode.
35
+
36
+ This configures the context with just a URL
37
+ and not an Engine, though an Engine is acceptable
38
+ here as well. By skipping the Engine creation
39
+ we don't even need a DBAPI to be available.
40
+
41
+ Calls to context.execute() here emit the given string to the
42
+ script output.
43
+
44
+ """
45
+
46
+ url = config.get_main_option("sqlalchemy.url")
47
+ context.configure(
48
+ url=url,
49
+ target_metadata=target_metadata,
50
+ literal_binds=True,
51
+ version_table="{}_alembic_version".format(name),
52
+ )
53
+
54
+ with context.begin_transaction():
55
+ context.run_migrations()
56
+
57
+
58
+ def run_migrations_online():
59
+ """Run migrations in 'online' mode.
60
+
61
+ In this scenario we need to create an Engine
62
+ and associate a connection with the context.
63
+
64
+ """
65
+ connectable = engine_from_config(
66
+ config.get_section(config.config_ini_section),
67
+ prefix="sqlalchemy.",
68
+ poolclass=pool.NullPool,
69
+ )
70
+
71
+ with connectable.connect() as connection:
72
+ context.configure(
73
+ connection=connection,
74
+ target_metadata=target_metadata,
75
+ version_table="{}_alembic_version".format(name),
76
+ )
77
+
78
+ with context.begin_transaction():
79
+ context.run_migrations()
80
+
81
+
82
+ if context.is_offline_mode():
83
+ run_migrations_offline()
84
+ else:
85
+ run_migrations_online()
@@ -0,0 +1,24 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from alembic import op
9
+ import sqlalchemy as sa
10
+ ${imports if imports else ""}
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = ${repr(up_revision)}
14
+ down_revision = ${repr(down_revision)}
15
+ branch_labels = ${repr(branch_labels)}
16
+ depends_on = ${repr(depends_on)}
17
+
18
+
19
+ def upgrade():
20
+ ${upgrades if upgrades else "pass"}
21
+
22
+
23
+ def downgrade():
24
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1,69 @@
1
+ """Init tables
2
+
3
+ Revision ID: a849104ccfdc
4
+ Revises:
5
+ Create Date: 2024-02-14 17:11:12.064281
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = "a849104ccfdc"
14
+ down_revision = None
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade():
20
+ op.create_table(
21
+ "perm_role",
22
+ sa.Column("id", sa.String(), primary_key=True),
23
+ sa.Column("label", sa.String(), nullable=False),
24
+ sa.Column("description", sa.String(), nullable=False),
25
+ )
26
+
27
+ op.create_table(
28
+ "perm_user_role",
29
+ sa.Column(
30
+ "user_id",
31
+ sa.String(),
32
+ sa.ForeignKey("user.id", ondelete="CASCADE"),
33
+ primary_key=True,
34
+ ),
35
+ sa.Column(
36
+ "role_id",
37
+ sa.String(),
38
+ sa.ForeignKey("perm_role.id", ondelete="CASCADE"),
39
+ primary_key=True,
40
+ ),
41
+ sa.Column(
42
+ "scope",
43
+ sa.String(),
44
+ primary_key=True,
45
+ default="global",
46
+ ),
47
+ sa.Column(
48
+ "scope_id",
49
+ sa.String(),
50
+ nullable=True,
51
+ ),
52
+ )
53
+
54
+ op.create_table(
55
+ "perm_role_permission",
56
+ sa.Column(
57
+ "role_id",
58
+ sa.String(),
59
+ sa.ForeignKey("perm_role.id", ondelete="CASCADE"),
60
+ primary_key=True,
61
+ ),
62
+ sa.Column("permission", sa.String(), nullable=False, primary_key=True),
63
+ )
64
+
65
+
66
+ def downgrade():
67
+ op.drop_table("perm_role_permission")
68
+ op.drop_table("perm_user_role")
69
+ op.drop_table("perm_role")
@@ -0,0 +1,148 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+
5
+ from sqlalchemy import Column, ForeignKey, String
6
+ from sqlalchemy.orm import Query, backref, relationship
7
+ from typing_extensions import Self
8
+
9
+ import ckan.model as model
10
+ import ckan.types as types
11
+ from ckan.plugins import toolkit as tk
12
+
13
+ import ckanext.permissions.types as perm_types
14
+
15
+ log = logging.getLogger(__name__)
16
+
17
+
18
+ class Role(tk.BaseModel):
19
+ __tablename__ = "perm_role"
20
+
21
+ id = Column(String, primary_key=True)
22
+ label = Column(String, nullable=False)
23
+ description = Column(String, nullable=False)
24
+
25
+ @classmethod
26
+ def create(cls, id: str, label: str, description: str) -> Self:
27
+ role = cls(id=id, label=label, description=description)
28
+
29
+ model.Session.add(role)
30
+ model.Session.commit()
31
+
32
+ return role
33
+
34
+ @classmethod
35
+ def get(cls, role: str) -> Self | None:
36
+ return model.Session.query(cls).filter(cls.id == role).one_or_none()
37
+
38
+ @classmethod
39
+ def all(cls) -> list[Self]:
40
+ return [role.dictize({}) for role in model.Session.query(cls).all()]
41
+
42
+ def update(self, description: str) -> None:
43
+ self.description = description
44
+
45
+ model.Session.commit()
46
+
47
+ def dictize(self, context: types.Context) -> perm_types.Role:
48
+ return perm_types.Role(
49
+ id=str(self.id),
50
+ label=str(self.label),
51
+ description=str(self.description),
52
+ )
53
+
54
+ def delete(self) -> None:
55
+ model.Session.delete(self)
56
+ model.Session.commit()
57
+
58
+
59
+ class UserRole(tk.BaseModel):
60
+ __tablename__ = "perm_user_role"
61
+
62
+ user_id = Column(String, ForeignKey("user.id", ondelete="CASCADE"), primary_key=True)
63
+ role_id = Column(String, ForeignKey("perm_role.id", ondelete="CASCADE"), primary_key=True)
64
+
65
+ scope = Column(String, primary_key=True, default="global")
66
+ scope_id = Column(String, nullable=True)
67
+
68
+ user = relationship(
69
+ model.User,
70
+ backref=backref("roles", cascade="all, delete"),
71
+ )
72
+
73
+ role = relationship(Role, cascade="all, delete")
74
+
75
+ @classmethod
76
+ def get(cls, user_id: str, scope: str = "global", scope_id: str | None = None) -> list[Self]:
77
+ query: Query = model.Session.query(cls).filter(cls.user_id == user_id).filter(cls.scope == scope)
78
+
79
+ if scope_id:
80
+ query = query.filter(cls.scope_id == scope_id)
81
+
82
+ return query.all()
83
+
84
+ @classmethod
85
+ def create(
86
+ cls,
87
+ user_id: str,
88
+ role: str,
89
+ scope: str = "global",
90
+ scope_id: str | None = None,
91
+ ) -> Self:
92
+ for user_role in cls.get(user_id, scope, scope_id):
93
+ if user_role.role_id != role:
94
+ continue
95
+
96
+ return user_role
97
+
98
+ user_role = cls(user_id=user_id, role_id=role, scope=scope, scope_id=scope_id)
99
+
100
+ model.Session.add(user_role)
101
+ model.Session.commit()
102
+
103
+ return user_role
104
+
105
+ @classmethod
106
+ def clear_user_roles(cls, user_id: str, scope: str = "", scope_id: str | None = None) -> None:
107
+ perm = model.Session.query(UserRole).filter(UserRole.user_id == user_id)
108
+
109
+ if scope:
110
+ perm = perm.filter_by(scope=scope)
111
+ if scope_id:
112
+ perm = perm.filter_by(scope_id=scope_id)
113
+
114
+ perm.delete()
115
+ model.Session.commit()
116
+
117
+ @classmethod
118
+ def delete(cls, user_id: str, role: str) -> None:
119
+ model.Session.query(cls).filter(cls.user_id == user_id, cls.role_id == role).delete()
120
+ model.Session.commit()
121
+
122
+
123
+ class RolePermission(tk.BaseModel):
124
+ __tablename__ = "perm_role_permission"
125
+
126
+ role_id = Column(String, ForeignKey("perm_role.id"), primary_key=True)
127
+ permission = Column(String, primary_key=True)
128
+
129
+ @classmethod
130
+ def get(cls, role_id: str, permission: str) -> Self | None:
131
+ query: Query = model.Session.query(cls).filter(cls.role_id == role_id, cls.permission == permission)
132
+
133
+ return query.one_or_none()
134
+
135
+ @classmethod
136
+ def create(cls, role_id: str, permission: str, defer_commit: bool = True) -> Self:
137
+ role_permission = cls(role_id=role_id, permission=permission)
138
+
139
+ model.Session.add(role_permission)
140
+
141
+ if defer_commit:
142
+ model.Session.commit()
143
+
144
+ return role_permission
145
+
146
+ def delete(self) -> None:
147
+ model.Session().autoflush = False
148
+ model.Session.delete(self)
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ import ckan.plugins as p
4
+ import ckan.plugins.toolkit as tk
5
+ from ckan import types
6
+
7
+ from ckanext.permissions import const as perm_const
8
+ from ckanext.permissions import implementation
9
+ from ckanext.permissions import types as perm_types
10
+ from ckanext.permissions import utils
11
+
12
+
13
+ @tk.blanket.cli
14
+ @tk.blanket.validators
15
+ @tk.blanket.actions
16
+ @tk.blanket.helpers
17
+ @tk.blanket.auth_functions
18
+ @tk.blanket.config_declarations
19
+ class PermissionsPlugin(implementation.PermissionLabels, p.SingletonPlugin):
20
+ p.implements(p.IConfigurer)
21
+ p.implements(p.ISignal)
22
+
23
+ _permissions_groups: perm_types.PermissionGroup | None = None
24
+ _permissions = dict[str, perm_types.PermissionDefinition]
25
+
26
+ # IConfigurer
27
+
28
+ def update_config(self, config_: tk.CKANConfig):
29
+ if not PermissionsPlugin._permissions_groups: # type: ignore
30
+ PermissionsPlugin._permissions_groups = list( # type: ignore
31
+ utils.parse_permission_group_schemas().values()
32
+ )
33
+ PermissionsPlugin._permissions = { # type: ignore
34
+ permission["key"]: permission
35
+ for group in PermissionsPlugin._permissions_groups # type: ignore
36
+ for permission in group["permissions"]
37
+ }
38
+
39
+ tk.add_template_directory(config_, "templates")
40
+
41
+ # ISignal
42
+
43
+ def get_signal_subscriptions(self) -> types.SignalMapping:
44
+ return {tk.signals.action_succeeded: [self.assign_default_user_role]}
45
+
46
+ @staticmethod
47
+ def assign_default_user_role(
48
+ action_name: str,
49
+ context: types.Context,
50
+ data_dict: types.DataDict,
51
+ result: types.DataDict,
52
+ ):
53
+ """Assign the default user role to a new user
54
+
55
+ Args:
56
+ action_name: The name of the action
57
+ context: The action context
58
+ data_dict: The action payload
59
+ result: The action result
60
+ """
61
+
62
+ if action_name != "user_create":
63
+ return
64
+
65
+ utils.assign_role_to_user(
66
+ result["id"], perm_const.Roles.Authenticated.value, "global"
67
+ )
File without changes
@@ -0,0 +1,68 @@
1
+ import pytest
2
+
3
+ import ckan.plugins.toolkit as tk
4
+ from ckan.tests.helpers import call_action
5
+
6
+ from ckanext.permissions import model as perm_model
7
+
8
+
9
+ @pytest.mark.usefixtures("with_plugins", "clean_db")
10
+ class TestPermissionsUpdate:
11
+ def test_permissions_update(self):
12
+ result = call_action(
13
+ "permissions_update",
14
+ permissions={
15
+ "perm_1": {
16
+ "anonymous": False,
17
+ "authenticated": True,
18
+ "administrator": True,
19
+ },
20
+ "perm_2": {
21
+ "anonymous": False,
22
+ "authenticated": True,
23
+ "administrator": True,
24
+ },
25
+ },
26
+ )
27
+
28
+ assert not result["missing_permissions"]
29
+ assert result["updated_permissions"] == {
30
+ "perm_1": {"authenticated": True, "administrator": True},
31
+ "perm_2": {"authenticated": True, "administrator": True},
32
+ }
33
+
34
+ assert not perm_model.RolePermission.get("anonymous", "perm_1")
35
+ assert perm_model.RolePermission.get("authenticated", "perm_1")
36
+ assert perm_model.RolePermission.get("administrator", "perm_1")
37
+
38
+ result = call_action(
39
+ "permissions_update",
40
+ permissions={"perm_1": {"anonymous": True}},
41
+ )
42
+
43
+ assert perm_model.RolePermission.get("anonymous", "perm_1")
44
+
45
+ def test_permissions_update_unregistered_permission_key(self):
46
+ result = call_action("permissions_update", permissions={"xxx": {}})
47
+
48
+ assert result["missing_permissions"] == ["xxx"]
49
+ assert not result["updated_permissions"]
50
+
51
+ def test_not_string_permission_key(self):
52
+ with pytest.raises(tk.ValidationError, match="Invalid permission key"):
53
+ call_action("permissions_update", permissions={1: {}})
54
+
55
+ def test_not_dict_roles_mapping(self):
56
+ with pytest.raises(tk.ValidationError, match="Invalid permission mapping"):
57
+ call_action("permissions_update", permissions={"perm_1": 1})
58
+
59
+ def test_not_bool_permission_value(self):
60
+ with pytest.raises(tk.ValidationError, match="Invalid permission value"):
61
+ call_action(
62
+ "permissions_update",
63
+ permissions={"perm_1": {"anonymous": "xxx", "authenticated": "yyy"}},
64
+ )
65
+
66
+ def test_role_id_not_exists(self):
67
+ with pytest.raises(tk.ValidationError, match="Role xxx doesn't exists"):
68
+ call_action("permissions_update", permissions={"perm_1": {"xxx": True}})
@@ -0,0 +1,110 @@
1
+ from typing import Any
2
+
3
+ import pytest
4
+
5
+ import ckan.plugins.toolkit as tk
6
+ from ckan.tests.helpers import call_action
7
+
8
+
9
+ @pytest.mark.usefixtures("with_plugins", "clean_db")
10
+ class TestPermissionRoleCreate:
11
+ def test_permission_role_create(self):
12
+ result = call_action(
13
+ "permission_role_create",
14
+ id="admin",
15
+ label="Admin",
16
+ description="Admin role",
17
+ )
18
+ assert result["id"] == "admin"
19
+ assert result["label"] == "Admin"
20
+ assert result["description"] == "Admin role"
21
+
22
+ @pytest.mark.parametrize("field", ["id", "label", "description"])
23
+ def test_permission_role_create_missing_required_fields(self, field):
24
+ data = {"id": "admin", "label": "Admin", "description": "Admin role"}
25
+ data.pop(field)
26
+
27
+ with pytest.raises(tk.ValidationError) as e:
28
+ call_action("permission_role_create", **data)
29
+
30
+ assert e.value.error_dict[field] == ["Missing value"]
31
+
32
+ def test_permission_role_create_duplicate_id(self):
33
+ call_action(
34
+ "permission_role_create",
35
+ id="admin",
36
+ label="Admin",
37
+ description="Admin role",
38
+ )
39
+
40
+ with pytest.raises(tk.ValidationError) as e:
41
+ call_action(
42
+ "permission_role_create",
43
+ id="admin",
44
+ label="Another Admin",
45
+ description="Another admin role",
46
+ )
47
+
48
+ assert e.value.error_dict["id"] == ["Role admin is already exists"]
49
+
50
+
51
+ @pytest.mark.usefixtures("with_plugins", "clean_db")
52
+ class TestPermissionRoleDelete:
53
+ def test_permission_role_delete(self):
54
+ # Create a role first
55
+ call_action(
56
+ "permission_role_create",
57
+ id="custom_role",
58
+ label="Custom Role",
59
+ description="Custom role for testing",
60
+ )
61
+
62
+ call_action("permission_role_delete", id="custom_role")
63
+
64
+ with pytest.raises(tk.ValidationError) as e:
65
+ call_action("permission_role_delete", id="xxx")
66
+
67
+ assert e.value.error_dict["id"] == ["Role xxx doesn't exists"]
68
+
69
+ def test_permission_role_delete_missing_id(self):
70
+ with pytest.raises(tk.ValidationError) as e:
71
+ call_action("permission_role_delete")
72
+
73
+ assert e.value.error_dict["id"] == ["Missing value"]
74
+
75
+ def test_permission_role_delete_nonexistent_role(self):
76
+ with pytest.raises(tk.ValidationError) as e:
77
+ call_action("permission_role_delete", id="xxx")
78
+
79
+ assert e.value.error_dict["id"] == ["Role xxx doesn't exists"]
80
+
81
+
82
+ @pytest.mark.usefixtures("with_plugins", "clean_db")
83
+ class TestPermissionRoleUpdate:
84
+ def test_permission_role_update(self, test_role: dict[str, Any]):
85
+ result = call_action("permission_role_update", id=test_role["id"], description="New description")
86
+
87
+ assert result["id"] == test_role["id"]
88
+ assert result["description"] == "New description"
89
+
90
+ def test_permission_role_update_missing_id(self):
91
+ with pytest.raises(tk.ValidationError) as e:
92
+ call_action("permission_role_update")
93
+
94
+ assert e.value.error_dict["id"] == ["Missing value"]
95
+
96
+ def test_permission_role_update_nonexistent_role(self):
97
+ with pytest.raises(tk.ValidationError) as e:
98
+ call_action("permission_role_update", id="xxx")
99
+
100
+ assert e.value.error_dict["id"] == ["Role xxx doesn't exists"]
101
+
102
+ def test_permission_role_update_cant_update_label(self, test_role: dict[str, Any]):
103
+ result = call_action(
104
+ "permission_role_update",
105
+ id=test_role["id"],
106
+ label="XXX",
107
+ description="New description",
108
+ )
109
+
110
+ assert test_role["label"] == result["label"]
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ import factory
4
+ import pytest
5
+ from faker import Faker
6
+ from pytest_factoryboy import register
7
+
8
+ from ckan.tests import factories
9
+
10
+ import ckanext.permissions.model as perm_model
11
+ from ckanext.permissions import utils as perm_utils
12
+
13
+ fake = Faker()
14
+
15
+
16
+ @pytest.fixture()
17
+ def clean_db(reset_db, migrate_db_for):
18
+ reset_db()
19
+ migrate_db_for("permissions")
20
+
21
+ perm_utils.ensure_default_roles()
22
+
23
+
24
+ @register(_name="test_role")
25
+ class RoleFactory(factories.CKANFactory):
26
+ class Meta:
27
+ model = perm_model.Role
28
+ action = "permission_role_create"
29
+
30
+ id = "creator"
31
+ label = "Creator"
32
+ description = factory.LazyFunction(lambda: fake.sentence(nb_words=5))
33
+
34
+
35
+ @register(_name="dataset")
36
+ class DatasetFactory(factories.Dataset):
37
+ owner_org = factory.LazyFunction(lambda: OrganizationFactory()["id"])
38
+
39
+
40
+ @register(_name="organization")
41
+ class OrganizationFactory(factories.Organization):
42
+ pass
43
+
44
+
45
+ @register(_name="sysadmin")
46
+ class SysadminFactory(factories.SysadminWithToken):
47
+ pass
48
+
49
+
50
+ @register(_name="user")
51
+ class UserFactory(factories.UserWithToken):
52
+ pass
@@ -0,0 +1,26 @@
1
+ import pytest
2
+
3
+ import ckan.plugins.toolkit as tk
4
+ from ckan.tests.helpers import call_action
5
+
6
+ import ckanext.permissions.model as perm_model
7
+
8
+
9
+ @pytest.mark.usefixtures("with_plugins", "clean_db")
10
+ class TestRoleAutoassignment:
11
+ def test_assign_user_role_on_create(self, user):
12
+ """Test if a role is assigned to a user when it's created"""
13
+ roles = tk.h.get_user_roles(user["id"])
14
+ assert roles == ["authenticated"]
15
+
16
+ def test_user_roles_deleted_with_role(self, user, test_role):
17
+ """Test if user roles are deleted when the role is deleted"""
18
+ perm_model.UserRole.create(user["id"], test_role["id"])
19
+
20
+ result = tk.h.get_user_roles(user["id"])
21
+ assert "authenticated" in result
22
+ assert "creator" in result
23
+
24
+ call_action("permission_role_delete", id=test_role["id"])
25
+
26
+ assert tk.h.get_user_roles(user["id"]) == ["authenticated"]