Source code for hawat.blueprints.groups

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------
# This file is part of Mentat system (https://mentat.cesnet.cz/).
#
# Copyright (C) since 2011 CESNET, z.s.p.o (http://www.ces.net/)
# Use of this source is governed by the MIT license, see LICENSE file.
# -------------------------------------------------------------------------------


"""
This file contains pluggable module for Hawat web interface containing features
related to user group management. These features include:

* general group listing
* detailed group view
* creating new groups
* updating existing groups
* deleting existing groups
* enabling existing groups
* disabling existing groups
* adding group members
* removing group members
* rejecting group membership requests
"""

__author__ = "Jan Mach <jan.mach@cesnet.cz>"
__credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>"

import markupsafe
import flask
import flask_login
import flask_principal
from flask_babel import gettext, lazy_gettext

from sqlalchemy import and_, or_

from mentat.datatype.sqldb import SettingsReportingModel, FilterModel, NetworkModel, ItemChangeLogModel
from mentat.const import tr_

import hawat.const
import hawat.acl
import hawat.menu
from hawat.utils import URLParamsBuilder
from hawat.base import HawatBlueprint
from hawat.view import ItemListView, ItemShowView, ItemCreateView, ItemUpdateView, ItemDeleteView, ItemEnableView, \
    ItemDisableView, ItemObjectRelationView
from hawat.view.mixin import HTMLMixin, SQLAlchemyMixin
from hawat.blueprints.groups.forms import AdminCreateGroupForm, AdminUpdateGroupForm, UpdateGroupForm, GroupSearchForm

BLUEPRINT_NAME = 'groups'
"""Name of the blueprint as module global constant."""


