Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move supplier user creation invite emails to notify #747

Merged
merged 6 commits into from
Sep 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def create_app(config_name):

from .main import main as main_blueprint
from .status import status as status_blueprint
from .external.views.external import external as external_blueprint
from dmutils.external import external as external_blueprint

application.register_blueprint(main_blueprint, url_prefix='/suppliers')
application.register_blueprint(status_blueprint, url_prefix='/suppliers')
Expand Down
Empty file removed app/external/__init__.py
Empty file.
Empty file removed app/external/views/__init__.py
Empty file.
18 changes: 0 additions & 18 deletions app/external/views/external.py

This file was deleted.

7 changes: 0 additions & 7 deletions app/main/helpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@
from flask import current_app, flash


def hash_email(email):
m = hashlib.sha256()
m.update(email.encode('utf-8'))

return base64.urlsafe_b64encode(m.digest())


def login_required(func):
@wraps(func)
@flask_login.login_required
Expand Down
11 changes: 6 additions & 5 deletions app/main/views/frameworks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from dmapiclient.audit import AuditTypes
from dmutils.email import send_email
from dmutils.email.exceptions import EmailError
from dmutils.email.helpers import hash_string
from dmcontent.formats import format_service_price
from dmcontent.questions import ContentQuestion
from dmcontent.errors import ContentNotFoundError
Expand All @@ -27,7 +28,7 @@

from ... import data_api_client, flask_featureflags
from ...main import main, content_loader
from ..helpers import hash_email, login_required
from ..helpers import login_required
from ..helpers.frameworks import (
get_declaration_status, get_last_modified_from_first_matching_file, register_interest_in_framework,
get_supplier_on_framework_from_info, get_declaration_status_from_info, get_supplier_framework_info,
Expand Down Expand Up @@ -688,7 +689,7 @@ def framework_updates_email_clarification_question(framework_slug):
extra={'error': six.text_type(e),
'framework': framework['slug'],
'supplier_id': current_user.supplier_id,
'email_hash': hash_email(current_user.email_address)})
'email_hash': hash_string(current_user.email_address)})
abort(503, "Clarification question email failed to send")

if framework['clarificationQuestionsOpen']:
Expand Down Expand Up @@ -720,7 +721,7 @@ def framework_updates_email_clarification_question(framework_slug):
extra={'error': six.text_type(e),
'framework': framework['slug'],
'supplier_id': current_user.supplier_id,
'email_hash': hash_email(current_user.email_address)})
'email_hash': hash_string(current_user.email_address)})
else:
# Do not send confirmation email to the user who submitted the question
# Zendesk will handle this instead
Expand Down Expand Up @@ -868,7 +869,7 @@ def upload_framework_agreement(framework_slug):
"error {error} supplier_id {supplier_id} email_hash {email_hash}",
extra={'error': six.text_type(e),
'supplier_id': current_user.supplier_id,
'email_hash': hash_email(current_user.email_address)})
'email_hash': hash_string(current_user.email_address)})
abort(503, "Framework agreement email failed to send")

return redirect(url_for('.framework_agreement', framework_slug=framework_slug))
Expand Down Expand Up @@ -1067,7 +1068,7 @@ def contract_review(framework_slug, agreement_id):
"error {error} supplier_id {supplier_id} email_hash {email_hash}",
extra={'error': six.text_type(e),
'supplier_id': current_user.supplier_id,
'email_hash': hash_email(current_user.email_address)})
'email_hash': hash_string(current_user.email_address)})
abort(503, "Framework agreement email failed to send")

session.pop('signature_page', None)
Expand Down
59 changes: 14 additions & 45 deletions app/main/views/login.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
from __future__ import absolute_import
import six
from flask_login import current_user
from flask import current_app, flash, redirect, render_template, url_for, abort
from flask import flash, redirect, render_template, url_for, current_app

from dmapiclient.audit import AuditTypes
from dmutils.email import generate_token, send_email
from dmutils.email.exceptions import EmailError
from dmutils.email import send_user_account_email

from .. import main
from ..forms.auth_forms import EmailAddressForm
from ..helpers import hash_email, login_required
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is hash_email used by anything else or could we delete the helper function as well? There's a shared one in dmutils now that we should probably switch to if it is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used in app/main/view/frameworks, but could probable update to use the utils one. I'm going to check they have the same functionality though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, have removed the helper and used the one from utils.

from ..helpers import login_required
from ... import data_api_client


