def test_a_unsuccessful_send_to_pub_sub_with_exception(self): with self.app.app_context(): future = MagicMock() future.result.side_effect = Exception("bad") publisher = MagicMock() publisher.publish.return_value = future notify = NotifyController() notify.publisher = publisher with self.assertRaises(NotifyError): notify.request_to_notify( email="*****@*****.**", template_name="request_password_change", personalisation=None)
def send_create_account_email(email): """Sends an email through GovNotify to the specified address with an encoded link to verify their email :param email: The email address to send to """ url_safe_serializer = URLSafeSerializer(app.config["SECRET_KEY"]) response = uaa_controller.get_user_by_email(email) if response is None: return render_template("request-new-account-error.html") if response["totalResults"] == 0: internal_url = app.config["RESPONSE_OPERATIONS_UI_URL"] verification_url = f"{internal_url}/account/create-account/{token_decoder.generate_email_token(email)}" logger.info("Sending create account email", verification_url=verification_url) personalisation = {"CREATE_ACCOUNT_URL": verification_url, "EMAIL": email} try: NotifyController().request_to_notify( email=email, template_name="request_create_account", personalisation=personalisation ) except NotifyError as e: logger.error("Error sending create account request email to Notify Gateway", msg=e.description) return render_template("request-new-account-error.html") logger.info("Successfully sent create account request email", encoded_email=url_safe_serializer.dumps(email)) else: logger.info( "Requested account creation for email already in UAA", encoded_email=url_safe_serializer.dumps(email) ) return render_template("request-new-account-exists.html", email=email) return render_template("request-new-account-check-email.html", email=email)
def send_update_account_email(token_dict, first_name): """Sends an email through GovNotify to the specified address with an encoded link to verify their email when it has been changed :param token_dict: A dictionary containing the email address to send to and user id :param first_name: the name of the user the email is being sent to, used in email """ response = uaa_controller.get_user_by_email(token_dict["email"]) if response is None: return render_template("request-new-account-error.html") if response["totalResults"] == 0: internal_url = app.config["RESPONSE_OPERATIONS_UI_URL"] verification_url = ( f"{internal_url}/account/verify-email/{token_decoder.generate_email_token(json.dumps(token_dict))}" ) logger.info("Sending update account verification email", verification_url=verification_url) personalisation = {"CONFIRM_EMAIL_URL": verification_url, "first_name": first_name} NotifyController().request_to_notify( email=token_dict["email"], template_name="update_email", personalisation=personalisation ) else: url_safe_serializer = URLSafeSerializer(app.config["SECRET_KEY"]) logger.info( "Requested account creation for email already in UAA", encoded_email=url_safe_serializer.dumps(token_dict["email"]), ) flash("Email already in use", category="error")
def test_a_successful_send_to_pub_sub(self): with self.app.app_context(): publisher = MagicMock() publisher.topic_path.return_value = 'projects/test-project-id/topics/ras-rm-notify-test' notify = NotifyController() notify.publisher = publisher result = notify.request_to_notify( email='*****@*****.**', template_name='request_password_change', personalisation=None) data = b'{"notify": {"email_address": "*****@*****.**", ' \ b'"template_id": "request_password_change_id", "personalisation": {}}}' publisher.publish.assert_called() publisher.publish.assert_called_with( 'projects/test-project-id/topics/ras-rm-notify-test', data=data) self.assertIsNone(result)
def test_an_unsuccessful_send(self): with responses.RequestsMock() as rsps: rsps.add(rsps.POST, url_send_notify, json={'emailAddress': '*****@*****.**'}, status=500) with self.app.app_context(): with self.assertRaises(NotifyError): NotifyController().request_to_notify( email='*****@*****.**', template_name='request_password_change')
def send_confirm_created_email(email, first_name): personalisation = {"FIRST_NAME": first_name} try: NotifyController().request_to_notify( email=email, template_name="confirm_create_account", personalisation=personalisation ) except NotifyError as e: # This shouldn't show the client an error - the account creation was still successful. # They just won't get a confirmation email logger.error("Error sending account creation confirmation email to Notify Gateway", msg=e.description)
def test_a_successful_send_to_pub_sub_with_personalisation(self): with self.app.app_context(): publisher = MagicMock() publisher.topic_path.return_value = "projects/test-project-id/topics/ras-rm-notify-test" notify = NotifyController() notify.publisher = publisher personalisation = { "first_name": "firstname", "last_name": "surname" } result = notify.request_to_notify( email="*****@*****.**", template_name="request_password_change", personalisation=personalisation) data = ( b'{"notify": {"email_address": "*****@*****.**", ' b'"template_id": "request_password_change_id", "personalisation": {"first_name": "firstname", ' b'"last_name": "surname"}}}') publisher.publish.assert_called() publisher.publish.assert_called_with( "projects/test-project-id/topics/ras-rm-notify-test", data=data) self.assertIsNone(result)
def send_confirm_change_email(email): user = uaa_controller.get_user_by_email(email) first_name = user['resources'][0]['name']['givenName'] if first_name != "": personalisation = {'FIRST_NAME': first_name} try: NotifyController().request_to_notify( email=email, template_name='confirm_password_change', personalisation=personalisation) except NotifyError as e: # This shouldn't show the client an error - the password change was still successful. # They just won't get a confirmation email logger.error( 'Error sending password change confirmation email to Notify Gateway', msg=e.description)
def test_a_successful_send(self): with responses.RequestsMock() as rsps: rsps.add(rsps.POST, url_send_notify, json={'emailAddress': '*****@*****.**'}, status=201) with self.app.app_context(): try: NotifyController().request_to_notify( email='*****@*****.**', template_name='request_password_change') except NotifyError: self.fail('NotifyController didnt properly handle a 201') except KeyError: self.fail( 'NotifyController couldnt find the template ID request_password_change' )
def change_password(): form = ChangePasswordFrom() user_id = session["user_id"] user_from_uaa = uaa_controller.get_user_by_id(user_id) if request.method == "POST" and form.validate(): password = form["password"].data new_password = form["new_password"].data if new_password == password: return render_template( "account/change-password.html", form=form, errors={"new_password": ["Your new password is the same as your old password"]}, ) logger.info("Sending account password acknowledgement email", user_id=user_id) personalisation = {"first_name": user_from_uaa["name"]["givenName"]} uaa_errors = uaa_controller.update_user_password(user_from_uaa, password, new_password) if uaa_errors is None: try: NotifyController().request_to_notify( email=user_from_uaa["emails"][0]["value"], # it's safe to assume that zeroth element is primary in # RAS/RM case template_name="update_account_password", personalisation=personalisation, ) except NotifyError as e: logger.error( "Error sending change of password acknowledgement email to Notify Gateway", msg=e.description ) flash("We were unable to send the password change acknowledgement email.", category="warn") flash("Your password has been changed", category="successful_signout") return redirect(url_for("logout_bp.logout")) else: logger.error("Error changing user password", msg=uaa_errors) if uaa_errors["status_code"] == 401: flash( "your current password is incorrect. Please re-enter a correct current password.", category="error", ) else: flash( "Something went wrong while updating your username. Please try again.", category="error", ) errors = form.errors return render_template("account/change-password.html", form=form, errors=errors)
def change_username(): form = UsernameChangeForm() user_id = session["user_id"] user_from_uaa = uaa_controller.get_user_by_id(user_id) username = user_from_uaa["userName"] username_exists = False if request.method == "POST" and form.validate(): if form["username"].data != username: user_from_uaa["userName"] = form["username"].data logger.info("Sending update account details email", user_id=user_id) personalisation = { "first_name": user_from_uaa["name"]["givenName"], "value_name": "username", "changed_value": form["username"].data, } try: NotifyController().request_to_notify( email=user_from_uaa["emails"][0]["value"], template_name="update_account_details", personalisation=personalisation, ) uaa_errors = uaa_controller.update_user_account(user_from_uaa) if uaa_errors is None: flash("Your username has been changed", category="successful_signout") return redirect(url_for("logout_bp.logout")) elif uaa_errors["status_code"] == 400: username_exists = True else: logger.error("Error changing user information", msg=uaa_errors) flash( "Something went wrong. Please ignore the email you have received and try again", category="error", ) except NotifyError as e: logger.error( "Error sending change of username acknowledgement email to Notify Gateway", msg=e.description ) flash("Something went wrong while updating your username. Please try again", category="error") else: return redirect(url_for("account_bp.get_my_account")) errors = form.errors if username_exists: errors = {"username": [uaa_errors["message"]]} return render_template("account/change-username.html", username=username, form=form, errors=errors)
def send_password_change_email(email): url_safe_serializer = URLSafeSerializer(app.config['SECRET_KEY']) response = uaa_controller.get_user_by_email(email) if response is None: return render_template('forgot-password-error.html') if response['totalResults'] > 0: first_name = response['resources'][0]['name']['givenName'] internal_url = app.config['RESPONSE_OPERATIONS_UI_URL'] verification_url = f'{internal_url}/passwords/reset-password/{token_decoder.generate_email_token(email)}' logger.info('Sending password change email', verification_url=verification_url) personalisation = { 'RESET_PASSWORD_URL': verification_url, 'FIRST_NAME': first_name } try: NotifyController().request_to_notify( email=email, template_name='request_password_change', personalisation=personalisation) except NotifyError as e: logger.error( 'Error sending password change request email to Notify Gateway', msg=e.description) return render_template('forgot-password-error.html') logger.info('Successfully sent password change request email', email=url_safe_serializer.dumps(email)) else: # We still want to render the template for an email without an account to avoid # people fishing for valid emails logger.info('Requested password reset for email not in UAA', email=url_safe_serializer.dumps(email)) return redirect( url_for('passwords_bp.forgot_password_check_email', email=url_safe_serializer.dumps(email)))
def send_password_change_email(email): url_safe_serializer = URLSafeSerializer(app.config["SECRET_KEY"]) response = uaa_controller.get_user_by_email(email) if response is None: return render_template("forgot-password-error.html") if response["totalResults"] > 0: first_name = response["resources"][0]["name"]["givenName"] internal_url = app.config["RESPONSE_OPERATIONS_UI_URL"] verification_url = f"{internal_url}/passwords/reset-password/{token_decoder.generate_email_token(email)}" logger.info("Sending password change email", verification_url=verification_url) personalisation = { "RESET_PASSWORD_URL": verification_url, "FIRST_NAME": first_name } try: NotifyController().request_to_notify( email=email, template_name="request_password_change", personalisation=personalisation) except NotifyError as e: logger.error( "Error sending password change request email to Notify Gateway", msg=e.description) return render_template("forgot-password-error.html") logger.info("Successfully sent password change request email", email=url_safe_serializer.dumps(email)) else: # We still want to render the template for an email without an account to avoid # people fishing for valid emails logger.info("Requested password reset for email not in UAA", email=url_safe_serializer.dumps(email)) return redirect( url_for("passwords_bp.forgot_password_check_email", email=url_safe_serializer.dumps(email)))
def change_account_name(): form = ChangeAccountName() user_id = session["user_id"] user_from_uaa = uaa_controller.get_user_by_id(user_id) user = { "first_name": f"{user_from_uaa['name']['givenName']}", "last_name": f"{user_from_uaa['name']['familyName']}", } if request.method == "POST" and form.validate(): if (form.data["first_name"] != user["first_name"]) or (form.data["last_name"] != user["last_name"]): user_from_uaa["name"] = {"familyName": form.data["last_name"], "givenName": form.data["first_name"]} full_name = f"{form.data['first_name']} {form.data['last_name']}" logger.info("Sending update account details email", user_id=user_id) personalisation = { "first_name": user["first_name"], "value_name": "name", "changed_value": full_name, } try: NotifyController().request_to_notify( email=user_from_uaa["emails"][0]["value"], template_name="update_account_details", personalisation=personalisation, ) errors = uaa_controller.update_user_account(user_from_uaa) if errors is None: flash("Your name has been changed", category="successful_signout") return redirect(url_for("logout_bp.logout")) else: logger.error("Error changing user information", msg=errors) flash( "Something went wrong. Please ignore the email you have received and try again", category="error", ) except NotifyError as e: logger.error("Error sending change of name acknowledgement email to Notify Gateway", msg=e.description) flash("Something went wrong while updating your name. Please try again", category="error") else: return redirect(url_for("account_bp.get_my_account")) return render_template("account/change-account-name.html", user=user, form=form, errors=form.errors)
def change_email(): url_safe_serializer = URLSafeSerializer(app.config["SECRET_KEY"]) form = ChangeEmailForm() user_id = session["user_id"] user_from_uaa = uaa_controller.get_user_by_id(user_id) if request.method == "POST" and form.validate(): if form.data["email_address"] != user_from_uaa["emails"][0]["value"]: personalisation = { "first_name": user_from_uaa["name"]["givenName"], "value_name": "email", "changed_value": form.data["email_address"], } try: logger.info("Sending verification email", email=obfuscate_email(form.data["email_address"])) token_dict = {"email": form.data["email_address"], "user_id": user_id} send_update_account_email(token_dict, user_from_uaa["name"]["givenName"]) logger.info("Sending notification email") NotifyController().request_to_notify( email=user_from_uaa["emails"][0]["value"], template_name="update_account_details", personalisation=personalisation, ) logger.info( "Successfully sent email change email", encoded_email=url_safe_serializer.dumps(form.data["email_address"]), ) flash( "A verification email has been sent. You will need to login to change your email", category="successful_signout", ) return redirect(url_for("logout_bp.logout")) except NotifyError as e: logger.error("Error sending 'email change' email to Notify Gateway", msg=e.description) flash("Something went wrong while updating your email. Please try again", category="error") else: return redirect(url_for("account_bp.get_my_account")) return render_template("account/change-email.html", form=form, errors=form.errors)
def test_an_invalid_template_id(self): with self.app.app_context(): with self.assertRaises(KeyError): NotifyController().request_to_notify( email='*****@*****.**', template_name='fake-template-name')
def test_an_invalid_template_id(self): with self.app.app_context(): with self.assertRaises(KeyError): notify = NotifyController() notify._get_template_id(template_name="invalid_template")