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'
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')
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
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()
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)
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()
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'))
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
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)
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']
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)
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
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)
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()
def after_model_change(self, form, model, is_created): if is_created: send_reset_password_instructions(model)