Ejemplo n.º 1
0
 def test_get_respondent_by_email_fail(self):
     with responses.RequestsMock() as rsps:
         rsps.add(rsps.GET, url_get_respondent_email, status=500)
         with app.app_context():
             with self.assertRaises(ApiError):
                 party_controller.get_respondent_by_email(
                     respondent_party['emailAddress'])
Ejemplo n.º 2
0
    def test_get_respondent_by_email_not_found(self):
        with responses.RequestsMock() as rsps:
            rsps.add(rsps.GET, url_get_respondent_email, status=404)
            with app.app_context():
                respondent = party_controller.get_respondent_by_email(
                    respondent_party['emailAddress'])

                self.assertTrue(respondent is None)
def _is_existing_account(respondent_email):
    """
    Checks if the respondent already exists against the email address provided
    :param respondent_email: email of the respondent
    :type respondent_email: str
    :return: returns true if account already registered
    :rtype: bool
    """
    respondent = party_controller.get_respondent_by_email(respondent_email)
    if not respondent:
        return False
    return True
Ejemplo n.º 4
0
    def test_get_respondent_by_email_success(self):
        with responses.RequestsMock() as rsps:
            rsps.add(rsps.GET,
                     url_get_respondent_email,
                     json=respondent_party,
                     status=200)
            with app.app_context():
                respondent = party_controller.get_respondent_by_email(
                    respondent_party['emailAddress'])

                self.assertEqual(respondent['emailAddress'],
                                 respondent_party['emailAddress'])
                self.assertEqual(respondent['firstName'],
                                 respondent_party['firstName'])
Ejemplo n.º 5
0
def get_reset_password(token, form_errors=None):
    form = ResetPasswordForm(request.form)

    try:
        duration = app.config["EMAIL_TOKEN_EXPIRY"]
        email = verification.decode_email_token(token, duration)
        respondent = party_controller.get_respondent_by_email(email)
        db_token = respondent["password_verification_token"]
        if not token == db_token:
            logger.warning("Token not found for respondent",
                           token=token,
                           respondent_id=respondent["id"])
            return render_template("passwords/password-token-not-found.html",
                                   token=token)
    except KeyError:
        logger.warning("Token not found for respondent",
                       token=token,
                       exc_info=True)
        return render_template("passwords/password-token-not-found.html",
                               token=token)
    except SignatureExpired:
        try:
            party_controller.delete_verification_token(token)
        except NotFound:
            return render_template("passwords/password-token-not-found.html",
                                   token=token)
        logger.warning("Token expired for frontstage reset",
                       token=token,
                       exc_info=True)
        return render_template("passwords/password-expired.html", token=token)
    except (BadSignature, BadData):
        logger.warning("Invalid token sent to frontstage password reset",
                       token=token,
                       exc_info=True)
        return render_template("passwords/password-expired.html", token=token)

    template_data = {"error": {"type": form_errors}, "token": token}
    return render_template("passwords/reset-password.html",
                           form=form,
                           data=template_data)
def request_password_change(email):
    respondent = party_controller.get_respondent_by_email(email)

    if not respondent:
        logger.info("Respondent does not exist")
        return redirect(url_for('passwords_bp.reset_password_check_email'))

    party_id = str(respondent['id'])

    logger.info("Requesting password change", party_id=party_id)

    token = verification.generate_email_token(email)

    url_root = request.url_root
    # url_for comes with a leading slash, so strip off the trailing slash in url_root if there is one
    if url_root.endswith('/'):
        url_root = url_root[:-1]
    verification_url = url_root + url_for('passwords_bp.post_reset_password', token=token)

    personalisation = {
        'RESET_PASSWORD_URL': verification_url,
        'FIRST_NAME': respondent['firstName']
    }

    logger.info('Reset password url', url=verification_url, party_id=party_id)

    try:
        NotifyGateway(app.config).request_to_notify(email=email,
                                                    personalisation=personalisation,
                                                    reference=party_id)
        logger.info('Password reset email successfully sent', party_id=party_id)
    except RasNotifyError:
        # Note: intentionally suppresses exception
        logger.error('Error sending request to Notify Gateway', respondent_id=party_id, exc_info=True)

    return redirect(url_for('passwords_bp.reset_password_check_email'))
