Example #1
0
 def test_get_message_count_unauthorized(self, headers):
     headers.return_value = {"Authorization": "token"}
     session = Session.from_party_id("id")
     with responses.RequestsMock() as rsps:
         rsps.add(rsps.GET, url_get_conversation_count, status=403)
         with app.app_context():
             with self.assertRaises(IncorrectAccountAccessError):
                 conversation_controller.get_message_count_from_api(session)
Example #2
0
    def test_get_message_count_other_error_returns_0(self, headers):
        headers.return_value = {"Authorization": "token"}
        session = Session.from_party_id("id")
        with responses.RequestsMock() as rsps:
            rsps.add(rsps.GET, url_get_conversation_count, status=400)
            with app.app_context():
                count = conversation_controller.get_message_count_from_api(
                    session)

                self.assertEqual(0, count)
Example #3
0
    def test_get_message_count_from_api(self, headers):
        headers.return_value = {'Authorization': "token"}
        session = Session.from_party_id("id")
        with responses.RequestsMock() as rsps:
            rsps.add(rsps.GET,
                     url_get_conversation_count,
                     json=message_count,
                     status=200,
                     headers={'Authorisation': 'token'},
                     content_type='application/json')
            with app.app_context():
                count = conversation_controller.get_message_count_from_api(
                    session)

                self.assertEqual(3, count)
Example #4
0
    def test_get_message_count_from_session_if_persisted_by_api(self):
        session = Session.from_party_id("id")
        session.set_unread_message_total(1)

        session_key = session.session_key

        session_under_test = Session.from_session_key(session_key)
        with responses.RequestsMock() as rsps:
            rsps.add(rsps.GET,
                     url_get_conversation_count,
                     json=message_count,
                     status=200,
                     headers={'Authorisation': 'token'},
                     content_type='application/json')
            with app.app_context():
                count = conversation_controller.get_message_count_from_api(
                    session_under_test)

                self.assertEqual(3,
                                 session_under_test.get_unread_message_count())
Example #5
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)
Example #6
0
def view_conversation(session, thread_id):
    """Endpoint to view conversations by thread_id"""
    party_id = session.get_party_id()
    logger.info("Getting conversation", thread_id=thread_id, party_id=party_id)
    conversation = get_conversation(thread_id)
    # secure message will send category in case the conversation is technical or miscellaneous
    is_survey_category = (
        False if "category" in conversation
        and conversation["category"] in ["TECHNICAL", "MISC"] else True)
    # sets appropriate message category
    category = "SURVEY" if is_survey_category else conversation["category"]
    logger.info("Successfully retrieved conversation",
                thread_id=thread_id,
                party_id=party_id)
    try:
        refined_conversation = [
            refine(message) for message in reversed(conversation["messages"])
        ]
    except KeyError:
        logger.error("Message is missing important data",
                     thread_id=thread_id,
                     party_id=party_id)
        raise

    if refined_conversation[-1]["unread"]:
        remove_unread_label(refined_conversation[-1]["message_id"])

    form = SecureMessagingForm(request.form)
    form.subject.data = refined_conversation[0].get("subject")

    if not conversation["is_closed"]:
        if form.validate_on_submit():
            logger.info("Sending message",
                        thread_id=thread_id,
                        party_id=party_id)
            msg_to = get_msg_to(refined_conversation)
            if is_survey_category:
                send_message(
                    _get_message_json(form,
                                      refined_conversation[0],
                                      msg_to=msg_to,
                                      msg_from=party_id))
            else:
                send_message(
                    _get_non_survey_message_json(form,
                                                 refined_conversation[0],
                                                 msg_to=msg_to,
                                                 msg_from=party_id,
                                                 category=category))
            logger.info("Successfully sent message",
                        thread_id=thread_id,
                        party_id=party_id)
            thread_url = url_for("secure_message_bp.view_conversation",
                                 thread_id=thread_id) + "#latest-message"
            flash(
                Markup(f"Message sent. <a href={thread_url}>View Message</a>"))
            return redirect(
                url_for("secure_message_bp.view_conversation_list"))

    unread_message_count = {
        "unread_message_count": get_message_count_from_api(session)
    }
    survey_name = None
    business_name = None
    if is_survey_category:
        try:
            survey_name = get_survey(
                app.config["SURVEY_URL"], app.config["BASIC_AUTH"],
                refined_conversation[-1]["survey_id"]).get("longName")
        except ApiError as exc:
            logger.info("Failed to get survey name, setting to None",
                        status_code=exc.status_code)
        try:
            business_name = conversation["messages"][-1]["@business_details"][
                "name"]
        except (KeyError, TypeError):
            logger.info("Failed to get business name, setting to None")

    return render_template(
        "secure-messages/conversation-view.html",
        form=form,
        conversation=refined_conversation,
        conversation_data=conversation,
        unread_message_count=unread_message_count,
        survey_name=survey_name,
        business_name=business_name,
        category=category,
    )
Example #7
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)
def view_conversation(session, thread_id):
    """Endpoint to view conversations by thread_id"""
    party_id = session.get_party_id()
    logger.info("Getting conversation", thread_id=thread_id, party_id=party_id)
    conversation = get_conversation(thread_id)
    logger.info('Successfully retrieved conversation',
                thread_id=thread_id,
                party_id=party_id)
    try:
        refined_conversation = [
            refine(message) for message in reversed(conversation['messages'])
        ]
    except KeyError:
        logger.error('Message is missing important data',
                     thread_id=thread_id,
                     party_id=party_id)
        raise

    if refined_conversation[-1]['unread']:
        remove_unread_label(refined_conversation[-1]['message_id'])

    form = SecureMessagingForm(request.form)
    form.subject.data = refined_conversation[0].get('subject')

    if not conversation['is_closed']:
        if form.validate_on_submit():
            logger.info("Sending message",
                        thread_id=thread_id,
                        party_id=party_id)
            msg_to = get_msg_to(refined_conversation)
            send_message(
                _get_message_json(form,
                                  refined_conversation[0],
                                  msg_to=msg_to,
                                  msg_from=party_id))
            logger.info("Successfully sent message",
                        thread_id=thread_id,
                        party_id=party_id)
            thread_url = url_for("secure_message_bp.view_conversation",
                                 thread_id=thread_id) + "#latest-message"
            flash(
                Markup(f'Message sent. <a href={thread_url}>View Message</a>'))
            return redirect(
                url_for('secure_message_bp.view_conversation_list'))

    unread_message_count = {
        'unread_message_count': get_message_count_from_api(session)
    }
    survey_name = None
    try:
        survey_name = get_survey(
            app.config['SURVEY_URL'], app.config['BASIC_AUTH'],
            refined_conversation[-1]['survey_id']).get('longName')
    except ApiError as exc:
        logger.info('Failed to get survey name, setting to None',
                    status_code=exc.status_code)

    business_name = None
    try:
        business_name = conversation['messages'][-1]['@business_details'][
            'name']
    except KeyError:
        logger.info('Failed to get business name, setting to None')

    return render_template('secure-messages/conversation-view.html',
                           form=form,
                           conversation=refined_conversation,
                           conversation_data=conversation,
                           unread_message_count=unread_message_count,
                           survey_name=survey_name,
                           business_name=business_name)