Beispiel #1
0
    def create(cls, data, **kwargs):
        """User record creation.

        :param cls - class object
        :param data - dictionary representing a user record
        """
        with db.session.begin_nested():
            email = data.pop('email', None)
            roles = data.pop('roles', None)
            cls._validate(data=data)
            password = data.pop('password', data.get('birth_date', '123456'))
            user = BaseUser(password=hash_password(password),
                            profile=data,
                            active=True)
            db.session.add(user)
            profile = user.profile
            for field in cls.profile_fields:
                value = data.get(field)
                if value is not None:
                    if field == 'birth_date':
                        value = datetime.strptime(value, '%Y-%m-%d')
                    setattr(profile, field, value)
            # send the reset password notification for new users
            if email:
                user.email = email
            db.session.merge(user)
        db.session.commit()
        if user.email:
            send_reset_password_instructions(user)
        confirm_user(user)
        return cls(user)
def user_reset_password():
    api_key, err_response = auth_request(db)
    if err_response:
        return err_response
    user = api_key.user
    send_reset_password_instructions(user)
    return 'ok'
Beispiel #3
0
    def create(cls,
               data,
               id_=None,
               delete_pid=False,
               dbcommit=False,
               reindex=False,
               email_notification=True,
               **kwargs):
        """Patron record creation.

        :param cls - class object
        :param data - dictionary representing a library user
        :param id_ - UUID, it would be generated if it is not given
        :param delete_pid - remove the pid present in the data if True
        :param dbcommit - commit the changes in the db after the creation
        :param reindex - index the record after the creation
        :param email_notification - send a reset password link to the user
        """
        # remove spaces
        data = trim_barcode_for_record(data=data)
        # synchronize the rero id user profile data
        user = cls.sync_user_and_profile(data)
        try:
            record = super(Patron, cls).create(data, id_, delete_pid, dbcommit,
                                               reindex, **kwargs)
            record._update_roles()
        except Exception as e:
            db.session.rollback()
            raise e
        if user:
            # send the reset password notification
            if (email_notification and data.get('email')):
                send_reset_password_instructions(user)
            confirm_user(user)
        return record
    def test_valid_submit(self, user, api_client, password_resets, outbox,
                          templates):
        send_reset_password_instructions(user)
        token = password_resets[0]['token']

        r = api_client.post(url_for('security.reset_password', token=token),
                            data=dict(newPassword='******',
                                      confirmNewPassword='******'))
        assert r.status_code == 200
        # user should be logged in
        assert 'user' in r.json
        assert 'token' in r.json
        assert current_user == user

        assert len(outbox) == len(templates) == 2
        # first email is for the valid reset request
        assert templates[
            0].template.name == 'security/email/reset_instructions.html'
        assert templates[0].context.get('reset_link')
        # second email is to notify of the changed password
        assert templates[1].template.name == 'security/email/reset_notice.html'

        # make sure the password got updated in the database
        api_client.logout()
        assert isinstance(current_user._get_current_object(), AnonymousUser)
        api_client.login_with_creds(user.email, 'new password')
        assert current_user == user
def reset_password(token):
    """View function that handles a reset password request."""

    expired, invalid, user = reset_password_token_status(token)

    if invalid:
        return redirect(url_for('frontend.forgot_password') + '?invalid')
    elif expired:
        send_reset_password_instructions(user)
        return redirect(url_for('frontend.forgot_password') + '?expired')
    elif request.method == 'GET':
        return redirect(url_for('frontend.reset_password', token=token))

    form = _security.reset_password_form()

    if form.validate_on_submit():
        after_this_request(_commit)
        update_password(user, form.newPassword.data)
        login_user(user)
    else:
        return jsonify({'errors': form.errors}), HTTPStatus.BAD_REQUEST

    return jsonify({
        'token': user.get_auth_token(),
        'user': user,
    })
    def test_token_expired(self, user, client, password_resets, outbox,
                           templates):
        import time

        send_reset_password_instructions(user)
        assert len(password_resets) == 1
        token = password_resets[0]['token']

        time.sleep(1)

        r = client.get(url_for('security.reset_password', token=token))
        assert r.status_code == 302
        assert r.path == url_for('frontend.forgot_password')
        assert r.query == 'expired'

        assert len(outbox) == len(templates) == 2
        # first email is for the valid reset request
        assert templates[
            0].template.name == 'security/email/reset_instructions.html'
        assert templates[0].context.get('reset_link')
        # second email is with a new token
        assert templates[
            1].template.name == 'security/email/reset_instructions.html'
        assert templates[1].context.get('reset_link')
        assert templates[0].context.get(
            'reset_link') != templates[1].context.get('reset_link')