Ejemplo n.º 7
0
def login():  # noqa: C901
    form = LoginForm(request.form)
    form.username.data = form.username.data.strip()
    account_activated = request.args.get('account_activated', None)

    secure = app.config['WTF_CSRF_ENABLED']

    if request.method == 'POST' and form.validate():
        username = form.username.data
        password = request.form.get('password')
        bound_logger = logger.bind(email=obfuscate_email(username))
        bound_logger.info("Attempting to find user in auth service")
        try:
            auth_controller.sign_in(username, password)
        except AuthError as exc:
            error_message = exc.auth_error
            party_json = party_controller.get_respondent_by_email(username)
            party_id = party_json.get('id') if party_json else None
            bound_logger = bound_logger.bind(party_id=party_id)

            if USER_ACCOUNT_LOCKED in error_message:  # pylint: disable=no-else-return
                if not party_id:
                    bound_logger.error("Respondent account locked in auth but doesn't exist in party")
                    return render_template('sign-in/sign-in.html', form=form, data={"error": {"type": "failed"}})
                bound_logger.info('User account is locked on the Auth server', status=party_json['status'])
                if party_json['status'] == 'ACTIVE' or party_json['status'] == 'CREATED':
                    notify_party_and_respondent_account_locked(respondent_id=party_id,
                                                               email_address=username,
                                                               status='SUSPENDED')
                return render_template('sign-in/sign-in.account-locked.html', form=form)
            elif NOT_VERIFIED_ERROR in error_message:
                bound_logger.info('User account is not verified on the Auth server')
                return render_template('sign-in/sign-in.account-not-verified.html', party_id=party_id)
            elif BAD_AUTH_ERROR in error_message:
                bound_logger.info('Bad credentials provided')
            elif UNKNOWN_ACCOUNT_ERROR in error_message:
                bound_logger.info('User account does not exist in auth service')
            else:
                bound_logger.error('Unexpected error was returned from Auth service', auth_error=error_message)

            return render_template('sign-in/sign-in.html', form=form, data={"error": {"type": "failed"}}, next=request.args.get('next'))

        bound_logger.info("Successfully found user in auth service.  Attempting to find user in party service")
        party_json = party_controller.get_respondent_by_email(username)
        if not party_json or 'id' not in party_json:
            bound_logger.error("Respondent has an account in auth but not in party")
            return render_template('sign-in/sign-in.html', form=form, data={"error": {"type": "failed"}})
        party_id = party_json['id']
        bound_logger = bound_logger.bind(party_id=party_id)

        if request.args.get('next'):
            response = make_response(redirect(request.args.get('next')))
        else:
            response = make_response(redirect(url_for('surveys_bp.get_survey_list', tag='todo', _external=True,
                                                      _scheme=getenv('SCHEME', 'http'))))

        bound_logger.info("Successfully found user in party service")
        bound_logger.info('Creating session')
        session = Session.from_party_id(party_id)
        response.set_cookie('authorization',
                            value=session.session_key,
                            expires=session.get_expires_in(),
                            secure=secure,
                            httponly=secure)
        count = conversation_controller.get_message_count_from_api(session)
        session.set_unread_message_total(count)
        bound_logger.info('Successfully created session', session_key=session.session_key)
        return response

    template_data = {
        "error": {
            "type": form.errors,
            "logged_in": "False"
        },
        'account_activated': account_activated
    }
    if request.args.get('next'):
        return render_template('sign-in/sign-in.html', form=form, data=template_data,
                               next=request.args.get('next'))
    return render_template('sign-in/sign-in.html', form=form, data=template_data)
