diff --git a/tailbone/templates/roles/index.mako b/tailbone/templates/roles/index.mako new file mode 100644 index 00000000..857e5f6a --- /dev/null +++ b/tailbone/templates/roles/index.mako @@ -0,0 +1,12 @@ +## -*- coding: utf-8; -*- +<%inherit file="/principal/index.mako" /> + +<%def name="context_menu_items()"> + ${parent.context_menu_items()} + % if master.has_perm('download_permissions_matrix'): +
  • ${h.link_to("Download Permissions Matrix", url('roles.download_permissions_matrix'))}
  • + % endif + + + +${parent.body()} diff --git a/tailbone/views/roles.py b/tailbone/views/roles.py index d0dd8967..613576e6 100644 --- a/tailbone/views/roles.py +++ b/tailbone/views/roles.py @@ -26,12 +26,16 @@ Role Views from __future__ import unicode_literals, absolute_import +import os + import six from sqlalchemy import orm +from openpyxl.styles import Font, PatternFill from rattail.db import model from rattail.db.auth import (has_permission, grant_permission, revoke_permission, administrator_role, guest_role, authenticated_role) +from rattail.excel import ExcelWriter import colander from deform import widget as dfwidget @@ -278,6 +282,69 @@ class RolesView(PrincipalMasterView): roles.append(role) return roles + def download_permissions_matrix(self): + """ + View which renders the complete role / permissions matrix data into an + Excel spreadsheet, and returns that file. + """ + roles = self.Session.query(model.Role)\ + .order_by(model.Role.name)\ + .all() + + permissions = self.get_available_permissions() + + # prep the excel writer + path = os.path.join(self.rattail_config.workdir(), + 'permissions-matrix.xlsx') + writer = ExcelWriter(path, None) + sheet = writer.sheet + + # write header + sheet.cell(row=1, column=1, value="") + for i, role in enumerate(roles, 2): + sheet.cell(row=1, column=i, value=role.name) + + # font and fill pattern for permission group rows + bold = Font(bold=True) + group_fill = PatternFill(patternType='solid', + fgColor='d9d9d9', + bgColor='d9d9d9') + + # now we'll write the rows + writing_row = 2 + for groupkey in sorted(permissions, key=lambda k: permissions[k]['label'].lower()): + group = permissions[groupkey] + + # group label is bold, with fill pattern + cell = sheet.cell(row=writing_row, column=1, value=group['label']) + cell.font = bold + cell.fill = group_fill + + # continue fill pattern for rest of group row + for col, role in enumerate(roles, 2): + cell = sheet.cell(row=writing_row, column=col) + cell.fill = group_fill + + # okay, that row is done + writing_row += 1 + + # now we list each perm in the group + perms = group['perms'] + for key in sorted(perms, key=lambda p: perms[p]['label'].lower()): + sheet.cell(row=writing_row, column=1, value=perms[key]['label']) + + # and show an 'X' for any role which has this perm + for col, role in enumerate(roles, 2): + if has_permission(self.Session(), role, key, include_guest=False): + sheet.cell(row=writing_row, column=col, value="X") + + writing_row += 1 + + writer.auto_resize() + writer.auto_freeze() + writer.save() + return self.file_response(path) + @classmethod def defaults(cls, config): cls._principal_defaults(config) @@ -286,6 +353,8 @@ class RolesView(PrincipalMasterView): @classmethod def _role_defaults(cls, config): + route_prefix = cls.get_route_prefix() + url_prefix = cls.get_url_prefix() permission_prefix = cls.get_permission_prefix() # extra permissions for editing built-in roles etc. @@ -296,6 +365,14 @@ class RolesView(PrincipalMasterView): config.add_tailbone_permission(permission_prefix, '{}.edit_my'.format(permission_prefix), "Edit Role(s) to which current user belongs") + # download permissions matrix + config.add_tailbone_permission(permission_prefix, '{}.download_permissions_matrix'.format(permission_prefix), + "Download complete Role/Permissions matrix") + config.add_route('{}.download_permissions_matrix'.format(route_prefix), '{}/permissions-matrix'.format(url_prefix), + request_method='GET') + config.add_view(cls, attr='download_permissions_matrix', route_name='{}.download_permissions_matrix'.format(route_prefix), + permission='{}.download_permissions_matrix'.format(permission_prefix)) + class PermissionsWidget(dfwidget.Widget): template = 'permissions'