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)
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)
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)
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())
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)
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, )
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)