Ejemplo n.º 8
0
def login():
    form = LoginForm(request.form)
    form.username.data = form.username.data.strip()
    account_activated = request.args.get('account_activated', None)

    if request.method == 'POST' and form.validate():
        username = form.username.data
        password = request.form.get('password')

        party_json = party_controller.get_respondent_by_email(username)
        if not party_json or 'id' not in party_json:
            logger.info(
                'Respondent not able to sign in as they don\'t have an active account in the system.'
            )
            return render_template('sign-in/sign-in.html',
                                   form=form,
                                   data={"error": {
                                       "type": "failed"
                                   }})
        party_id = party_json['id']

        try:
            oauth2_token = oauth_controller.sign_in(username, password)
        except OAuth2Error as exc:
            error_message = exc.oauth2_error
            if USER_ACCOUNT_LOCKED in error_message:
                logger.info('User account is locked on the OAuth2 server',
                            party_id=party_id)
                if party_json['status'] == 'ACTIVE' or party_json[
                        'status'] == 'CREATED':
                    notify_party_and_respondent_account_locked(
                        respondent_id=party_id,
                        email_address=username,
                        status='SUSPENDED')
                return render_template('sign-in/sign-in.account-locked.html',
                                       form=form)
            elif BAD_AUTH_ERROR in error_message:
                return render_template('sign-in/sign-in.html',
                                       form=form,
                                       data={"error": {
                                           "type": "failed"
                                       }})
            elif NOT_VERIFIED_ERROR in error_message:
                logger.info(
                    'User account is not verified on the OAuth2 server')
                return render_template(
                    'sign-in/sign-in.account-not-verified.html',
                    party_id=party_id,
                    email=username)
            else:
                logger.info(
                    'OAuth 2 server generated 401 which is not understood',
                    oauth2_error=error_message)
                return render_template('sign-in/sign-in.html',
                                       form=form,
                                       data={"error": {
                                           "type": "failed"
                                       }})

        # Take our raw token and add a UTC timestamp to the expires_at attribute
        data_dict = {**oauth2_token, 'party_id': party_id}
        data_dict_for_jwt_token = timestamp_token(data_dict)
        encoded_jwt_token = encode(data_dict_for_jwt_token)
        response = make_response(
            redirect(
                url_for('surveys_bp.get_survey_list',
                        tag='todo',
                        _external=True,
                        _scheme=getenv('SCHEME', 'http'))))

        session = SessionHandler()
        logger.info('Creating session', party_id=party_id)
        session.create_session(encoded_jwt_token)
        response.set_cookie('authorization',
                            value=session.session_key,
                            expires=data_dict_for_jwt_token['expires_at'])
        logger.info('Successfully created session',
                    party_id=party_id,
                    session_key=session.session_key)
        return response

    template_data = {
        "error": {
            "type": form.errors,
            "logged_in": "False"
        },
        'account_activated': account_activated
    }
    return render_template('sign-in/sign-in.html',
                           form=form,
                           data=template_data)
Ejemplo n.º 9
0
def login():  # noqa: C901
    form = LoginForm(request.form)
    if form.username.data is not None:
        form.username.data = form.username.data.strip()

    if request.method == "POST" and form.validate():
        username = form.username.data
        password = request.form.get("password")
        bound_logger = logger.bind(email=obfuscate_email(username))
        bound_logger.info("Attempting to find user in auth service")
        try:
            auth_controller.sign_in(username, password)
        except AuthError as exc:
            error_message = exc.auth_error
            party_json = party_controller.get_respondent_by_email(username)
            party_id = party_json.get("id") if party_json else None
            bound_logger = bound_logger.bind(party_id=party_id)

            if USER_ACCOUNT_LOCKED in error_message:
                if not party_id:
                    bound_logger.error(
                        "Respondent account locked in auth but doesn't exist in party"
                    )
                    return render_template("sign-in/sign-in.html",
                                           form=form,
                                           data={"error": {
                                               "type": "failed"
                                           }})
                bound_logger.info("User account is locked on the Auth server",
                                  status=party_json["status"])
                if party_json["status"] == "ACTIVE" or party_json[
                        "status"] == "CREATED":
                    notify_party_and_respondent_account_locked(
                        respondent_id=party_id,
                        email_address=username,
                        status="SUSPENDED")
                return render_template("sign-in/sign-in.account-locked.html",
                                       form=form)
            elif NOT_VERIFIED_ERROR in error_message:
                bound_logger.info(
                    "User account is not verified on the Auth server")
                return render_template(
                    "sign-in/sign-in.account-not-verified.html",
                    party_id=party_id)
            elif BAD_AUTH_ERROR in error_message:
                bound_logger.info("Bad credentials provided")
            elif UNKNOWN_ACCOUNT_ERROR in error_message:
                bound_logger.info(
                    "User account does not exist in auth service")
            elif USER_ACCOUNT_DELETED in error_message:
                bound_logger.info("User account is marked for deletion")
            else:
                bound_logger.error(
                    "Unexpected error was returned from Auth service",
                    auth_error=error_message)

            logger.unbind("email")
            return render_template("sign-in/sign-in.html",
                                   form=form,
                                   data={"error": {
                                       "type": "failed"
                                   }})

        bound_logger.info(
            "Successfully found user in auth service.  Attempting to find user in party service"
        )
        party_json = party_controller.get_respondent_by_email(username)
        if not party_json or "id" not in party_json:
            bound_logger.error(
                "Respondent has an account in auth but not in party")
            return render_template("sign-in/sign-in.html",
                                   form=form,
                                   data={"error": {
                                       "type": "failed"
                                   }})
        party_id = party_json["id"]
        bound_logger = bound_logger.bind(party_id=party_id)

        if session.get("next"):
            response = make_response(redirect(session.get("next")))
            session.pop("next")
        else:
            response = make_response(
                redirect(
                    url_for("surveys_bp.get_survey_list",
                            tag="todo",
                            _external=True,
                            _scheme=getenv("SCHEME", "http"))))

        bound_logger.info("Successfully found user in party service")
        bound_logger.info("Creating session")
        redis_session = Session.from_party_id(party_id)
        secure = app.config["WTF_CSRF_ENABLED"]
        response.set_cookie(
            "authorization",
            value=redis_session.session_key,
            expires=redis_session.get_expires_in(),
            secure=secure,
            httponly=secure,
            samesite="strict",
        )
        count = conversation_controller.get_message_count_from_api(
            redis_session)
        redis_session.set_unread_message_total(count)
        bound_logger.info("Successfully created session",
                          session_key=redis_session.session_key)
        bound_logger.unbind("email")
        return response

    account_activated = request.args.get("account_activated", None)
    template_data = {
        "error": {
            "type": form.errors,
            "logged_in": "False"
        },
        "account_activated": account_activated
    }
    return render_template("sign-in/sign-in.html",
                           form=form,
                           data=template_data)