# Any invites sent before the new user-frontend becomes active will be linking to this route. We need to maintain it
# for seven days after the user-frontend goes live.
@main.route('/create-user/<string:encoded_token>', methods=['GET'])
def create_user(encoded_token):
return redirect(url_for('external.create_user', encoded_token=encoded_token), 301)


@main.route('/invite-user', methods=["GET"])
@login_required
def invite_user():
Expand All @@ -36,41 +27,19 @@ def send_invite_user():
form = EmailAddressForm()

if form.validate_on_submit():
token = generate_token(
{
"role": "supplier",
"supplier_id": current_user.supplier_id,
"supplier_name": current_user.supplier_name,
"email_address": form.email_address.data
send_user_account_email(
'supplier',
form.email_address.data,
current_app.config['NOTIFY_TEMPLATES']['invite_contributor'],
extra_token_data={
'supplier_id': current_user.supplier_id,
'supplier_name': current_user.supplier_name
},
current_app.config['SHARED_EMAIL_KEY'],
current_app.config['INVITE_EMAIL_SALT']
personalisation={
'user': current_user.name,
'supplier': current_user.supplier_name
}
)
url = url_for('external.create_user', encoded_token=token, _external=True)
email_body = render_template(
"emails/invite_user_email.html",
url=url,
user=current_user.name,
supplier=current_user.supplier_name)

try:
send_email(
form.email_address.data,
email_body,
current_app.config['DM_MANDRILL_API_KEY'],
current_app.config['INVITE_EMAIL_SUBJECT'],
current_app.config['INVITE_EMAIL_FROM'],
current_app.config['INVITE_EMAIL_NAME'],
["user-invite"]
)
except EmailError as e:
current_app.logger.error(
"Invitation email failed to send. "
"error {error} supplier_id {supplier_id} email_hash {email_hash}",
extra={'error': six.text_type(e),
'supplier_id': current_user.supplier_id,
'email_hash': hash_email(current_user.email_address)})
abort(503, "Failed to send user invite reset")

data_api_client.create_audit_event(
audit_type=AuditTypes.invite_user,
Expand Down
47 changes: 8 additions & 39 deletions app/main/views/suppliers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@

from flask import render_template, request, redirect, url_for, abort, session, Markup, flash
from flask_login import current_user, current_app
import six

from dmapiclient import APIError
from dmapiclient.audit import AuditTypes
from dmutils.email import send_email, generate_token
from dmutils.email.exceptions import EmailError
from dmutils.email import send_user_account_email
from dmutils.email.dm_mailchimp import DMMailChimpClient
from dmcontent.content_loader import ContentNotFoundError

Expand All @@ -20,7 +18,7 @@
CompanyContactDetailsForm, CompanyNameForm, EmailAddressForm
)
from ..helpers.frameworks import get_frameworks_by_status, get_frameworks_closed_and_open_for_applications
from ..helpers import hash_email, login_required
from ..helpers import login_required
from .users import get_current_suppliers_users


Expand Down Expand Up @@ -432,45 +430,16 @@ def submit_company_summary():
session['email_company_name'] = supplier['suppliers']['name']
session['email_supplier_id'] = supplier['suppliers']['id']

token = generate_token(
{
"role": "supplier",
"email_address": account_email_address,
send_user_account_email(
'supplier',
account_email_address,
current_app.config['NOTIFY_TEMPLATES']['create_user_account'],
extra_token_data={
"supplier_id": session['email_supplier_id'],
"supplier_name": session['email_company_name']
},
current_app.config['SHARED_EMAIL_KEY'],
current_app.config['INVITE_EMAIL_SALT']
}
)

url = url_for('external.create_user', encoded_token=token, _external=True)

email_body = render_template(
"emails/create_user_email.html",
company_name=session['email_company_name'],
url=url
)
try:
send_email(
account_email_address,
email_body,
current_app.config['DM_MANDRILL_API_KEY'],
current_app.config['CREATE_USER_SUBJECT'],
current_app.config['RESET_PASSWORD_EMAIL_FROM'],
current_app.config['RESET_PASSWORD_EMAIL_NAME'],
["user-creation"]
)
session['email_sent_to'] = account_email_address
except EmailError as e:
current_app.logger.error(
"suppliercreate.fail: Create user email failed to send. "
"error {error} supplier_id {supplier_id} email_hash {email_hash}",
extra={
'error': six.text_type(e),
'supplier_id': session['email_supplier_id'],
'email_hash': hash_email(account_email_address)})
abort(503, "Failed to send user creation email")

data_api_client.create_audit_event(
audit_type=AuditTypes.invite_user,
object_type='suppliers',
Expand Down
24 changes: 0 additions & 24 deletions app/templates/emails/create_user_email.html

This file was deleted.

24 changes: 0 additions & 24 deletions app/templates/emails/invite_user_email.html

This file was deleted.

23 changes: 18 additions & 5 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ class Config(object):
DM_DATA_API_URL = None
DM_DATA_API_AUTH_TOKEN = None
DM_MANDRILL_API_KEY = None
DM_NOTIFY_API_KEY = None
DM_CLARIFICATION_QUESTION_EMAIL = '[email protected]'
DM_FRAMEWORK_AGREEMENTS_EMAIL = '[email protected]'

NOTIFY_TEMPLATES = {
'create_user_account': '1d1e38a6-744a-4d5a-84af-aefccde70a6c',
'invite_contributor': '1cca85e8-d647-46e6-9c0d-6af90b9e69b0'
}

DM_AGREEMENTS_BUCKET = None
DM_COMMUNICATIONS_BUCKET = None
DM_DOCUMENTS_BUCKET = None
Expand All @@ -39,10 +45,6 @@ class Config(object):

DEBUG = False

RESET_PASSWORD_EMAIL_NAME = 'Digital Marketplace Admin'
RESET_PASSWORD_EMAIL_FROM = '[email protected]'
RESET_PASSWORD_EMAIL_SUBJECT = 'Reset your Digital Marketplace password'

INVITE_EMAIL_NAME = 'Digital Marketplace Admin'
INVITE_EMAIL_FROM = '[email protected]'
INVITE_EMAIL_SUBJECT = 'Your Digital Marketplace invitation'
Expand All @@ -56,7 +58,6 @@ class Config(object):

DM_GENERIC_NOREPLY_EMAIL = '[email protected]'

CREATE_USER_SUBJECT = 'Create your Digital Marketplace account'
SECRET_KEY = None
SHARED_EMAIL_KEY = None
RESET_PASSWORD_SALT = 'ResetPasswordSalt'
Expand Down Expand Up @@ -100,6 +101,8 @@ class Test(Config):
WTF_CSRF_ENABLED = False
SERVER_NAME = 'localhost'
DM_MANDRILL_API_KEY = 'MANDRILL'
DM_NOTIFY_API_KEY = "not_a_real_key-00000000-fake-uuid-0000-000000000000"

SHARED_EMAIL_KEY = "KEY"
DM_CLARIFICATION_QUESTION_EMAIL = '[email protected]'

Expand Down Expand Up @@ -137,6 +140,8 @@ class Development(Config):
DM_ASSETS_URL = "https://{}.s3-eu-west-1.amazonaws.com".format(DM_SUBMISSIONS_BUCKET)

DM_MANDRILL_API_KEY = "not_a_real_key"
DM_NOTIFY_API_KEY = "not_a_real_key-00000000-fake-uuid-0000-000000000000"

SHARED_EMAIL_KEY = "very_secret"
SECRET_KEY = 'verySecretKey'

Expand All @@ -162,6 +167,14 @@ class Preview(Live):
class Production(Live):
FEATURE_FLAGS_CONTRACT_VARIATION = enabled_since('2016-08-23')

NOTIFY_TEMPLATES = {
'create_user_account': '84f5d812-df9d-4ab8-804a-06f64f5abd30',
'invite_contributor': '5eefe42d-1694-4388-8908-991cdfba0a71'
}

# Check we didn't forget any live template IDs
assert NOTIFY_TEMPLATES.keys() == Config.NOTIFY_TEMPLATES.keys()


class Staging(Production):
FEATURE_FLAGS_CONTRACT_VARIATION = enabled_since('2016-08-22')
Expand Down
2 changes: 1 addition & 1 deletion requirements-app.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ Flask==0.10.1
Flask-Login==0.2.11
Flask-WTF==0.12

git+https://github.com/alphagov/digitalmarketplace-utils.git@28.1.0#egg=digitalmarketplace-utils==28.1.0
git+https://github.com/alphagov/digitalmarketplace-utils.git@28.5.0#egg=digitalmarketplace-utils==28.5.0
git+https://github.com/alphagov/[email protected]#egg=digitalmarketplace-content-loader==4.2.0
git+https://github.com/alphagov/[email protected]#egg=digitalmarketplace-apiclient==10.4.0
Loading