From e952e1031e50976d2eec52b3370a0fb0fcfaacc4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 12 Dec 2024 19:24:36 -0600 Subject: [PATCH] fix: add basic views for raw Permissions --- src/wuttaweb/forms/schema.py | 24 ++++++++++++ src/wuttaweb/menus.py | 5 +++ src/wuttaweb/templates/base.mako | 1 + src/wuttaweb/views/roles.py | 66 +++++++++++++++++++++++++++++++- tests/forms/test_schema.py | 24 ++++++++++++ tests/views/test_roles.py | 31 +++++++++++++++ 6 files changed, 149 insertions(+), 2 deletions(-) diff --git a/src/wuttaweb/forms/schema.py b/src/wuttaweb/forms/schema.py index bb49d1f..c414376 100644 --- a/src/wuttaweb/forms/schema.py +++ b/src/wuttaweb/forms/schema.py @@ -382,6 +382,30 @@ class PersonRef(ObjectRef): return self.request.route_url('people.view', uuid=person.uuid) +class RoleRef(ObjectRef): + """ + Custom schema type for a + :class:`~wuttjamaican:wuttjamaican.db.model.auth.Role` reference + field. + + This is a subclass of :class:`ObjectRef`. + """ + + @property + def model_class(self): + """ """ + model = self.app.model + return model.Role + + def sort_query(self, query): + """ """ + return query.order_by(self.model_class.name) + + def get_object_url(self, role): + """ """ + return self.request.route_url('roles.view', uuid=role.uuid) + + class UserRef(ObjectRef): """ Custom schema type for a diff --git a/src/wuttaweb/menus.py b/src/wuttaweb/menus.py index 80b6c53..1ac1180 100644 --- a/src/wuttaweb/menus.py +++ b/src/wuttaweb/menus.py @@ -168,6 +168,11 @@ class MenuHandler(GenericHandler): 'route': 'roles', 'perm': 'roles.list', }, + { + 'title': "Permissions", + 'route': 'permissions', + 'perm': 'permissions.list', + }, {'type': 'sep'}, { 'title': "App Info", diff --git a/src/wuttaweb/templates/base.mako b/src/wuttaweb/templates/base.mako index 695bfb8..a33d3ca 100644 --- a/src/wuttaweb/templates/base.mako +++ b/src/wuttaweb/templates/base.mako @@ -583,6 +583,7 @@ const WholePage = { template: '#whole-page-template', + mixins: [WuttaRequestMixin], computed: {}, mounted() { diff --git a/src/wuttaweb/views/roles.py b/src/wuttaweb/views/roles.py index bf4981d..a93bf9e 100644 --- a/src/wuttaweb/views/roles.py +++ b/src/wuttaweb/views/roles.py @@ -24,11 +24,11 @@ Views for roles """ -from wuttjamaican.db.model import Role +from wuttjamaican.db.model import Role, Permission from wuttaweb.views import MasterView from wuttaweb.db import Session from wuttaweb.forms import widgets -from wuttaweb.forms.schema import UserRefs, Permissions +from wuttaweb.forms.schema import UserRefs, Permissions, RoleRef class RoleView(MasterView): @@ -273,12 +273,74 @@ class RoleView(MasterView): f"Edit the Built-in {model_title_plural}") +class PermissionView(MasterView): + """ + Master view for permissions. + + Default route prefix is ``permissions``. + + Notable URLs provided by this class: + + * ``/permissions/`` + * ``/permissions/XXX`` + * ``/permissions/XXX/delete`` + """ + model_class = Permission + creatable = False + editable = False + + grid_columns = [ + 'role', + 'permission', + ] + + sort_defaults = 'role' + + form_fields = [ + 'role', + 'permission', + ] + + def get_query(self, **kwargs): + """ """ + query = super().get_query(**kwargs) + model = self.app.model + + # always join on Role + query = query.join(model.Role) + + return query + + def configure_grid(self, g): + """ """ + super().configure_grid(g) + model = self.app.model + + # role + g.set_sorter('role', model.Role.name) + g.set_filter('role', model.Role.name, label="Role Name") + g.set_link('role') + + # permission + g.set_link('permission') + + def configure_form(self, f): + """ """ + super().configure_form(f) + + # role + f.set_node('role', RoleRef(self.request)) + + def defaults(config, **kwargs): base = globals() RoleView = kwargs.get('RoleView', base['RoleView']) RoleView.defaults(config) + PermissionView = kwargs.get('PermissionView', base['PermissionView']) + PermissionView.defaults(config) + def includeme(config): defaults(config) diff --git a/tests/forms/test_schema.py b/tests/forms/test_schema.py index 9d6e70f..299add2 100644 --- a/tests/forms/test_schema.py +++ b/tests/forms/test_schema.py @@ -255,6 +255,30 @@ class TestPersonRef(WebTestCase): self.assertIn(f'/people/{person.uuid}', url) +class TestRoleRef(WebTestCase): + + def test_sort_query(self): + typ = mod.RoleRef(self.request, session=self.session) + query = typ.get_query() + self.assertIsInstance(query, orm.Query) + sorted_query = typ.sort_query(query) + self.assertIsInstance(sorted_query, orm.Query) + self.assertIsNot(sorted_query, query) + + def test_get_object_url(self): + self.pyramid_config.add_route('roles.view', '/roles/{uuid}') + model = self.app.model + typ = mod.RoleRef(self.request, session=self.session) + + role = model.Role(name='Manager') + self.session.add(role) + self.session.commit() + + url = typ.get_object_url(role) + self.assertIsNotNone(url) + self.assertIn(f'/roles/{role.uuid}', url) + + class TestUserRef(WebTestCase): def test_sort_query(self): diff --git a/tests/views/test_roles.py b/tests/views/test_roles.py index 8641d6f..22a30d2 100644 --- a/tests/views/test_roles.py +++ b/tests/views/test_roles.py @@ -7,6 +7,7 @@ from sqlalchemy import orm import colander from wuttaweb.views import roles as mod +from wuttaweb.forms.schema import RoleRef from tests.util import WebTestCase @@ -240,3 +241,33 @@ class TestRoleView(WebTestCase): self.session.commit() self.assertIs(role, blokes) self.assertEqual(blokes.permissions, ['widgets.polish', 'widgets.view']) + + +class TestPermissionView(WebTestCase): + + def make_view(self): + return mod.PermissionView(self.request) + + def test_get_query(self): + view = self.make_view() + query = view.get_query(session=self.session) + self.assertIsInstance(query, orm.Query) + + def test_configure_grid(self): + model = self.app.model + view = self.make_view() + grid = view.make_grid(model_class=model.Permission) + self.assertFalse(grid.is_linked('role')) + view.configure_grid(grid) + self.assertTrue(grid.is_linked('role')) + + def test_configure_form(self): + model = self.app.model + role = model.Role(name="Foo") + perm = model.Permission(role=role, permission='whatever') + view = self.make_view() + form = view.make_form(model_instance=perm) + self.assertIsNone(form.schema) + view.configure_form(form) + schema = form.get_schema() + self.assertIsInstance(schema['role'].typ, RoleRef)