refactor RegisterFromInvite to make auth_type required, and update test fixtures

This commit is contained in:
Leo Hemsted
2017-11-13 13:39:31 +00:00
parent 79393c97ef
commit 65ba7e88c8
8 changed files with 61 additions and 98 deletions

View File

@@ -171,13 +171,11 @@ class RegisterUserForm(Form):
email_address = email_address() email_address = email_address()
mobile_number = international_phone_number() mobile_number = international_phone_number()
password = password() password = password()
# always register as sms type
auth_type = HiddenField('auth_type', default='sms_auth')
class RegisterUserFromInviteForm(Form): class RegisterUserFromInviteForm(Form):
def __init__(self, auth_type, *args, **kwargs):
self.auth_type = auth_type
super().__init__(*args, **kwargs)
name = StringField( name = StringField(
'Full name', 'Full name',
validators=[DataRequired(message='Cant be empty')] validators=[DataRequired(message='Cant be empty')]
@@ -186,9 +184,10 @@ class RegisterUserFromInviteForm(Form):
password = password() password = password()
service = HiddenField('service') service = HiddenField('service')
email_address = HiddenField('email_address') email_address = HiddenField('email_address')
auth_type = HiddenField('auth_type', validators=[DataRequired()])
def validate_mobile_number(self, field): def validate_mobile_number(self, field):
if self.auth_type == 'sms_auth' and not field.data: if self.auth_type.data == 'sms_auth' and not field.data:
raise ValidationError('Cant be empty') raise ValidationError('Cant be empty')

View File

@@ -43,7 +43,7 @@ def register():
@main.route('/register-from-invite', methods=['GET', 'POST']) @main.route('/register-from-invite', methods=['GET', 'POST'])
def register_from_invite(): def register_from_invite():
invited_user = session.get('invited_user') invited_user = session.get('invited_user')
form = RegisterUserFromInviteForm(invited_user['auth_type']) form = RegisterUserFromInviteForm()
if not invited_user: if not invited_user:
abort(404) abort(404)
@@ -70,7 +70,8 @@ def _do_registration(form, service=None, send_sms=True, send_email=True):
user = user_api_client.register_user(form.name.data, user = user_api_client.register_user(form.name.data,
form.email_address.data, form.email_address.data,
form.mobile_number.data, form.mobile_number.data,
form.password.data) form.password.data,
form.auth_type.data)
# TODO possibly there should be some exception handling # TODO possibly there should be some exception handling
# for sending sms and email codes. # for sending sms and email codes.

View File

@@ -150,7 +150,7 @@ class User(UserMixin):
class InvitedUser(object): class InvitedUser(object):
def __init__(self, id, service, from_user, email_address, permissions, status, created_at, auth_type=None): def __init__(self, id, service, from_user, email_address, permissions, status, created_at, auth_type):
self.id = id self.id = id
self.service = str(service) self.service = str(service)
self.from_user = from_user self.from_user = from_user

View File

@@ -21,12 +21,13 @@ class UserApiClient(NotifyAdminAPIClient):
self.api_key = app.config['ADMIN_CLIENT_SECRET'] self.api_key = app.config['ADMIN_CLIENT_SECRET']
self.max_failed_login_count = app.config["MAX_FAILED_LOGIN_COUNT"] self.max_failed_login_count = app.config["MAX_FAILED_LOGIN_COUNT"]
def register_user(self, name, email_address, mobile_number, password): def register_user(self, name, email_address, mobile_number, password, auth_type):
data = { data = {
"name": name, "name": name,
"email_address": email_address, "email_address": email_address,
"mobile_number": mobile_number, "mobile_number": mobile_number,
"password": password "password": password,
"auth_type": auth_type
} }
user_data = self.post("/user", data) user_data = self.post("/user", data)
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count) return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)

View File

@@ -149,15 +149,17 @@ def api_key_json(id_, name, expiry_date=None):
} }
def invite_json(id_, from_user, service_id, email_address, permissions, created_at, status): def invite_json(id_, from_user, service_id, email_address, permissions, created_at, status, auth_type):
return {'id': id_, return {
'from_user': from_user, 'id': id_,
'service': service_id, 'from_user': from_user,
'email_address': email_address, 'service': service_id,
'status': status, 'email_address': email_address,
'permissions': permissions, 'status': status,
'created_at': created_at 'permissions': permissions,
} 'created_at': created_at,
'auth_type': auth_type
}
TEST_USER_EMAIL = 'test@user.gov.uk' TEST_USER_EMAIL = 'test@user.gov.uk'

