class InstitutionModelView(AdminModelView): """ Institution model view. """ column_list = ['name', 'key', 'parent'] column_default_sort = 'name' column_labels = { 'users': 'Members', } column_formatters = { 'parent': lambda vw, ctx, model, prop: model.parent.name if model.parent else None } form_columns = ['parent', 'name', 'key', 'users'] form_overrides = { 'key': KeyField } form_args = { 'parent': dict( get_label='name', query_factory=lambda: db_session.query(Institution).order_by('name'), ), 'users': dict( get_label='email', query_factory=lambda: db_session.query(User).order_by('email'), ), } create_template = 'institution_create.html' edit_template = 'institution_edit.html'
def consent(): """ Implements the consent provider component of the Hydra consent workflow. Hydra redirects to this endpoint based on the ``URLS_CONSENT`` environment variable configured on the Hydra server. """ try: challenge = request.args.get('consent_challenge') consent_request = hydra_admin.get_consent_request(challenge) user_id = consent_request['subject'] user = db_session.query(User).get(user_id) access_info = get_access_info(user, consent_request['requested_scope']) user_profile = get_user_profile(user) consent_params = { 'grant_scope': consent_request['requested_scope'], 'grant_audience': consent_request['requested_access_token_audience'], 'access_token_data': access_info.dict(), 'id_token_data': user_profile.dict(), } redirect_to = hydra_admin.accept_consent_request( challenge, **consent_params) return redirect(redirect_to) except HydraAdminError as e: return hydra_error_page(e)
class MemberModelView(AdminModelView): """ Member model view. Used for assigning capabilities to members - effectively creating/deleting privilege records. """ can_create = False can_delete = False column_list = ['user.email', 'institution.name', 'capabilities'] column_default_sort = [('user.email', False), ('institution.name', False)] column_labels = { 'user.email': 'User', 'institution.name': 'Institution', } column_formatters = { 'capabilities': lambda vw, ctx, model, prop: ', '.join(sorted([c.label for c in model.capabilities])) } form_columns = ['capabilities'] form_args = { 'capabilities': dict( get_label='label', query_factory=lambda: db_session.query(Capability).join(Scope).join(Role).order_by(Scope.key, Role.name), ) } edit_template = 'member_edit.html'
def validate_auto_login(user_id): """ Validate a login request for which Hydra has indicated that the user is already authenticated, returning the user object on success. An ``ODPIdentityError`` is raised if the login cannot be permitted for any reason. :param user_id: the user id :return: a User object :raises ODPUserNotFound: if the user account associated with this id has been deleted :raises ODPAccountLocked: if the user account has been temporarily locked :raises ODPAccountDisabled: if the user account has been deactivated :raises ODPEmailNotVerified: if the user changed their email address since their last login, but have not yet verified it """ user = db_session.query(User).get(user_id) if not user: raise x.ODPUserNotFound if is_account_locked(user): raise x.ODPAccountLocked if not user.active: raise x.ODPAccountDisabled if not user.verified: raise x.ODPEmailNotVerified return user
def delete_view(self): id = request.form.get('id') if id is not None: user = db_session.query(User).get(id) if user and user.superuser and not current_user.superuser: flash("Only superusers may perform this action.") return redirect(get_redirect_target()) return super().delete_view()
def edit_view(self): id = get_mdict_item_or_list(request.args, 'id') if id is not None: user = db_session.query(User).get(id) if user and user.superuser and not current_user.superuser: flash("Only superusers may perform this action.") return redirect(get_redirect_target()) return super().edit_view()
def is_accessible(self): if not current_user.is_authenticated: return False if current_user.superuser: return True # TODO: cache the result of this query; it's called repeatedly admin_privilege = db_session.query(Privilege).filter_by(user_id=current_user.id) \ .join(Institution, Privilege.institution_id == Institution.id).filter_by(key=current_app.config['ADMIN_INSTITUTION']) \ .join(Role, Privilege.role_id == Role.id).filter_by(key=current_app.config['ADMIN_ROLE']) \ .join(Scope, Privilege.scope_id == Scope.id).filter_by(key=current_app.config['ADMIN_SCOPE']) \ .one_or_none() return admin_privilege is not None
def validate_email_verification(email): """ Validate an email verification. :param email: the user's email address :return: a User object :raises ODPUserNotFound: if the email address is not associated with any user account """ user = db_session.query(User).filter_by(email=email).first() if not user: raise x.ODPUserNotFound return user
class UserModelView(AdminModelView): """ User model view. """ can_create = False # users may only be created via signup action_disallowed_list = ['delete' ] # disallow deletion of multiple users at once column_list = [ 'id', 'email', 'verified', 'active', 'superuser', 'institutions' ] column_default_sort = 'email' column_formatters = { 'institutions': lambda vw, ctx, model, prop: ', '.join( sorted([i.name for i in model.institutions])) } form_columns = ['email', 'active', 'institutions'] form_args = { 'institutions': dict( get_label='name', query_factory=lambda: db_session.query(Institution).order_by('name' ), ) } edit_template = 'user_edit.html' @expose('/edit/', methods=('GET', 'POST')) def edit_view(self): id = get_mdict_item_or_list(request.args, 'id') if id is not None: user = db_session.query(User).get(id) if user and user.superuser and not current_user.superuser: flash("Only superusers may perform this action.") return redirect(get_redirect_target()) return super().edit_view() @expose('/delete/', methods=('POST', )) def delete_view(self): id = request.form.get('id') if id is not None: user = db_session.query(User).get(id) if user and user.superuser and not current_user.superuser: flash("Only superusers may perform this action.") return redirect(get_redirect_target()) return super().delete_view()
def validate_user_login(email, password): """ Validate the credentials supplied by a user via the login form, returning the user object on success. An ``ODPIdentityError`` is raised if the login cannot be permitted for any reason. :param email: the input email address :param password: the input plain-text password :return: a User object :raises ODPUserNotFound: if the email address is not associated with any user account :raises ODPAccountLocked: if the user account has been temporarily locked :raises ODPIncorrectPassword: if the password is incorrect :raises ODPAccountDisabled: if the user account has been deactivated :raises ODPEmailNotVerified: if the email address has not yet been verified """ user = db_session.query(User).filter_by(email=email).first() if not user: raise x.ODPUserNotFound # first check whether the account is currently locked and should still be locked, unlocking it if necessary if is_account_locked(user): raise x.ODPAccountLocked # check the password before checking further account properties, to minimize the amount of knowledge # a potential attacker can gain about an account try: ph.verify(user.password, password) # if argon2_cffi's password hashing defaults have changed, we rehash the user's password if ph.check_needs_rehash(user.password): user.password = ph.hash(password) db_session.add(user) db_session.commit() except VerifyMismatchError: if lock_account(user): raise x.ODPAccountLocked raise x.ODPIncorrectPassword if not user.active: raise x.ODPAccountDisabled if not user.verified: raise x.ODPEmailNotVerified return user
def validate_user_signup(email, password): """ Validate the credentials supplied by a new user. An ``ODPIdentityError`` is raised if the signup cannot be permitted for any reason. :param email: the input email address :param password: the input plain-text password :raises ODPEmailInUse: if the email address is already associated with a user account :raises ODPPasswordComplexityError: if the password does not meet the minimum complexity requirements """ user = db_session.query(User).filter_by(email=email).first() if user: raise x.ODPEmailInUse if not check_password_complexity(email, password): raise x.ODPPasswordComplexityError
def validate_password_reset(email, password): """ Validate a new password set by the user. :param email: the user's email address :param password: the new password :return: a User object :raises ODPUserNotFound: if the email address is not associated with any user account :raises ODPPasswordComplexityError: if the password does not meet the minimum complexity requirements """ user = db_session.query(User).filter_by(email=email).first() if not user: raise x.ODPUserNotFound if not check_password_complexity(email, password): raise x.ODPPasswordComplexityError return user
def validate_forgot_password(email): """ Validate that a forgotten password request is for a valid email address. :param email: the input email address :return: a User object :raises ODPUserNotFound: if there is no user account for the given email address :raises ODPAccountLocked: if the user account has been temporarily locked :raises ODPAccountDisabled: if the user account has been deactivated """ user = db_session.query(User).filter_by(email=email).first() if not user: raise x.ODPUserNotFound if is_account_locked(user): raise x.ODPAccountLocked if not user.active: raise x.ODPAccountDisabled return user
class RoleModelView(SysAdminModelView): """ Role model view. """ column_list = ['name', 'key', 'scopes'] column_default_sort = 'name' column_formatters = { 'scopes': lambda vw, ctx, model, prop: ', '.join(sorted([s.key for s in model.scopes])) } form_columns = ['name', 'key', 'scopes'] form_overrides = { 'key': KeyField } form_args = { 'scopes': dict( get_label='key', query_factory=lambda: db_session.query(Scope).order_by('key'), ) } create_template = 'role_create.html' edit_template = 'role_edit.html'
class ScopeModelView(SysAdminModelView): """ Scope model view. """ column_list = ['key', 'description', 'roles'] column_default_sort = 'key' column_formatters = { 'roles': lambda vw, ctx, model, prop: ', '.join( sorted([r.name for r in model.roles])) } form_columns = ['key', 'description', 'roles'] form_args = { 'key': dict(filters=[lambda s: s.strip() if s else s]), 'roles': dict( get_label='name', query_factory=lambda: db_session.query(Role).order_by('name'), ) } create_template = 'scope_create.html' edit_template = 'scope_edit.html'
def load_user(user_id): return db_session.query(User).get(user_id)