Ejemplo n.º 10
0
def request_password_change(email):
    respondent = party_controller.get_respondent_by_email(email)

    if not respondent:
        logger.info("Respondent does not exist")
        return redirect(url_for("passwords_bp.reset_password_trouble"))

    party_id = str(respondent["id"])
    password_reset_counter = party_controller.get_password_reset_counter(
        party_id)["counter"]

    if password_reset_counter != 0:
        try:
            email = verification.decode_email_token(
                respondent["password_verification_token"],
                app.config["PASSWORD_RESET_ATTEMPTS_TIMEOUT"])
        except SignatureExpired:
            try:
                party_controller.reset_password_reset_counter(party_id)
                password_reset_counter = 0
            except ApiError:
                logger.error("Error resetting password reset counter")
                return redirect(url_for("passwords_bp.reset_password_trouble"))

    if password_reset_counter >= 5:
        logger.error("Password reset attempts exceeded")
        return redirect(
            url_for("passwords_bp.exceeded_number_of_reset_attempts"))

    logger.info("Requesting password change", party_id=party_id)

    token = verification.generate_email_token(email)

    url_root = request.url_root
    # url_for comes with a leading slash, so strip off the trailing slash in url_root if there is one
    if url_root.endswith("/"):
        url_root = url_root[:-1]
    verification_url = url_root + url_for("passwords_bp.post_reset_password",
                                          token=token)

    personalisation = {
        "RESET_PASSWORD_URL": verification_url,
        "FIRST_NAME": respondent["firstName"]
    }

    logger.info("Reset password url", url=verification_url, party_id=party_id)

    party_controller.post_verification_token(email, token)

    try:
        NotifyGateway(app.config).request_to_notify(
            email=email, personalisation=personalisation, reference=party_id)
        logger.info("Password reset email successfully sent",
                    party_id=party_id)
    except RasNotifyError:
        # Note: intentionally suppresses exception
        logger.error("Error sending request to Notify Gateway",
                     respondent_id=party_id,
                     exc_info=True)

    # Get real time counter to check how many attempts are left
    password_reset_counter = party_controller.get_password_reset_counter(
        party_id)["counter"]
    if password_reset_counter == 4:
        flash(message="You have 1 try left to reset your password",
              category="warn")

    return redirect(
        url_for("passwords_bp.reset_password_check_email", token=token))