Beispiel #7
0
    def reset_user_password(self, id: int, user: UserModel):
        """API endpoint to reset the user's current password, cookies and auth tokens, and to email a password reset link to the user.

        .. :quickref: User; Password reset

        Reset the user's password, and send them instructions on how to reset the password.
        This endpoint is useful from a security standpoint, in case of worries the password might be compromised.
        It sets the current password to something random, invalidates cookies and auth tokens,
        and also sends an email for resetting the password to the user.

        Users can reset their own passwords. Only admins can use this endpoint to reset passwords of other users.

        :reqheader Authorization: The authentication token
        :reqheader Content-Type: application/json
        :resheader Content-Type: application/json
        :status 200: PROCESSED
        :status 400: INVALID_REQUEST, REQUIRED_INFO_MISSING, UNEXPECTED_PARAMS
        :status 401: UNAUTHORIZED
        :status 403: INVALID_SENDER
        :status 422: UNPROCESSABLE_ENTITY
        """
        set_random_password(user)
        remove_cookie_and_token_access(user)
        send_reset_password_instructions(user)

        # commit only if sending instructions worked, as well
        db.session.commit()
    def test_http_get_redirects_to_frontend_form(self, user, client, password_resets):
        send_reset_password_instructions(user)
        assert len(password_resets) == 1
        token = password_resets[0]['token']

        r = client.get(url_for('security.reset_password', token=token))
        assert r.status_code == 302
        assert r.path == url_for('frontend.reset_password', token=token)
def forgot_password():
    """View function that handles a forgotten password request."""
    form = _security.forgot_password_form(MultiDict(request.get_json()))

    if form.validate_on_submit():
        send_reset_password_instructions(form.user)
    else:
        return jsonify({'errors': form.errors}), HTTPStatus.BAD_REQUEST

    return '', HTTPStatus.NO_CONTENT
Beispiel #10
0
def reset_password(user_id: int, user: UserModel):
    """
    Reset the user's current password, cookies and auth tokens.
    Send a password reset link to the user.
    """
    set_random_password(user)
    remove_cookie_and_token_access(user)
    send_reset_password_instructions(user)

    # commit only if sending instructions worked, as well
    db.session.commit()
Beispiel #11
0
    def editor_method_posthook(self, form):
        '''
        send new users a link to set their password

        :param form: edit form
        :return: None
        '''
        action = get_request_action(form)
        if action == 'create':
            user = User.query.filter_by(id=self.created_id).one()
            send_reset_password_instructions(user)
Beispiel #12
0
def reset_password(user):
    """
    Reset the user's current password.
    Send a password reset link to the user.
    """
    new_random_password = "".join(
        [random.choice(string.ascii_lowercase) for _ in range(24)])
    update_password(user, new_random_password)

    send_reset_password_instructions(user)

    # commit only if sending instructions worked
    db.session.commit()
Beispiel #13
0
def forgot_password():
    """View function that handles a forgotten password request."""

    form = ForgotPasswordForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        user = _datastore.find_user(**form.to_dict())
        send_reset_password_instructions(user)
        _logger.debug('%s requested to reset their password' % user)
        do_flash(*get_message('PASSWORD_RESET_REQUEST', email=user.email))

    return render_template('security/forgot_password.html',
                           forgot_password_form=form,
                           **_ctx('forgot_password'))
Beispiel #14
0
    def user(self):
        """Invenio user of a patron."""
        email = self.get('email')
        user = _datastore.find_user(email=email)
        if not user:
            password = hash_password(email)

            _datastore.create_user(email=email, password=password)
            _datastore.commit()
            # send password reset
            user = _datastore.find_user(email=email)
            send_reset_password_instructions(user)
            confirm_user(user)
        return user
