Esempio n. 1
0
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'
Esempio n. 2
0
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)
Esempio n. 3
0
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'
Esempio n. 4
0
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
Esempio n. 5
0
 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()
Esempio n. 6
0
 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()
Esempio n. 7
0
    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
Esempio n. 8
0
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
Esempio n. 9
0
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()
Esempio n. 10
0
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
Esempio n. 11
0
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
Esempio n. 12
0
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
Esempio n. 13
0
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
Esempio n. 14
0
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'
Esempio n. 15
0
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'
Esempio n. 16
0
def load_user(user_id):
    return db_session.query(User).get(user_id)