#!/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 account management. These features include:
* general user account listing
* detailed user account view
* creating new user accounts
* updating existing user accounts
* deleting existing user accounts
* enabling existing user accounts
* disabling existing user accounts
* adding group memberships
* removing group memberships
* 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
import flask_mail
from flask_babel import gettext, lazy_gettext, force_locale
from sqlalchemy import or_, nullslast, nullsfirst
import hawat.const
import hawat.acl
import hawat.menu
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.users.forms import CreateUserAccountForm, UpdateUserAccountForm, AdminUpdateUserAccountForm, \
UserSearchForm
BLUEPRINT_NAME = 'users'
"""Name of the blueprint as module global constant."""
[docs]class ListView(HTMLMixin, SQLAlchemyMixin, ItemListView):
"""
General user account listing.
"""
methods = ['GET']
authentication = True
authorization = [hawat.acl.PERMISSION_POWER]
[docs] @classmethod
def get_view_title(cls, **kwargs):
return lazy_gettext('User management')
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
[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.login.like('%{}%'.format(form_args['search'])),
model.fullname.ilike('%{}%'.format(form_args['search'])),
model.email.ilike('%{}%'.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.is_(True))
elif form_args['state'] == 'disabled':
query = query.filter(model.enabled.is_(False))
# Adjust query based on user role selection.
if 'role' in form_args and form_args['role']:
if form_args['role'] == hawat.const.NO_ROLE:
query = query.filter(model.roles == [])
else:
query = query.filter(model.roles.any(form_args['role']))
# Adjust query based on user membership selection.
if 'membership' in form_args and form_args['membership']:
query = query \
.join(model.memberships) \
.filter(model.memberships.any(id=form_args['membership'].id))
# Adjust query based on user management selection.
if 'management' in form_args and form_args['management']:
query = query \
.join(model.managements) \
.filter(model.managements.any(id=form_args['management'].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()),
'login.desc': lambda x, y: x.order_by(y.login.desc()),
'login.asc': lambda x, y: x.order_by(y.login.asc()),
'fullname.desc': lambda x, y: x.order_by(y.fullname.desc()),
'fullname.asc': lambda x, y: x.order_by(y.fullname.asc()),
'email.desc': lambda x, y: x.order_by(y.email.desc()),
'email.asc': lambda x, y: x.order_by(y.email.asc()),
'logintime.desc': lambda x, y: x.order_by(nullslast(y.logintime.desc())),
'logintime.asc': lambda x, y: x.order_by(nullsfirst(y.logintime.asc())),
}
query = sortmap[form_args['sortby']](query, model)
return query
[docs]class ShowView(HTMLMixin, SQLAlchemyMixin, ItemShowView):
"""
Detailed user account view.
"""
methods = ['GET']
authentication = True
[docs] @classmethod
def get_view_icon(cls):
return 'action-show-user'
[docs] @classmethod
def get_view_title(cls, **kwargs):
return lazy_gettext('Show user account details')
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
[docs] @classmethod
def authorize_item_action(cls, **kwargs):
# Each user must be able to view his/her account.
permission_me = flask_principal.Permission(
flask_principal.UserNeed(kwargs['item'].id)
)
# Managers of the groups the user is member of may view his/her account.
needs = [hawat.acl.ManagementNeed(x.id) for x in kwargs['item'].memberships]
permission_mngr = flask_principal.Permission(*needs)
return (hawat.acl.PERMISSION_POWER.can() or permission_me.can() or
(permission_mngr.can() and len(needs) > 0))
[docs] def do_before_response(self, **kwargs): # pylint: disable=locally-disabled,unused-argument
item = self.response_context['item']
action_menu = hawat.menu.Menu()
action_menu.add_entry(
'endpoint',
'show',
endpoint='groups.show',
hidetitle=True
)
action_menu.add_entry(
'submenu',
'more',
align_right=True,
legend=gettext('More actions')
)
action_menu.add_entry(
'endpoint',
'more.add_member',
endpoint='groups.addmember'
)
action_menu.add_entry(
'endpoint',
'more.reject_member',
endpoint='groups.rejectmember'
)
action_menu.add_entry(
'endpoint',
'more.remove_member',
endpoint='groups.removemember'
)
action_menu.add_entry(
'endpoint',
'more.add_manager',
endpoint='groups.addmanager'
)
action_menu.add_entry(
'endpoint',
'more.remove_manager',
endpoint='groups.removemanager'
)
action_menu.add_entry(
'endpoint',
'more.enable',
endpoint='groups.enable'
)
action_menu.add_entry(
'endpoint',
'more.disable',
endpoint='groups.disable'
)
action_menu.add_entry(
'endpoint',
'more.update',
endpoint='groups.update'
)
self.response_context.update(
context_action_menu_groups=action_menu
)
if self.has_endpoint('changelogs.search'):
self.response_context.update(
context_action_menu_changelogs=self.get_endpoint_class(
'changelogs.search'
).get_context_action_menu()
)
if self.can_access_endpoint('users.update', item=item) and self.has_endpoint('changelogs.search'):
item_changelog_model = self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
item_changelog = self.dbsession.query(item_changelog_model). \
filter(item_changelog_model.model == item.__class__.__name__). \
filter(item_changelog_model.model_id == item.id). \
order_by(item_changelog_model.createtime.desc()). \
limit(100). \
all()
self.response_context.update(
item_changelog=item_changelog
)
user_changelog = self.dbsession.query(item_changelog_model). \
filter(item_changelog_model.author_id == item.id). \
order_by(item_changelog_model.createtime.desc()). \
limit(100). \
all()
self.response_context.update(
user_changelog=user_changelog
)
[docs]class MeView(ShowView): # pylint: disable=locally-disabled,too-many-ancestors
"""
Detailed user account view for currently logged-in user (profile page).
"""
methods = ['GET']
authentication = True
authorization = [hawat.acl.PERMISSION_ANY]
[docs] @classmethod
def get_view_name(cls):
return 'me'
[docs] @classmethod
def get_view_icon(cls):
return 'profile'
[docs] @classmethod
def get_view_url(cls, **kwargs):
return flask.url_for(cls.get_view_endpoint())
[docs] @classmethod
def get_view_title(cls, **kwargs):
return lazy_gettext('My user account')
[docs] @classmethod
def get_view_template(cls):
return '{}/show.html'.format(BLUEPRINT_NAME)
[docs] @classmethod
def authorize_item_action(cls, **kwargs):
return True
[docs] def dispatch_request(self): # pylint: disable=locally-disabled,arguments-differ
"""
Mandatory interface required by the :py:func:`flask.views.View.dispatch_request`.
Will be called by the *Flask* framework to service the request.
Single item with given unique identifier will be retrieved from database
and injected into template to be displayed to the user.
"""
item_id = flask_login.current_user.get_id()
item = self.dbquery().filter(self.dbmodel.id == item_id).first()
if not item:
self.abort(404)
self.response_context.update(
item_id=item_id,
item=item,
breadcrumbs_menu=self.get_breadcrumbs_menu(),
action_menu=self.get_action_menu()
)
self.do_before_response()
return self.generate_response()
[docs]class CreateView(HTMLMixin, SQLAlchemyMixin, ItemCreateView): # pylint: disable=locally-disabled,too-many-ancestors
"""
View for creating new user accounts.
"""
methods = ['GET', 'POST']
authentication = True
authorization = [hawat.acl.PERMISSION_POWER]
[docs] @classmethod
def get_view_icon(cls):
return 'action-create-user'
[docs] @classmethod
def get_view_title(cls, **kwargs):
return lazy_gettext('Create new user account')
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbchlogmodel(self):
return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @staticmethod
def get_message_success(**kwargs):
return gettext(
'User account <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 user account.')
[docs] @staticmethod
def get_message_cancel(**kwargs):
return gettext('Canceled creating new user account.')
[docs]class UpdateView(HTMLMixin, SQLAlchemyMixin, ItemUpdateView): # pylint: disable=locally-disabled,too-many-ancestors
"""
View for updating existing user accounts.
"""
methods = ['GET', 'POST']
authentication = True
[docs] @classmethod
def get_view_icon(cls):
return 'action-update-user'
[docs] @classmethod
def get_view_title(cls, **kwargs):
return lazy_gettext('Update user account details')
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbchlogmodel(self):
return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @classmethod
def authorize_item_action(cls, **kwargs):
permission_me = flask_principal.Permission(
flask_principal.UserNeed(kwargs['item'].id)
)
return hawat.acl.PERMISSION_ADMIN.can() or permission_me.can()
[docs] @staticmethod
def get_message_success(**kwargs):
return gettext(
'User account <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 user account <strong>%(item_id)s</strong>.',
item_id=markupsafe.escape(str(kwargs['item']))
)
[docs] @staticmethod
def get_message_cancel(**kwargs):
return gettext(
'Canceled updating user account <strong>%(item_id)s</strong>.',
item_id=markupsafe.escape(str(kwargs['item']))
)
[docs]class AddMembershipView(HTMLMixin, SQLAlchemyMixin,
ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors
"""
View for adding group memberships.
"""
methods = ['GET', 'POST']
authentication = True
[docs] @classmethod
def get_view_name(cls):
return 'addmembership'
[docs] @classmethod
def get_view_title(cls, **kwargs):
return gettext('Add group membership')
[docs] @classmethod
def get_view_icon(cls):
return 'action-add-member'
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbmodel_other(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['other'].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'].memberships:
return False
return True
[docs] @classmethod
def change_item(cls, **kwargs):
kwargs['item'].memberships.append(kwargs['other'])
if kwargs['item'].is_state_disabled():
kwargs['item'].set_state_enabled()
flask.current_app.send_infomail(
'users.enable',
account=kwargs['item']
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[docs]class RejectMembershipView(HTMLMixin, SQLAlchemyMixin,
ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors
"""
View for rejecting group membership requests.
"""
methods = ['GET', 'POST']
authentication = True
[docs] @classmethod
def get_view_name(cls):
return 'rejectmembership'
[docs] @classmethod
def get_view_title(cls, **kwargs):
return gettext('Reject group membership')
[docs] @classmethod
def get_view_icon(cls):
return 'action-rej-member'
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbmodel_other(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['other'].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'].memberships_wanted:
return False
return True
[docs] @classmethod
def change_item(cls, **kwargs):
kwargs['item'].memberships_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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[docs]class RemoveMembershipView(HTMLMixin, SQLAlchemyMixin,
ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors
"""
View for removing group memberships.
"""
methods = ['GET', 'POST']
authentication = True
[docs] @classmethod
def get_view_name(cls):
return 'removemembership'
[docs] @classmethod
def get_view_title(cls, **kwargs):
return gettext('Remove group membership')
[docs] @classmethod
def get_view_icon(cls):
return 'action-rem-member'
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbmodel_other(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['other'].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'].memberships:
return False
return True
[docs] @classmethod
def change_item(cls, **kwargs):
try:
kwargs['item'].memberships.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 member from group <strong>%(group_id)s</strong>.',
user_id=markupsafe.escape(str(kwargs['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[docs]class AddManagementView(HTMLMixin, SQLAlchemyMixin,
ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors
"""
View for adding group managements.
"""
methods = ['GET', 'POST']
authentication = True
[docs] @classmethod
def get_view_name(cls):
return 'addmanagement'
[docs] @classmethod
def get_view_title(cls, **kwargs):
return gettext('Add group management')
[docs] @classmethod
def get_view_icon(cls):
return 'action-add-manager'
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbmodel_other(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['other'].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'].managements:
return False
return True
[docs] @classmethod
def change_item(cls, **kwargs):
kwargs['item'].managements.append(kwargs['other'])
if kwargs['item'].is_state_disabled():
kwargs['item'].set_state_enabled()
flask.current_app.send_infomail(
'users.enable',
account=kwargs['item']
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[docs]class RemoveManagementView(HTMLMixin, SQLAlchemyMixin,
ItemObjectRelationView): # pylint: disable=locally-disabled,too-many-ancestors
"""
View for removing group managements.
"""
methods = ['GET', 'POST']
authentication = True
[docs] @classmethod
def get_view_name(cls):
return 'removemanagement'
[docs] @classmethod
def get_view_title(cls, **kwargs):
return gettext('Remove group management')
[docs] @classmethod
def get_view_icon(cls):
return 'action-rem-manager'
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbmodel_other(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['other'].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'].managements:
return False
return True
[docs] @classmethod
def change_item(cls, **kwargs):
try:
kwargs['item'].managements.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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[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['item'])),
group_id=markupsafe.escape(str(kwargs['other']))
)
[docs]class EnableView(HTMLMixin, SQLAlchemyMixin, ItemEnableView): # pylint: disable=locally-disabled,too-many-ancestors
"""
View for enabling existing user accounts.
"""
methods = ['GET', 'POST']
authentication = True
authorization = [hawat.acl.PERMISSION_POWER]
[docs] @classmethod
def get_view_icon(cls):
return 'action-enable-user'
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbchlogmodel(self):
return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @staticmethod
def get_message_success(**kwargs):
return gettext(
'User account <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 user account <strong>%(item_id)s</strong>.',
item_id=markupsafe.escape(str(kwargs['item']))
)
[docs] @staticmethod
def get_message_cancel(**kwargs):
return gettext(
'Canceled enabling user account <strong>%(item_id)s</strong>.',
item_id=markupsafe.escape(str(kwargs['item']))
)
[docs] def do_after_action(self, item):
try:
self.inform_user(item)
except ConnectionRefusedError:
# Mail service is probably not configured.
self.logger.error("Unable to send infomail about user account activation.")
[docs]class DisableView(HTMLMixin, SQLAlchemyMixin, ItemDisableView): # pylint: disable=locally-disabled,too-many-ancestors
"""
View for disabling user accounts.
"""
methods = ['GET', 'POST']
authentication = True
authorization = [hawat.acl.PERMISSION_POWER]
[docs] @classmethod
def get_view_icon(cls):
return 'action-disable-user'
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbchlogmodel(self):
return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @staticmethod
def get_message_success(**kwargs):
return gettext(
'User account <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 user account <strong>%(item_id)s</strong>.',
item_id=markupsafe.escape(str(kwargs['item']))
)
[docs] @staticmethod
def get_message_cancel(**kwargs):
return gettext(
'Canceled disabling user account <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 user accounts.
"""
methods = ['GET', 'POST']
authentication = True
authorization = [hawat.acl.PERMISSION_ADMIN]
[docs] @classmethod
def get_view_icon(cls):
return 'action-delete-user'
@property
def dbmodel(self):
return self.get_model(hawat.const.MODEL_USER)
@property
def dbchlogmodel(self):
return self.get_model(hawat.const.MODEL_ITEM_CHANGELOG)
[docs] @staticmethod
def get_message_success(**kwargs):
return gettext(
'User account <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 user account <strong>%(item_id)s</strong>.',
item_id=markupsafe.escape(str(kwargs['item']))
)
[docs] @staticmethod
def get_message_cancel(**kwargs):
return gettext(
'Canceled deleting user account <strong>%(item_id)s</strong>.',
item_id=markupsafe.escape(str(kwargs['item']))
)
# -------------------------------------------------------------------------------
[docs]class UsersBlueprint(HawatBlueprint):
"""Pluggable module - user account management (*users*)."""
[docs] @classmethod
def get_module_title(cls):
return lazy_gettext('User account management')
[docs] def register_app(self, app):
app.menu_main.add_entry(
'view',
'admin.{}'.format(BLUEPRINT_NAME),
position=40,
group=lazy_gettext('Object management'),
view=ListView
)
app.menu_auth.add_entry(
'view',
'my_account',
position=10,
view=MeView,
params=lambda: {'item': flask_login.current_user}
)
app.set_infomailer('users.enable', EnableView.inform_user)
# -------------------------------------------------------------------------------
[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 = UsersBlueprint(
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(MeView, '/me')
hbp.register_view_class(UpdateView, '/<int:item_id>/update')
hbp.register_view_class(AddMembershipView, '/<int:item_id>/add_membership/<int:other_id>')
hbp.register_view_class(RemoveMembershipView, '/<int:item_id>/remove_membership/<int:other_id>')
hbp.register_view_class(RejectMembershipView, '/<int:item_id>/reject_membership/<int:other_id>')
hbp.register_view_class(AddManagementView, '/<int:item_id>/add_management/<int:other_id>')
hbp.register_view_class(RemoveManagementView, '/<int:item_id>/remove_management/<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