Source code for hawat.blueprints.users.forms
#!/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 module contains custom user account management forms for Hawat.
"""
__author__ = "Jan Mach <jan.mach@cesnet.cz>"
__credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>"
import pytz
import sqlalchemy
import wtforms
from wtforms_sqlalchemy.fields import QuerySelectField, QuerySelectMultipleField
from flask_babel import gettext, lazy_gettext
import hawat.db
import hawat.const
import hawat.forms
from hawat.forms import check_login, get_available_groups
from mentat.datatype.sqldb import UserModel
[docs]def check_id_existence(form, field):
"""
Callback for validating user logins during account create action.
"""
try:
hawat.db.db_get().session.query(UserModel). \
filter(UserModel.login == field.data). \
one()
except sqlalchemy.orm.exc.NoResultFound:
return
except: # pylint: disable=locally-disabled,bare-except
pass
raise wtforms.validators.ValidationError(gettext('User account with this login already exists.'))
[docs]def check_id_uniqueness(form, field):
"""
Callback for validating user logins during account update action.
"""
user = hawat.db.db_get().session.query(UserModel). \
filter(UserModel.login == field.data). \
filter(UserModel.id != form.db_item_id). \
all()
if not user:
return
raise wtforms.validators.ValidationError(gettext('User account with this login already exists.'))
[docs]class BaseUserAccountForm(hawat.forms.BaseItemForm):
"""
Class representing base user account form.
"""
fullname = wtforms.StringField(
lazy_gettext('Full name:'),
validators=[
wtforms.validators.DataRequired(),
wtforms.validators.Length(min=3, max=100),
hawat.forms.check_null_character
]
)
email = wtforms.StringField(
lazy_gettext('Email:'),
validators=[
wtforms.validators.DataRequired(),
wtforms.validators.Length(min=3, max=250),
wtforms.validators.Email()
]
)
organization = wtforms.StringField(
lazy_gettext('Home organization:'),
validators=[
wtforms.validators.DataRequired(),
wtforms.validators.Length(min=3, max=250),
hawat.forms.check_null_character
]
)
locale = hawat.forms.SelectFieldWithNone(
lazy_gettext('Prefered locale:'),
validators=[
wtforms.validators.Optional()
],
choices=[('', lazy_gettext('<< no preference >>'))],
filters=[lambda x: x or None],
default=''
)
timezone = hawat.forms.SelectFieldWithNone(
lazy_gettext('Prefered timezone:'),
validators=[
wtforms.validators.Optional(),
],
choices=[('', lazy_gettext('<< no preference >>'))] + list(zip(pytz.common_timezones, pytz.common_timezones)),
filters=[lambda x: x or None],
default=''
)
submit = wtforms.SubmitField(
lazy_gettext('Submit')
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#
# Handle additional custom keywords.
#
# The list of choices for 'locale' attribute comes from outside of the
# form to provide as loose tie as possible to the outer application.
# Another approach would be to load available choices here with:
#
# locales = list(flask.current_app.config['SUPPORTED_LOCALES'].items())
#
# That would mean direct dependency on flask.Flask application.
self.locale.choices[1:] = kwargs['choices_locales']
[docs]class AdminUserAccountForm(BaseUserAccountForm):
"""
Class representing base user account form for admins.
"""
enabled = wtforms.RadioField(
lazy_gettext('State:'),
validators=[
wtforms.validators.InputRequired(),
],
choices=[
(True, lazy_gettext('Enabled')),
(False, lazy_gettext('Disabled'))
],
filters=[hawat.forms.str_to_bool],
coerce=hawat.forms.str_to_bool
)
roles = wtforms.SelectMultipleField(
lazy_gettext('Roles:'),
validators=[
wtforms.validators.Optional()
]
)
memberships = QuerySelectMultipleField(
lazy_gettext('Group memberships:'),
query_factory=get_available_groups
)
managements = QuerySelectMultipleField(
lazy_gettext('Group managements:'),
query_factory=get_available_groups
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#
# Handle additional custom keywords.
#
# The list of choices for 'roles' attribute comes from outside of the
# form to provide as loose tie as possible to the outer application.
# Another approach would be to load available choices here with:
#
# roles = flask.current_app.config['ROLES']
#
# That would mean direct dependency on flask.Flask application.
self.roles.choices = kwargs['choices_roles']
[docs]class CreateUserAccountForm(AdminUserAccountForm):
"""
Class representing user account create form.
"""
login = wtforms.StringField(
lazy_gettext('Login:'),
validators=[
wtforms.validators.DataRequired(),
wtforms.validators.Length(min=3, max=50),
check_login,
check_id_existence,
hawat.forms.check_null_character
]
)
[docs]class UpdateUserAccountForm(BaseUserAccountForm):
"""
Class representing user account update form for regular users.
"""
[docs]class AdminUpdateUserAccountForm(AdminUserAccountForm):
"""
Class representing user account update form for administrators.
"""
login = wtforms.StringField(
lazy_gettext('Login:'),
validators=[
wtforms.validators.DataRequired(),
wtforms.validators.Length(min=3, max=50),
hawat.forms.check_login,
check_id_uniqueness,
hawat.forms.check_null_character
]
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#
# Handle additional custom keywords.
#
# Store the ID of original item in database to enable the ID uniqueness
# check with check_id_uniqueness() validator.
self.db_item_id = kwargs['db_item_id']
[docs]class UserSearchForm(hawat.forms.BaseSearchForm):
"""
Class representing simple user search form.
"""
search = wtforms.StringField(
lazy_gettext('Login, name, email:'),
validators=[
wtforms.validators.Optional(),
wtforms.validators.Length(min=3, max=100),
hawat.forms.check_null_character
],
description=lazy_gettext(
'User`s login, full name or email address. Search is performed even in the middle of the strings, so for example you may lookup by domain.')
)
dt_from = hawat.forms.SmartDateTimeField(
lazy_gettext('Creation time from:'),
validators=[
wtforms.validators.Optional()
],
description=lazy_gettext(
'Lower time boundary for item creation time. Timestamp is expected to be in the format <code>YYYY-MM-DD hh:mm:ss</code> and in the timezone according to the user`s preferences.')
)
dt_to = hawat.forms.SmartDateTimeField(
lazy_gettext('Creation time to:'),
validators=[
wtforms.validators.Optional()
],
description=lazy_gettext(
'Upper time boundary for item creation time. Timestamp is expected to be in the format <code>YYYY-MM-DD hh:mm:ss</code> and in the timezone according to the user`s preferences.')
)
state = wtforms.SelectField(
lazy_gettext('State:'),
validators=[
wtforms.validators.Optional(),
],
choices=[
('', lazy_gettext('Nothing selected')),
('enabled', lazy_gettext('Enabled')),
('disabled', lazy_gettext('Disabled'))
],
default='',
description=lazy_gettext('Search for users with particular account state.')
)
role = wtforms.SelectField(
lazy_gettext('Role:'),
validators=[
wtforms.validators.Optional()
],
default='',
description=lazy_gettext('Search for users with particular role, or without any assigned roles.')
)
membership = QuerySelectField(
lazy_gettext('Group membership:'),
query_factory=get_available_groups,
allow_blank=True,
description=lazy_gettext('Search for users with membership with particular group.')
)
management = QuerySelectField(
lazy_gettext('Group management:'),
query_factory=get_available_groups,
allow_blank=True,
description=lazy_gettext('Search for users with management rights to particular group.')
)
sortby = wtforms.SelectField(
lazy_gettext('Sort result by:'),
validators=[
wtforms.validators.Optional()
],
choices=[
('createtime.desc', lazy_gettext('by creation time descending')),
('createtime.asc', lazy_gettext('by creation time ascending')),
('login.desc', lazy_gettext('by login descending')),
('login.asc', lazy_gettext('by login ascending')),
('fullname.desc', lazy_gettext('by name descending')),
('fullname.asc', lazy_gettext('by name ascending')),
('email.desc', lazy_gettext('by email descending')),
('email.asc', lazy_gettext('by email ascending')),
('logintime.desc', lazy_gettext('by login time descending')),
('logintime.asc', lazy_gettext('by login time ascending')),
],
default='fullname.asc'
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
#
# Handle additional custom keywords.
#
# The list of choices for 'roles' attribute comes from outside of the
# form to provide as loose tie as possible to the outer application.
# Another approach would be to load available choices here with:
#
# roles = flask.current_app.config['ROLES']
#
# That would mean direct dependency on flask.Flask application.
self.role.choices = [
('', lazy_gettext('Nothing selected')),
(hawat.const.NO_ROLE, lazy_gettext('Without any roles'))
] + kwargs['choices_roles']
[docs] @staticmethod
def is_multivalue(field_name):
"""
Check, if given form field is a multivalue field.
:param str field_name: Name of the form field.
:return: ``True``, if the field can contain multiple values, ``False`` otherwise.
:rtype: bool
"""
return False