[docs]class ListView(HTMLMixin, SQLAlchemyMixin, ItemListView): """ General group listing. """ methods = ['GET'] authentication = True authorization = [hawat.acl.PERMISSION_POWER]
[docs] @classmethod def get_view_title(cls, **kwargs): return lazy_gettext('Group management')
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP)
[docs] @classmethod def get_action_menu(cls): action_menu = hawat.menu.Menu() action_menu.add_entry( 'endpoint', 'create', endpoint='groups.create', resptitle=True ) return action_menu
[docs] @classmethod def get_context_action_menu(cls): action_menu = hawat.menu.Menu() action_menu.add_entry( 'endpoint', 'show', endpoint='groups.show', hidetitle=True ) action_menu.add_entry( 'endpoint', 'update', endpoint='groups.update', hidetitle=True ) action_menu.add_entry( 'endpoint', 'disable', endpoint='groups.disable', hidetitle=True ) action_menu.add_entry( 'endpoint', 'enable', endpoint='groups.enable', hidetitle=True ) action_menu.add_entry( 'endpoint', 'delete', endpoint='groups.delete', hidetitle=True ) return action_menu
[docs] @staticmethod def get_search_form(request_args): """ Must return instance of :py:mod:`flask_wtf.FlaskForm` appropriate for searching given type of items. """ return GroupSearchForm( request_args, meta={'csrf': False} )
[docs] @staticmethod def build_query(query, model, form_args): # Adjust query based on text search string. if 'search' in form_args and form_args['search']: query = query \ .filter( or_( model.name.like('%{}%'.format(form_args['search'])), model.description.like('%{}%'.format(form_args['search'])), ) ) # Adjust query based on lower time boundary selection. if 'dt_from' in form_args and form_args['dt_from']: query = query.filter(model.createtime >= form_args['dt_from']) # Adjust query based on upper time boundary selection. if 'dt_to' in form_args and form_args['dt_to']: query = query.filter(model.createtime <= form_args['dt_to']) # Adjust query based on user state selection. if 'state' in form_args and form_args['state']: if form_args['state'] == 'enabled': query = query.filter(model.enabled == True) elif form_args['state'] == 'disabled': query = query.filter(model.enabled == False) # Adjust query based on record source selection. if 'source' in form_args and form_args['source']: query = query \ .filter(model.source == form_args['source']) # Adjust query based on user membership selection. if 'member' in form_args and form_args['member']: query = query \ .join(model.members) \ .filter(model.members.any(id=form_args['member'].id)) # Adjust query based on user membership selection. if 'manager' in form_args and form_args['manager']: query = query \ .join(model.managers) \ .filter(model.managers.any(id=form_args['managers'].id)) if 'sortby' in form_args and form_args['sortby']: sortmap = { 'createtime.desc': lambda x, y: x.order_by(y.createtime.desc()), 'createtime.asc': lambda x, y: x.order_by(y.createtime.asc()), 'name.desc': lambda x, y: x.order_by(y.name.desc()), 'name.asc': lambda x, y: x.order_by(y.name.asc()) } query = sortmap[form_args['sortby']](query, model) return query
[docs]class ShowView(HTMLMixin, SQLAlchemyMixin, ItemShowView): """ Detailed group view. """ methods = ['GET'] authentication = True
[docs] @classmethod def get_menu_legend(cls, **kwargs): if isinstance(kwargs['item'], cls.get_model(hawat.const.MODEL_GROUP)): return lazy_gettext( 'View details of group &quot;%(item)s&quot;', item=markupsafe.escape(str(kwargs['item'])) ) return lazy_gettext( 'View details of group &quot;%(item)s&quot;', item=markupsafe.escape(str(kwargs['item'].group)) )
[docs] @classmethod def get_view_title(cls, **kwargs): return lazy_gettext('Show group details')
[docs] @classmethod def get_view_url(cls, **kwargs): if isinstance(kwargs['item'], cls.get_model(hawat.const.MODEL_GROUP)): return flask.url_for( cls.get_view_endpoint(), item_id=kwargs['item'].get_id() ) return flask.url_for( cls.get_view_endpoint(), item_id=kwargs['item'].group.get_id() )
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP)
[docs] @classmethod def authorize_item_action(cls, **kwargs): permission_mm = flask_principal.Permission( hawat.acl.MembershipNeed(kwargs['item'].id), hawat.acl.ManagementNeed(kwargs['item'].id) ) return hawat.acl.PERMISSION_POWER.can() or permission_mm.can()
[docs] @classmethod def get_action_menu(cls): action_menu = hawat.menu.Menu() action_menu.add_entry( 'endpoint', 'update', endpoint='groups.update', resptitle=True ) action_menu.add_entry( 'endpoint', 'disable', endpoint='groups.disable', resptitle=True ) action_menu.add_entry( 'endpoint', 'enable', endpoint='groups.enable', resptitle=True ) action_menu.add_entry( 'endpoint', 'delete', endpoint='groups.delete', resptitle=True ) return action_menu
[docs] def do_before_response(self, **kwargs): # pylint: disable=locally-disabled,no-self-use,unused-argument action_menu = hawat.menu.Menu() action_menu.add_entry( 'endpoint', 'show', endpoint='users.show', hidetitle=True, ) action_menu.add_entry( 'submenu', 'more', align_right=True, legend=gettext('More actions') ) action_menu.add_entry( 'endpoint', 'more.add_membership', endpoint='users.addmembership' ) action_menu.add_entry( 'endpoint', 'more.reject_membership', endpoint='users.rejectmembership' ) action_menu.add_entry( 'endpoint', 'more.remove_membership', endpoint='users.removemembership' ) action_menu.add_entry( 'endpoint', 'more.add_management', endpoint='users.addmanagement' ) action_menu.add_entry( 'endpoint', 'more.remove_management', endpoint='users.removemanagement' ) action_menu.add_entry( 'endpoint', 'more.enable', endpoint='users.enable' ) action_menu.add_entry( 'endpoint', 'more.disable', endpoint='users.disable' ) action_menu.add_entry( 'endpoint', 'more.update', endpoint='users.update' ) self.response_context.update(context_action_menu_users=action_menu) action_menu = hawat.menu.Menu() action_menu.add_entry( 'endpoint', 'show', endpoint='networks.show', hidetitle=True, ) self.response_context.update(context_action_menu_networks=action_menu) action_menu = hawat.menu.Menu() action_menu.add_entry( 'endpoint', 'show', endpoint='filters.show', hidetitle=True, ) self.response_context.update(context_action_menu_filters=action_menu) item = self.response_context['item'] if self.can_access_endpoint('groups.update', item=item) and self.has_endpoint('changelogs.search'): self.response_context.update( context_action_menu_changelogs=self.get_endpoint_class( 'changelogs.search' ).get_context_action_menu() ) item_changelog = self.dbsession.query(ItemChangeLogModel). \ filter( or_( # Changelogs related directly to group item. and_( ItemChangeLogModel.model == item.__class__.__name__, ItemChangeLogModel.model_id == item.id ), # Changelogs related to group reporting settings item. and_( ItemChangeLogModel.model == SettingsReportingModel.__name__, ItemChangeLogModel.model_id.in_( self.dbsession.query(SettingsReportingModel.id).filter( SettingsReportingModel.group_id == item.id) ) ), # Changelogs related to all group reporting filters. and_( ItemChangeLogModel.model == FilterModel.__name__, ItemChangeLogModel.model_id.in_( self.dbsession.query(FilterModel.id).filter(FilterModel.group_id == item.id) ) ), # Changelogs related to all group network records. and_( ItemChangeLogModel.model == NetworkModel.__name__, ItemChangeLogModel.model_id.in_( self.dbsession.query(NetworkModel.id).filter(NetworkModel.group_id == item.id) ) ) ) ). \ order_by(ItemChangeLogModel.createtime.desc()). \ limit(100). \ all() self.response_context.update(item_changelog=item_changelog)
[docs]class ShowByNameView(ShowView): # pylint: disable=locally-disabled,too-many-ancestors """ Detailed group view by group name. """
[docs] @classmethod def get_view_name(cls): return 'show_by_name'
[docs] @classmethod def get_view_template(cls): return '{}/show.html'.format(cls.module_name)
@property def search_by(self): return self.dbmodel.name
[docs]class CreateView(HTMLMixin, SQLAlchemyMixin, ItemCreateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for creating new groups. """ methods = ['GET', 'POST'] authentication = True authorization = [hawat.acl.PERMISSION_POWER]
[docs] @classmethod def get_menu_title(cls, **kwargs): return lazy_gettext('Create group')
[docs] @classmethod def get_view_title(cls, **kwargs): return lazy_gettext('Create new group')
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'Group <strong>%(item_id)s</strong> was successfully created.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext('Unable to create new group.')
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext('Canceled creating new group.')
[docs] @staticmethod def get_item_form(item): return AdminCreateGroupForm()
[docs] def do_before_action(self, item): # Create empty reporting settings object and assign it to the group. SettingsReportingModel(group=item)
[docs]class UpdateView(HTMLMixin, SQLAlchemyMixin, ItemUpdateView): # pylint: disable=locally-disabled,too-many-ancestors """ View for updating existing groups. """ methods = ['GET', 'POST'] authentication = True
[docs] @classmethod def get_menu_title(cls, **kwargs): return lazy_gettext('Update')
[docs] @classmethod def get_menu_legend(cls, **kwargs): return lazy_gettext( 'Update details of group &quot;%(item)s&quot;', item=markupsafe.escape(str(kwargs['item'])) )
[docs] @classmethod def get_view_title(cls, **kwargs): return lazy_gettext('Update group details')
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @classmethod def authorize_item_action(cls, **kwargs): permission_m = flask_principal.Permission( hawat.acl.ManagementNeed(kwargs['item'].id) ) return hawat.acl.PERMISSION_POWER.can() or permission_m.can()
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'Group <strong>%(item_id)s</strong> was successfully updated.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext( 'Unable to update group <strong>%(item_id)s</strong>.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext( 'Canceled updating group <strong>%(item_id)s</strong>.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_item_form(item): admin = flask_login.current_user.has_role('admin') if not admin: form = UpdateGroupForm(obj=item) else: form = AdminUpdateGroupForm(db_item_id=item.id, obj=item) return form
[docs]class AddMemberView(HTMLMixin, SQLAlchemyMixin, ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors """ View for adding group members. """ methods = ['GET', 'POST'] authentication = True
[docs] @classmethod def get_view_name(cls): return 'addmember'
[docs] @classmethod def get_view_title(cls, **kwargs): return gettext('Add group member')
[docs] @classmethod def get_view_icon(cls): return 'action-add-member'
[docs] @classmethod def get_menu_legend(cls, **kwargs): return lazy_gettext( 'Add user &quot;%(user_id)s&quot; to group &quot;%(group_id)s&quot;', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG) @property def dbmodel_other(self): return self.get_model(hawat.const.MODEL_USER)
[docs] @classmethod def authorize_item_action(cls, **kwargs): permission_m = flask_principal.Permission( hawat.acl.ManagementNeed(kwargs['item'].id) ) return hawat.acl.PERMISSION_POWER.can() or permission_m.can()
[docs] @classmethod def validate_item_change(cls, **kwargs): # pylint: disable=locally-disabled,unused-argument # Reject item change in case given item is already enabled. if kwargs['other'] in kwargs['item'].members: return False return True
[docs] @classmethod def change_item(cls, **kwargs): kwargs['item'].members.append(kwargs['other']) if kwargs['other'].is_state_disabled(): kwargs['other'].set_state_enabled() flask.current_app.send_infomail( 'users.enable', account=kwargs['other'] )
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'User <strong>%(user_id)s</strong> was successfully added as a member to group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext( 'Unable to add user <strong>%(user_id)s</strong> as a member to group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext( 'Canceled adding user <strong>%(user_id)s</strong> as a member to group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs]class RejectMemberView(HTMLMixin, SQLAlchemyMixin, ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors """ View for rejecting group membership reuests. """ methods = ['GET', 'POST'] authentication = True
[docs] @classmethod def get_view_name(cls): return 'rejectmember'
[docs] @classmethod def get_view_title(cls, **kwargs): return gettext('Reject group member')
[docs] @classmethod def get_view_icon(cls): return 'action-rej-member'
[docs] @classmethod def get_menu_legend(cls, **kwargs): return lazy_gettext( 'Reject user`s &quot;%(user_id)s&quot; membership request for group &quot;%(group_id)s&quot;', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG) @property def dbmodel_other(self): return self.get_model(hawat.const.MODEL_USER)
[docs] @classmethod def authorize_item_action(cls, **kwargs): permission_m = flask_principal.Permission( hawat.acl.ManagementNeed(kwargs['item'].id) ) return hawat.acl.PERMISSION_POWER.can() or permission_m.can()
[docs] @classmethod def validate_item_change(cls, **kwargs): # pylint: disable=locally-disabled,unused-argument # Reject item change in case given item is already enabled. if kwargs['other'] not in kwargs['item'].members_wanted: return False return True
[docs] @classmethod def change_item(cls, **kwargs): kwargs['item'].members_wanted.remove(kwargs['other'])
# ---------------------------------------------------------------------------
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'User`s <strong>%(user_id)s</strong> membership request for group <strong>%(group_id)s</strong> was successfully rejected.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext( 'Unable to reject user`s <strong>%(user_id)s</strong> membership request for group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext( 'Canceled rejecting user`s <strong>%(user_id)s</strong> membership request for group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs]class RemoveMemberView(HTMLMixin, SQLAlchemyMixin, ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors """ View for removing group members. """ methods = ['GET', 'POST'] authentication = True
[docs] @classmethod def get_view_name(cls): return 'removemember'
[docs] @classmethod def get_view_title(cls, **kwargs): return gettext('Remove group member')
[docs] @classmethod def get_view_icon(cls): return 'action-rem-member'
[docs] @classmethod def get_menu_legend(cls, **kwargs): return lazy_gettext( 'Remove user &quot;%(user_id)s&quot; from group &quot;%(group_id)s&quot;', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG) @property def dbmodel_other(self): return self.get_model(hawat.const.MODEL_USER)
[docs] @classmethod def authorize_item_action(cls, **kwargs): permission_m = flask_principal.Permission( hawat.acl.ManagementNeed(kwargs['item'].id) ) return hawat.acl.PERMISSION_POWER.can() or permission_m.can()
[docs] @classmethod def validate_item_change(cls, **kwargs): # pylint: disable=locally-disabled,unused-argument # Reject item change in case given item is already enabled. if kwargs['other'] not in kwargs['item'].members: return False return True
[docs] @classmethod def change_item(cls, **kwargs): kwargs['item'].members.remove(kwargs['other'])
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'User <strong>%(user_id)s</strong> was successfully removed as a member from group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext( 'Unable to remove user <strong>%(user_id)s</strong> as a member from group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext( 'Canceled removing user <strong>%(user_id)s</strong> as a member from group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs]class AddManagerView(HTMLMixin, SQLAlchemyMixin, ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors """ View for adding group managers. """ methods = ['GET', 'POST'] authentication = True
[docs] @classmethod def get_view_name(cls): return 'addmanager'
[docs] @classmethod def get_view_title(cls, **kwargs): return gettext('Add group manager')
[docs] @classmethod def get_view_icon(cls): return 'action-add-manager'
[docs] @classmethod def get_menu_legend(cls, **kwargs): return lazy_gettext( 'Add user &quot;%(user_id)s&quot; to group &quot;%(group_id)s&quot; as manager', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG) @property def dbmodel_other(self): return self.get_model(hawat.const.MODEL_USER)
[docs] @classmethod def authorize_item_action(cls, **kwargs): permission_m = flask_principal.Permission( hawat.acl.ManagementNeed(kwargs['item'].id) ) return hawat.acl.PERMISSION_POWER.can() or permission_m.can()
[docs] @classmethod def validate_item_change(cls, **kwargs): # pylint: disable=locally-disabled,unused-argument # Reject item change in case given item is already manager. if kwargs['other'] in kwargs['item'].managers: return False return True
[docs] @classmethod def change_item(cls, **kwargs): kwargs['item'].managers.append(kwargs['other']) if kwargs['other'].is_state_disabled(): kwargs['other'].set_state_enabled() flask.current_app.send_infomail( 'users.enable', account=kwargs['other'] )
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'User <strong>%(user_id)s</strong> was successfully added as a manager to group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext( 'Unable to add user <strong>%(user_id)s</strong> as a manager to group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext( 'Canceled adding user <strong>%(user_id)s</strong> as a manager to group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs]class RemoveManagerView(HTMLMixin, SQLAlchemyMixin, ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors """ View for removing group managers. """ methods = ['GET', 'POST'] authentication = True
[docs] @classmethod def get_view_name(cls): return 'removemanager'
[docs] @classmethod def get_view_title(cls, **kwargs): return gettext('Remove group manager')
[docs] @classmethod def get_view_icon(cls): return 'action-rem-manager'
[docs] @classmethod def get_menu_legend(cls, **kwargs): return lazy_gettext( 'Remove user &quot;%(user_id)s&quot; from group &quot;%(group_id)s&quot; as manager', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG) @property def dbmodel_other(self): return self.get_model(hawat.const.MODEL_USER)
[docs] @classmethod def authorize_item_action(cls, **kwargs): permission_m = flask_principal.Permission( hawat.acl.ManagementNeed(kwargs['item'].id) ) return hawat.acl.PERMISSION_POWER.can() or permission_m.can()
[docs] @classmethod def validate_item_change(cls, **kwargs): # pylint: disable=locally-disabled,unused-argument # Reject item change in case given item is not already manager. if kwargs['other'] not in kwargs['item'].managers: return False return True
[docs] @classmethod def change_item(cls, **kwargs): try: kwargs['item'].managers.remove(kwargs['other']) except ValueError: pass
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'User <strong>%(user_id)s</strong> was successfully removed as a manager from group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext( 'Unable to remove user <strong>%(user_id)s</strong> as a manager from group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext( 'Canceled removing user <strong>%(user_id)s</strong> as a manager from group <strong>%(group_id)s</strong>.', user_id=markupsafe.escape(str(kwargs['other'])), group_id=markupsafe.escape(str(kwargs['item'])) )
[docs]class EnableView(HTMLMixin, SQLAlchemyMixin, ItemEnableView): # pylint: disable=locally-disabled,too-many-ancestors """ View for enabling existing groups. """ methods = ['GET', 'POST'] authentication = True authorization = [hawat.acl.PERMISSION_POWER]
[docs] @classmethod def get_menu_legend(cls, **kwargs): return lazy_gettext( 'Enable group &quot;%(item)s&quot;', item=markupsafe.escape(str(kwargs['item'])) )
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'Group <strong>%(item_id)s</strong> was successfully enabled.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext( 'Unable to enable group <strong>%(item_id)s</strong>.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext( 'Canceled enabling group <strong>%(item_id)s</strong>.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs]class DisableView(HTMLMixin, SQLAlchemyMixin, ItemDisableView): # pylint: disable=locally-disabled,too-many-ancestors """ View for disabling groups. """ methods = ['GET', 'POST'] authentication = True authorization = [hawat.acl.PERMISSION_POWER]
[docs] @classmethod def get_menu_legend(cls, **kwargs): return lazy_gettext( 'Disable group &quot;%(item)s&quot;', item=markupsafe.escape(str(kwargs['item'])) )
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'Group <strong>%(item_id)s</strong> was successfully disabled.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext( 'Unable to disable group <strong>%(item_id)s</strong>.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext( 'Canceled disabling group <strong>%(item_id)s</strong>.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs]class DeleteView(HTMLMixin, SQLAlchemyMixin, ItemDeleteView): # pylint: disable=locally-disabled,too-many-ancestors """ View for deleting existing groups. """ methods = ['GET', 'POST'] authentication = True authorization = [hawat.acl.PERMISSION_ADMIN]
[docs] @classmethod def get_menu_legend(cls, **kwargs): return lazy_gettext( 'Delete group &quot;%(item)s&quot;', item=markupsafe.escape(str(kwargs['item'])) )
@property def dbmodel(self): return self.get_model(hawat.const.MODEL_GROUP) @property def dbchlogmodel(self): return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @staticmethod def get_message_success(**kwargs): return gettext( 'Group <strong>%(item_id)s</strong> was successfully and permanently deleted.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_failure(**kwargs): return gettext( 'Unable to delete group <strong>%(item_id)s</strong>.', item_id=markupsafe.escape(str(kwargs['item'])) )
[docs] @staticmethod def get_message_cancel(**kwargs): return gettext( 'Canceled deleting group <strong>%(item_id)s</strong>.', item_id=markupsafe.escape(str(kwargs['item'])) )
# -------------------------------------------------------------------------------
[docs]class GroupsBlueprint(HawatBlueprint): """Pluggable module - user groups (*groups*)."""
[docs] @classmethod def get_module_title(cls): return lazy_gettext('Group management')
[docs] def register_app(self, app): def _fetch_my_groups(): groups = {} for i in list(flask_login.current_user.memberships) + list(flask_login.current_user.managements): groups[str(i)] = i return list(sorted(groups.values(), key=str)) app.menu_main.add_entry( 'view', 'admin.{}'.format(BLUEPRINT_NAME), position=50, view=ListView ) app.menu_auth.add_entry( 'submenudb', 'my_groups', position=20, title=lazy_gettext('My groups'), resptitle=True, icon='module-groups', align_right=True, entry_fetcher=_fetch_my_groups, entry_builder=lambda x, y: hawat.menu.EndpointEntry(x, endpoint='groups.show', params={'item': y}, title=x, icon='module-groups') ) # Register context actions provided by this module. app.set_csag( hawat.const.CSAG_ABUSE, tr_('View details of abuse group <strong>%(name)s</strong>'), ShowByNameView, URLParamsBuilder().add_rule('item_id') )
# -------------------------------------------------------------------------------
[docs]def get_blueprint(): """ Mandatory interface for :py:mod:`hawat.Hawat` and factory function. This function must return a valid instance of :py:class:`hawat.app.HawatBlueprint` or :py:class:`flask.Blueprint`. """ hbp = GroupsBlueprint( BLUEPRINT_NAME, __name__, template_folder='templates', url_prefix='/{}'.format(BLUEPRINT_NAME) ) hbp.register_view_class(ListView, '/list') hbp.register_view_class(CreateView, '/create') hbp.register_view_class(ShowView, '/<int:item_id>/show') hbp.register_view_class(ShowByNameView, '/<item_id>/show_by_name') hbp.register_view_class(UpdateView, '/<int:item_id>/update') hbp.register_view_class(AddMemberView, '/<int:item_id>/add_member/<int:other_id>') hbp.register_view_class(RejectMemberView, '/<int:item_id>/reject_member/<int:other_id>') hbp.register_view_class(RemoveMemberView, '/<int:item_id>/remove_member/<int:other_id>') hbp.register_view_class(AddManagerView, '/<int:item_id>/add_manager/<int:other_id>') hbp.register_view_class(RemoveManagerView, '/<int:item_id>/remove_manager/<int:other_id>') hbp.register_view_class(EnableView, '/<int:item_id>/enable') hbp.register_view_class(DisableView, '/<int:item_id>/disable') hbp.register_view_class(DeleteView, '/<int:item_id>/delete') return hbp