Beispiel #15
0
def send_bulk_password_reset_links(users_csv):
    """
    Send bulk password reset links for the included emails.  Argument is a CSV,
    which will be read for an email column.
    """
    for row in csv_reader(os.path.join('/noi', users_csv)):
        # TODO filter users we don't have in the DB
        # TODO Make sure we only send bulk password resets to inactive users
        # who have never confirmed their email (even better would be to add an
        # auto-added flag and only send to those, then mark that we've sent
        # them a reset link)
        # TODO specialize the email sent so it's not "reset your password" but
        # "Claim your NoI account" or somesuch, upon which password is set.
        user = User.query_in_deployment().filter_by(email=row['email']).one()
        send_reset_password_instructions(user)
Beispiel #16
0
def send_bulk_password_reset_links(users_csv):
    """
    Send bulk password reset links for the included emails.  Argument is a CSV,
    which will be read for an email column.
    """
    for row in csv_reader(os.path.join('/noi', users_csv)):
        # TODO filter users we don't have in the DB
        # TODO Make sure we only send bulk password resets to inactive users
        # who have never confirmed their email (even better would be to add an
        # auto-added flag and only send to those, then mark that we've sent
        # them a reset link)
        # TODO specialize the email sent so it's not "reset your password" but
        # "Claim your NoI account" or somesuch, upon which password is set.
        user = User.query_in_deployment().filter_by(email=row['email']).one()
        send_reset_password_instructions(user)
Beispiel #17
0
def save_patron(data, record_type, fetcher, minter,
                record_indexer, record_class, parent_pid):
    """Save a record into the db and index it.

    If the user does not exists, it well be created
    and attached to the patron.
    """
    email = data.get('email')
    data = clean_patron_fields(data)
    if email:
        find_user = datastore.find_user(email=email)
        if find_user is None:
            password = hash_password(email)

            datastore.create_user(
                email=email,
                password=password
            )
            datastore.commit()
            # send password reset
            user = datastore.find_user(email=email)
            if user:
                send_reset_password_instructions(user)
                confirm_user(user)

        patron = Patron.get_patron_by_email(email)
        if patron:
            patron = Patron(data, model=patron.model)
            patron.update(data, dbcommit=True, reindex=True)
        else:
            patron = Patron.create(data, dbcommit=True, reindex=True)
        if patron.get('is_patron', False):
            patron.add_role('patrons')
        else:
            patron.remove_role('patrons')

        if patron.get('is_staff', False):
            patron.add_role('staff')
            # TODO: cataloguer role
            patron.add_role('cataloguer')
        else:
            patron.remove_role('cataloguer')
            # TODO: cataloguer role
            patron.remove_role('staff')
        patron.reindex()

    _next = url_for('invenio_records_ui.ptrn', pid_value=patron.pid)
    return _next, patron.persistent_identifier
    def test_submit_errors(self, user, api_client, password_resets):
        send_reset_password_instructions(user)
        token = password_resets[0]['token']

        r = api_client.post(url_for('security.reset_password', token=token))
        assert r.status_code == 400
        assert 'newPassword' in r.errors
        assert 'confirmNewPassword' in r.errors

        r = api_client.post(url_for('security.reset_password', token=token),
                            data=dict(newPassword='******',
                                      confirmNewPassword='******'))
        assert r.status_code == 400
        assert 'newPassword' in r.errors
        assert 'Password must be at least 8 characters long.' in r.errors['newPassword']

        r = api_client.post(url_for('security.reset_password', token=token),
                            data=dict(newPassword='******',
                                      confirmNewPassword='******'))
        assert r.status_code == 400
        assert 'confirmNewPassword' in r.errors
        assert 'Passwords do not match' in r.errors['confirmNewPassword']
Beispiel #19
0
def reset_password(token):
    """View function that handles a reset password request."""

    next = None
    form = ResetPasswordForm(csrf_enabled=not app.testing)

    if form.validate_on_submit():
        try:
            user = reset_by_token(token=token, **form.to_dict())
            msg = get_message('PASSWORD_RESET')
            next = (get_url(_security.post_reset_view) or
                    get_url(_security.post_login_view))
        except ResetPasswordError, e:
            msg = (str(e), 'error')
            if e.user:
                send_reset_password_instructions(e.user)
                msg = get_message('PASSWORD_RESET_EXPIRED',
                                  within=_security.reset_password_within,
                                  email=e.user.email)
            _logger.debug('Password reset error: ' + msg[0])

        do_flash(*msg)
