def user_postprocessor_post(result=None, **kw): """Create an User specific POST postprocessor. Accepts a single argument, `result`, which is the dictionary representation of the created instance of the model. """ logger.info('`user_postprocessor_post` used for endpoint') authorization = verify_authorization() role = verify_roles(authorization, ['admin']) """ HACK: We really shouldn't be doing this, however, it's quicker and more straight forward than converting the <dict> to enable dot sytnax that is compatible with Flask-Security """ user = db.session.query(Model).get(result['id']) """ Sends the reset password instructions email for the specified user. :param user: The user to send the instructions to """ token = generate_reset_password_token(user) reset_link = url_for_security('reset_password', token=token, _external=True) send_mail('An administrator has created an account for you', user.email, 'staff', user=user, confirmation_link=reset_link)
def resend_invitation(user_id): """ For the purpose of re-sending a lost "activation" email, this endpoint allows an admin to re-send that message. This is a convenience option in the admin's dashboard (on the individual user's profile) but it is no different than the reset password flow. We simply generate a reset password token and send the email using our "invite" custom template. Something to note: You can instruct your users who might have misplaced their "activation" email to just use the password reset link from the login page. It's the same flow and they can request a new password with their email address. """ user = Users.query.get(user_id) try: link = url_for_security('reset_password', token=generate_reset_password_token(user), _external=True) subject = 'Activate your account for the Health Tracker' if Config.ORG: subject = f'Activate your account for the {Config.ORG} Health Tracker' send_mail(subject, user.email, 'invite_new_user', reset_link=link) flash('User invitation email was sent.', category='success') except Exception as e: app.logger.debug(e) db.session.rollback() flash('User invitation email was not sent, there was an error', category='error') return redirect(url_for('single_user', id=user_id))
def register_user(_confirmation_link_func=None, **user_data): """Register a user.""" confirmation_link_func = _confirmation_link_func or \ default_confirmation_link_func if user_data.get('password') is not None: user_data['password'] = hash_password(user_data['password']) user = current_datastore.create_user(**user_data) current_datastore.commit() token, confirmation_link = None, None if current_security.confirmable and user.confirmed_at is None: token, confirmation_link = confirmation_link_func(user) user_registered.send(current_app._get_current_object(), user=user, confirm_token=token) if security_config_value('SEND_REGISTER_EMAIL'): send_mail(security_config_value('EMAIL_SUBJECT_REGISTER'), user.email, 'welcome', user=user, confirmation_link=confirmation_link) return user
def send_password_reset_notice(user): """Sends the password reset notice email for the specified user. :param user: The user to send the notice to """ if config_value('SEND_PASSWORD_RESET_NOTICE_EMAIL'): send_mail(config_value('EMAIL_SUBJECT_PASSWORD_NOTICE'), user.email, 'reset_notice', user=user)
def register_user(user): """Performs the user registration process. Returns True if the user has been logged in, false otherwise. """ if not _security.confirmable or _security.login_without_confirmation: user.active = True # confirmation token depends on having user.id set, which requires # the user be committed to the database user.save(commit=True) confirmation_link, token = None, None if _security.confirmable: confirmation_link, token = generate_confirmation_link(user) user_registered.send(current_app._get_current_object(), user=user, confirm_token=token) if config_value('SEND_REGISTER_EMAIL'): send_mail(config_value('EMAIL_SUBJECT_REGISTER'), user.email, 'welcome', user=user, confirmation_link=confirmation_link) if not _security.confirmable or _security.login_without_confirmation: login_user(user) # login_user will modify the user object if _security.trackable is set, # but it will not request a session commit itself when it needs it :/ after_this_request(_commit) return True return False
def send_reset_password_instructions(cls, user): """Send email containing instructions to reset password.""" token, reset_link = cls.reset_password_link_func(user) if config_value('SEND_PASSWORD_RESET_EMAIL'): send_mail(config_value('EMAIL_SUBJECT_PASSWORD_RESET'), user.email, 'reset_instructions', user=user, reset_link=reset_link) reset_password_instructions_sent.send( current_app._get_current_object(), user=user, token=token)
def send_confirmation_link(cls, user): """Send confirmation link.""" send_email_enabled = current_security.confirmable and \ config_value('SEND_REGISTER_EMAIL') if send_email_enabled: token, confirmation_link = cls.confirmation_link_func(user) send_mail(config_value('EMAIL_SUBJECT_REGISTER'), user.email, 'confirmation_instructions', user=user, confirmation_link=confirmation_link) return token
def register_user(should_confirm, **kwargs): confirmation_link, token = None, None kwargs['password'] = encrypt_password(kwargs['password']) user = user_datastore.create_user(**kwargs) user_datastore.commit() flash(gettext('User created successfully')) if should_confirm: confirmation_link, token = generate_confirmation_link(user) do_flash(*get_message('CONFIRM_REGISTRATION', email=user.email)) send_mail( config_value('EMAIL_SUBJECT_REGISTER'), user.email, 'welcome', user=user, confirmation_link=confirmation_link) user_registered.send(app, user=user, confirm_token=token)
def system_err(e): """ We're not anticipating full-blown monitoring in the context of running this app, so in the interest of alerting if a 500 err is encountered, we're going to attempt to email the admin with the details of err. """ try: send_mail('The Health Tracker system encountered an error', Config.BOOTSTRAP_EMAIL, 'system_500_email', error=traceback.format_exc()) except Exception as ex: """I mean, if this exception is raised, you got problems""" app.logger.debug(ex) return render_template('500.html'), 500
def send_reset_password_instructions(user): """Sends the reset password instructions email for the specified user. :param user: The user to send the instructions to """ token = generate_reset_password_token(user) reset_link = url_for('browser.reset_password', token=token, _external=True) send_mail(config_value('EMAIL_SUBJECT_PASSWORD_RESET'), user.email, 'reset_instructions', user=user, reset_link=reset_link) reset_password_instructions_sent.send( current_app._get_current_object(), user=user, token=token)
def send_reset_password_instructions(user): """Sends the reset password instructions email for the specified user. :param user: The user to send the instructions to """ token = generate_reset_password_token(user) url_data = urlparse(request.base_url) reset_link = f"{url_data.scheme}://{url_data.netloc}/#resetpass/{token}/" if config_value('SEND_PASSWORD_RESET_EMAIL'): send_mail(config_value('EMAIL_SUBJECT_PASSWORD_RESET'), user.email, 'reset_instructions', user=user, reset_link=reset_link) reset_password_instructions_sent.send( app._get_current_object(), user=user, token=token )
def change_user_password(_reset_password_link_func=None, **user_data): """Change user password.""" reset_password_link_func = _reset_password_link_func or \ default_reset_password_link_func user = user_data['user'] user.password = None if user_data.get('password') is not None: user.password = hash_password(user_data['password']) current_datastore.put(user) if security_config_value('SEND_PASSWORD_CHANGE_EMAIL'): reset_password_link = None if current_security.recoverable: _, reset_password_link = reset_password_link_func(user) subject = security_config_value('EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE') send_mail(subject, user.email, 'change_notice_rest', user=user, reset_password_link=reset_password_link) password_changed.send(current_app._get_current_object(), user=user)
def test_no_email_sender(app): """ Verify that if SECURITY_EMAIL_SENDER is default (which is a local proxy) that send_mail picks up MAIL_DEFAULT_SENDER. """ app.config["MAIL_DEFAULT_SENDER"] = "*****@*****.**" class TestUser(object): def __init__(self, email): self.email = email security = Security() security.init_app(app) with app.app_context(): user = TestUser("*****@*****.**") with app.mail.record_messages() as outbox: send_mail("Test Default Sender", user.email, "welcome", user=user) assert 1 == len(outbox) assert "*****@*****.**" == outbox[0].sender
def test_no_email_sender(app, sqlalchemy_datastore): """Verify that if SECURITY_EMAIL_SENDER is default (which is a local proxy) that send_mail picks up MAIL_DEFAULT_SENDER. """ app.config["MAIL_DEFAULT_SENDER"] = "*****@*****.**" class TestUser: def __init__(self, email): self.email = email security = Security() security.init_app(app, sqlalchemy_datastore) with app.app_context(): app.try_trigger_before_first_request_functions() user = TestUser("*****@*****.**") with app.mail.record_messages() as outbox: send_mail("Test Default Sender", user.email, "welcome", user=user) assert 1 == len(outbox) assert "*****@*****.**" == outbox[0].sender
def test_sender_tuple(app, sqlalchemy_datastore): """Verify that if sender is a (name, address) tuple, in the received email sender is properly formatted as "name <address>" """ app.config["MAIL_DEFAULT_SENDER"] = ("Test User", "*****@*****.**") class TestUser: def __init__(self, email): self.email = email security = Security() security.init_app(app, sqlalchemy_datastore) with app.app_context(): app.try_trigger_before_first_request_functions() user = TestUser("*****@*****.**") with app.mail.record_messages() as outbox: send_mail("Test Tuple Sender", user.email, "welcome", user=user) assert 1 == len(outbox) assert "Test User <*****@*****.**>" == outbox[0].sender
def register_user(**kwargs): if kwargs.pop('random_password', False) or len(kwargs['password']) == 0: kwargs['password'] = _randompassword() # hash password so that we never store the original kwargs['password'] = hash_password(kwargs['password']) # pop ask_confirm value before kwargs is presented to create_user() ask_confirm = kwargs.pop('ask_confirm', False) # generate a User record and commit it kwargs['active'] = True roles = kwargs.get('roles', []) roles_step1 = [db.session.query(Role).filter_by(name=r).first() for r in roles] roles_step2 = [x for x in roles_step1 if x is not None] kwargs['roles'] = roles_step2 kwargs['fs_uniquifier'] = uuid.uuid4().hex user = User(**kwargs) db.session.add(user) db.session.commit() # send confirmation email if we have been asked to if ask_confirm: confirmation_link, token = generate_confirmation_link(user) do_flash(*get_message('CONFIRM_REGISTRATION', email=user.email)) user_registered.send(current_app._get_current_object(), user=user, confirm_token=token) if config_value('SEND_REGISTER_EMAIL'): send_mail(config_value('EMAIL_SUBJECT_REGISTER'), user.email, 'welcome', user=user, confirmation_link=confirmation_link) else: user.confirmed_at = datetime.now() db.session.commit() return user
def change_user_password(_reset_password_link_func=None, **user_data): """Change user password.""" reset_password_link_func = (_reset_password_link_func or default_reset_password_link_func) user = user_data["user"] user.password = None if user_data.get("password") is not None: user.password = hash_password(user_data["password"]) current_datastore.put(user) if security_config_value("SEND_PASSWORD_CHANGE_EMAIL"): reset_password_link = None if current_security.recoverable: _, reset_password_link = reset_password_link_func(user) subject = security_config_value("EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE") send_mail( subject, user.email, "change_notice_rest", user=user, reset_password_link=reset_password_link, ) password_changed.send(current_app._get_current_object(), user=user)
def send_reminder(user_id): """ Should an admin see that a particular employee has not yet provided a reading, the admin may prompt the user for a reading via email. This route will attempt to send a reminder email to the user in question. We make use of the flask-security send_mail function and are providing a custom email template. """ try: user = Users.query.get(user_id) send_mail('You are required to record health readings', user.email, 'send_reminder', link=url_for('new_reading', _external=True)) flash(f'Reminder email sent to { user.email }', category='success') except Exception as e: flash( f'Reminder email was not sent to { user.email }, there was an error. ', category='error') if request.args.get('return'): return redirect(request.args.get('return')) else: return redirect(url_for('home'))
def new_user(): """ new_user is a route used exclusively by system admins to add new users to the system. There is no public registration page for this application (per the flask-security settings), users must be added by an admin. We will prompt for an email, username, and role, then create the user and send an email informing them that they have been added to the system and must change their password. The change password step is required as the temp password we generated for them is never revealed, just hashed and stored to protect the account from un-authorized logins while the confirmation process plays out. """ form = AddUser() if form.validate_on_submit(): new_user = user_datastore.find_user(email=form.email.data) if new_user: flash( 'User with given email address already exists. User not created.', category='error') return redirect(url_for('new_user')) """ Try and create the new user with given email, username, and role. Assign them a temp password. Users should be activated by default but for some reason we needed to manually activate. """ try: new_user = user_datastore.create_user(email=form.email.data, username=form.username.data, password=hash_password( Users.random_password())) role = user_datastore.find_role(form.roles.data) user_datastore.add_role_to_user(new_user, role) user_datastore.activate_user(new_user) db.session.commit() except Exception as e: app.logger.debug(e) db.session.rollback() flash( 'There was an error creating this user. Please try again before reporting.', category='error') return redirect(url_for('new_user')) """ Now that we have a new user, we're going to try and send them their "activation" link via email. We're really just making use of the built-in password reset function, so generate a new reset token and send the mail via the flask-security send_mail func. This sequence makes use of a custom email template. """ try: link = url_for_security( 'reset_password', token=generate_reset_password_token(new_user), _external=True) subject = 'Activate your account for the Health Tracker' if Config.ORG: subject = f'Activate your account for the {Config.ORG} Health Tracker' send_mail(subject, new_user.email, 'invite_new_user', reset_link=link) except Exception as e: db.session.rollback() flash('New user was created but invitation email was not sent.', category='error') return redirect(url_for('new_user')) flash( f'New user "{new_user.username}" was created and invitation email sent.', category='success') return redirect(url_for('new_user')) return render_template('new_user.html', form=form)
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()