View File

@@ -267,7 +267,8 @@ def test_new_user_accept_invite_completes_new_registration_redirects_to_verify(
'from_user': invited_user['from_user'], 'from_user': invited_user['from_user'],
'password': 'longpassword', 'password': 'longpassword',
'mobile_number': '+447890123456', 'mobile_number': '+447890123456',
'name': 'Invited User' 'name': 'Invited User',
'auth_type': 'email_auth'
} }
expected_redirect_location = 'http://localhost/verify' expected_redirect_location = 'http://localhost/verify'
@@ -280,7 +281,8 @@ def test_new_user_accept_invite_completes_new_registration_redirects_to_verify(
mock_register_user.assert_called_with(data['name'], mock_register_user.assert_called_with(data['name'],
data['email_address'], data['email_address'],
data['mobile_number'], data['mobile_number'],
data['password']) data['password'],
data['auth_type'])
assert mock_accept_invite.call_count == 1 assert mock_accept_invite.call_count == 1
@@ -312,65 +314,6 @@ def test_signed_in_existing_user_cannot_use_anothers_invite(
assert mock_accept_invite.call_count == 0 assert mock_accept_invite.call_count == 0
def test_new_invited_user_verifies_and_added_to_service(
client,
service_one,
sample_invite,
api_user_active,
mock_check_invite_token,
mock_dont_get_user_by_email,
mock_is_email_unique,
mock_register_user,
mock_send_verify_code,
mock_check_verify_code,
mock_get_user,
mock_update_user_attribute,
mock_add_user_to_service,
mock_accept_invite,
mock_get_service,
mock_get_service_templates,
mock_get_template_statistics,
mock_get_jobs,
mock_has_permissions,
mock_get_users_by_service,
mock_get_detailed_service,
mock_get_usage,
mocker,
):
mocker.patch('app.main.views.invites.check_token')
# visit accept token page
response = client.get(url_for('main.accept_invite', token='thisisnotarealtoken'))
data = {'service': sample_invite['service'],
'email_address': sample_invite['email_address'],
'from_user': sample_invite['from_user'],
'password': 'longpassword',
'mobile_number': '+447890123456',
'name': 'Invited User'
}
# get redirected to register from invite
response = client.post(url_for('main.register_from_invite'), data=data)
# that sends user on to verify
response = client.post(url_for('main.verify'), data={'sms_code': '12345'}, follow_redirects=True)
# when they post codes back to admin user should be added to
# service and sent on to dash board
expected_permissions = ['send_messages', 'manage_service', 'manage_api_keys']
with client.session_transaction() as session:
new_user_id = session['user_id']
mock_add_user_to_service.assert_called_with(data['service'], new_user_id, expected_permissions)
mock_accept_invite.assert_called_with(data['service'], sample_invite['id'])
mock_check_verify_code.assert_called_once_with(new_user_id, '12345', 'sms')
assert service_one['id'] == session['service_id']
raw_html = response.data.decode('utf-8')
page = BeautifulSoup(raw_html, 'html.parser')
assert page.find('h1').text == 'Dashboard'
def test_gives_message_if_token_has_expired( def test_gives_message_if_token_has_expired(
app_, app_,
client, client,

View File

@@ -49,7 +49,8 @@ def test_register_creates_new_user_and_redirects_to_continue_page(
user_data = {'name': 'Some One Valid', user_data = {'name': 'Some One Valid',
'email_address': 'notfound@example.gov.uk', 'email_address': 'notfound@example.gov.uk',
'mobile_number': phone_number_to_register_with, 'mobile_number': phone_number_to_register_with,
'password': 'validPassword!' 'password': 'validPassword!',
'auth_type': 'sms_auth'
} }
response = client.post(url_for('main.register'), data=user_data, follow_redirects=True) response = client.post(url_for('main.register'), data=user_data, follow_redirects=True)
@@ -62,7 +63,8 @@ def test_register_creates_new_user_and_redirects_to_continue_page(
mock_register_user.assert_called_with(user_data['name'], mock_register_user.assert_called_with(user_data['name'],
user_data['email_address'], user_data['email_address'],
user_data['mobile_number'], user_data['mobile_number'],
user_data['password']) user_data['password'],
user_data['auth_type'])
def test_register_continue_handles_missing_session_sensibly( def test_register_continue_handles_missing_session_sensibly(
@@ -175,15 +177,21 @@ def test_register_from_invite_(
"invited@user.com", "invited@user.com",
["manage_users"], ["manage_users"],
"pending", "pending",
datetime.utcnow()) datetime.utcnow(),
'sms_auth')
with client.session_transaction() as session: with client.session_transaction() as session:
session['invited_user'] = invited_user.serialize() session['invited_user'] = invited_user.serialize()
response = client.post(url_for('main.register_from_invite'), response = client.post(
data={'name': 'Registered in another Browser', url_for('main.register_from_invite'),
'email_address': invited_user.email_address, data={
'mobile_number': '+4407700900460', 'name': 'Registered in another Browser',
'service': str(invited_user.id), 'email_address': invited_user.email_address,
'password': 'somreallyhardthingtoguess'}) 'mobile_number': '+4407700900460',
'service': str(invited_user.id),
'password': 'somreallyhardthingtoguess',
'auth_type': 'sms_auth'
}
)
assert response.status_code == 302 assert response.status_code == 302
assert response.location == url_for('main.verify', _external=True) assert response.location == url_for('main.verify', _external=True)
@@ -199,14 +207,20 @@ def test_register_from_invite_when_user_registers_in_another_browser(
api_user_active.email_address, api_user_active.email_address,
["manage_users"], ["manage_users"],
"pending", "pending",
datetime.utcnow()) datetime.utcnow(),
'sms_auth')
with client.session_transaction() as session: with client.session_transaction() as session:
session['invited_user'] = invited_user.serialize() session['invited_user'] = invited_user.serialize()
response = client.post(url_for('main.register_from_invite'), response = client.post(
data={'name': 'Registered in another Browser', url_for('main.register_from_invite'),
'email_address': api_user_active.email_address, data={
'mobile_number': api_user_active.mobile_number, 'name': 'Registered in another Browser',
'service': str(api_user_active.id), 'email_address': api_user_active.email_address,
'password': 'somreallyhardthingtoguess'}) 'mobile_number': api_user_active.mobile_number,
'service': str(api_user_active.id),
'password': 'somreallyhardthingtoguess',
'auth_type': 'sms_auth'
}
)
assert response.status_code == 302 assert response.status_code == 302
assert response.location == url_for('main.verify', _external=True) assert response.location == url_for('main.verify', _external=True)

View File

@@ -1288,11 +1288,12 @@ def mock_send_change_email_verification(mocker):
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def mock_register_user(mocker, api_user_pending): def mock_register_user(mocker, api_user_pending):
def _register(name, email_address, mobile_number, password): def _register(name, email_address, mobile_number, password, auth_type):
api_user_pending.name = name api_user_pending.name = name
api_user_pending.email_address = email_address api_user_pending.email_address = email_address
api_user_pending.mobile_number = mobile_number api_user_pending.mobile_number = mobile_number
api_user_pending.password = password api_user_pending.password = password
api_user_pending.auth_type = auth_type
return api_user_pending return api_user_pending
return mocker.patch('app.user_api_client.register_user', side_effect=_register) return mocker.patch('app.user_api_client.register_user', side_effect=_register)
@@ -1910,7 +1911,9 @@ def sample_invite(mocker, service_one, status='pending'):
service_id = service_one['id'] service_id = service_one['id']
permissions = 'send_messages,manage_service,manage_api_keys' permissions = 'send_messages,manage_service,manage_api_keys'
created_at = str(datetime.utcnow()) created_at = str(datetime.utcnow())
return invite_json(id_, from_user, service_id, email_address, permissions, created_at, status) auth_type = 'sms_auth'
return invite_json(id_, from_user, service_id, email_address, permissions, created_at, status, auth_type)
@pytest.fixture(scope='function') @pytest.fixture(scope='function')