예제 #1
0
    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)
예제 #2
0
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))
예제 #3
0
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
예제 #4
0
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)
예제 #5
0
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
예제 #6
0
 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)
예제 #7
0
 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
예제 #8
0
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)
예제 #9
0
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
예제 #10
0
    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)
예제 #11
0
    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)
예제 #12
0
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
    )
예제 #13
0
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)
예제 #14
0
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
예제 #15
0
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
예제 #16
0
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
예제 #17
0
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
예제 #18
0
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)
예제 #19
0
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'))
예제 #20
0
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)
예제 #21
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()