Beispiel #20
0
    def update(self, data):
        """User record update.

        :param data - dictionary representing a user record to update
        """
        from ..patrons.listener import update_from_profile
        self._validate(data=data)
        email = data.pop('email', None)
        roles = data.pop('roles', None)
        user = self.user
        with db.session.begin_nested():
            if user.profile is None:
                user.profile = UserProfile(user_id=user.id)
            profile = user.profile
            for field in self.profile_fields:
                if field == 'birth_date':
                    setattr(profile, field,
                            datetime.strptime(data.get(field), '%Y-%m-%d'))
                else:
                    setattr(profile, field, data.get(field, ''))

            # change password
            if data.get('password'):
                user.password = hash_password(data['password'])

            # send reset password if email is changed
            if email and email != user.email:
                user.email = email
                send_reset_password_instructions(user)
            # remove the email from user data
            elif not email and user.email:
                user.email = None
            db.session.merge(user)
        db.session.commit()
        confirm_user(user)
        update_from_profile('user', self.user.profile)
        return self
 def test_anonymous_user_required(self, user, client, password_resets):
     send_reset_password_instructions(user)
     token = password_resets[0]['token']
     client.login_user()
     r = client.get(url_for('security.reset_password', token=token))
     assert r.status_code == 403
Beispiel #22
0
def user_create_send_password_reset(mapper, connection, target):
    """Send a password reset to users created without an email."""
    if not target.password:
        target.password = ''.join(
            random.choice(string.printable) for _ in range(20))
        recoverable.send_reset_password_instructions(target)
Beispiel #23
0
 def after_model_change(self, form, User, is_created):
     """Send password instructions if desired."""
     if is_created and form.notification.data is True:
         send_reset_password_instructions(User)
Beispiel #24
0
def add_user():
    """
    Handles POST requests to add a new user to the database. Adds the new user to the database and sends a welcome email as well as
    an email to change the user password.
    @return: Either a JSON with an object of the new user or a HTTP 500 error
    @rtype: JSON
    """
    id = None

    session = db.session()
    try:
        if not 'input-email' in request.form or not isinstance(request.form['input-email'], str):
            raise ValueError("No email provided for adding a user.")

        firstname = request.form['input-firstname'] if 'input-firstname' in request.form else ''
        lastname = request.form['input-lastname'] if 'input-lastname' in request.form else ''
        email = request.form['input-email']
        username = email.split('@')[0]
        if len(username) < 2:
            app.logger.exception("{} {}: Username must be at least two characters long. Make sure to specify a valid email.".format(__file__, __name__))
            return ("Could not add user.", 500)

        password = request.form['input-password'] if 'input-password' in request.form else ''
        roles = request.form.getlist('select-roles') if 'select-roles' in request.form else []
        active = 1 if 'input-active' in request.form else 0

        newUser = addUser(session,
                firstname=firstname,
                lastname=lastname,
                username=username,
                email=email,
                password=password,
                active=active,
                created=datetime.now())
        session.flush()
        id = newUser.id

        for role in roles:
            addRolesUsers(session,
                user_id=id,
                role_id=role
            )
        session.commit()

        role_names = []
        for r in roles:
            role = session.query(Role.name).filter(Role.id == r).first()
            if role is not None:
                role_names.append(role[0])

        send_mail('[SciBib] Welcome!', newUser.email, 'welcome', user=newUser)
        send_reset_password_instructions(newUser)

        app.logger.info("{} {}: Successfully added user {} ({})".format(__file__, __name__, email, id))
        return jsonify({
            'msg': 'User {} successfully added.'.format(username),
            'user': {'id': id,
                    'firstname': firstname,
                     'lastname': lastname,
                     'email': email,
                     'username': username,
                     'roles': role_names,
                     'active': active}
        })
    except Exception as ex:
        app.logger.exception("{} {}: Exception occurred while trying to add user:"******"Could not add user: {}".format(ex), 500)
    finally:
        session.close()
Beispiel #25
0
 def after_model_change(self, form, model, is_created):
     if is_created:
         send_reset_password_instructions(model)
Beispiel #26
0
def user_create_send_password_reset(mapper, connection, target):
    """Send a password reset to users created without an email."""
    if not target.password:
        target.password = ''.join(
            random.choice(string.printable) for _ in range(20))
        recoverable.send_reset_password_instructions(target)
 def after_model_change(self, form, User, is_created):
     """Send password instructions if desired."""
     if is_created and form.notification.data is True:
         send_reset_password_instructions(User)