Merge lp:~canonical-isd-hackers/canonical-identity-provider/930377-add-device into lp:canonical-identity-provider/release

Proposed by Michael Foord
Status: Merged
Approved by: David Owen
Approved revision: no longer in the source branch.
Merged at revision: 337
Proposed branch: lp:~canonical-isd-hackers/canonical-identity-provider/930377-add-device
Merge into: lp:canonical-identity-provider/release
Diff against target: 213 lines (+54/-10)
9 files modified
identityprovider/models/twofactor.py (+0/-2)
identityprovider/tests/acceptance/devices/add_device.py (+15/-1)
identityprovider/tests/acceptance/devices/add_two_devices.py (+3/-0)
identityprovider/tests/acceptance/devices/prefs.py (+3/-1)
identityprovider/tests/acceptance/devices/remove_device.py (+5/-6)
identityprovider/tests/acceptance/shared/devices.py (+5/-0)
identityprovider/tests/acceptance/shared/helpers.py (+18/-0)
identityprovider/tests/unit/test_devices.py (+4/-0)
identityprovider/views/devices.py (+1/-0)
To merge this branch: bzr merge lp:~canonical-isd-hackers/canonical-identity-provider/930377-add-device
Reviewer Review Type Date Requested Status
David Owen (community) Approve
Review via email: mp+93006@code.launchpad.net

Commit message

Adding a two factor device authenticates the user with that device.

To post a comment you must log in.
Revision history for this message
David Owen (dsowen) wrote :

Test changes look good. It's not every day you see a decent use for globals, but I think this is one.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'identityprovider/models/twofactor.py'
--- identityprovider/models/twofactor.py 2012-02-07 14:39:20 +0000
+++ identityprovider/models/twofactor.py 2012-02-14 18:17:19 +0000
@@ -79,5 +79,3 @@
79 self.counter = new_counter79 self.counter = new_counter
80 self.save()80 self.save()
81 return valid81 return valid
82
83
8482
=== modified file 'identityprovider/tests/acceptance/devices/add_device.py'
--- identityprovider/tests/acceptance/devices/add_device.py 2012-02-13 16:12:21 +0000
+++ identityprovider/tests/acceptance/devices/add_device.py 2012-02-14 18:17:19 +0000
@@ -4,7 +4,9 @@
4from sst.actions import *4from sst.actions import *
55
6import helpers6import helpers
7from devices import click_add_device_button, click_add_new_device_link7from devices import (
8 click_add_device_button, click_add_new_device_link, click_remove_button
9)
810
911
10config.set_base_url_from_env()12config.set_base_url_from_env()
@@ -100,3 +102,15 @@
100assert_table_has_rows('device-list', 1)102assert_table_has_rows('device-list', 1)
101assert_table_row_contains_text('device-list', 0,103assert_table_row_contains_text('device-list', 0,
102 ['My Generic Device', 'Remove'], regex=True)104 ['My Generic Device', 'Remove'], regex=True)
105
106# Click on the remove button
107click_remove_button()
108
109# Click on ok - this will only work if adding a device also authenticates us.
110# If not we will need to authenticate and this next step will fail.
111click_button(get_element(tag='button', text='Remove this device'))
112# Check we are back on the device-list page
113assert_url('/device-list')
114
115# Check that our device has been removed
116assert_table_has_rows('device-list', 0)
103\ No newline at end of file117\ No newline at end of file
104118
=== modified file 'identityprovider/tests/acceptance/devices/add_two_devices.py'
--- identityprovider/tests/acceptance/devices/add_two_devices.py 2012-02-13 17:07:37 +0000
+++ identityprovider/tests/acceptance/devices/add_two_devices.py 2012-02-14 18:17:19 +0000
@@ -19,6 +19,9 @@
19# Add an authentication device19# Add an authentication device
20aes_key = add_device('This device')20aes_key = add_device('This device')
2121
22# Logout and back in so we are no longer 2f-authenticated
23helpers.logout_and_in()
24
22# Click on "Add a new authentication device" link25# Click on "Add a new authentication device" link
23click_add_new_device_link()26click_add_new_device_link()
2427
2528
=== modified file 'identityprovider/tests/acceptance/devices/prefs.py'
--- identityprovider/tests/acceptance/devices/prefs.py 2012-02-13 16:12:21 +0000
+++ identityprovider/tests/acceptance/devices/prefs.py 2012-02-14 18:17:19 +0000
@@ -11,7 +11,6 @@
11# TODO: remove once staging / production have two-factor authentication11# TODO: remove once staging / production have two-factor authentication
12check_flags('TWOFACTOR')12check_flags('TWOFACTOR')
1313
14
15# Create account and login14# Create account and login
16helpers.register_account()15helpers.register_account()
1716
@@ -42,6 +41,9 @@
42assert_attribute(get_element(tag="input", name="two-factor", value="always"),41assert_attribute(get_element(tag="input", name="two-factor", value="always"),
43 'checked', None)42 'checked', None)
4443
44# Logout and back in so we are no longer 2f-authenticated
45helpers.logout_and_in()
46
45# Change the preference to always require 2 factor authentication and save47# Change the preference to always require 2 factor authentication and save
46set_radio_value(get_element(tag="input", name="two-factor", value="always"))48set_radio_value(get_element(tag="input", name="two-factor", value="always"))
47click_button("authentication-prefs")49click_button("authentication-prefs")
4850
=== modified file 'identityprovider/tests/acceptance/devices/remove_device.py'
--- identityprovider/tests/acceptance/devices/remove_device.py 2012-02-13 16:12:21 +0000
+++ identityprovider/tests/acceptance/devices/remove_device.py 2012-02-14 18:17:19 +0000
@@ -5,12 +5,8 @@
5from oath import hotp5from oath import hotp
66
7import helpers7import helpers
8from devices import add_device8from devices import add_device, click_remove_button
99
10
11def click_remove_button():
12 # this button is actually a link
13 click_link(get_element(tag='a', text_regex='Remove'))
1410
15config.set_base_url_from_env()11config.set_base_url_from_env()
16# TODO: remove once staging / production have two-factor authentication12# TODO: remove once staging / production have two-factor authentication
@@ -23,6 +19,9 @@
23# Add an authentication device19# Add an authentication device
24aes_key = add_device('This device')20aes_key = add_device('This device')
2521
22# Logout and back in so we are no longer 2f-authenticated
23helpers.logout_and_in()
24
26# Click on the remove button25# Click on the remove button
27click_remove_button()26click_remove_button()
2827
2928
=== modified file 'identityprovider/tests/acceptance/shared/devices.py'
--- identityprovider/tests/acceptance/shared/devices.py 2012-02-13 15:58:27 +0000
+++ identityprovider/tests/acceptance/shared/devices.py 2012-02-14 18:17:19 +0000
@@ -11,6 +11,11 @@
11 get_element(tag='a', text_regex='^Add a new authentication device'))11 get_element(tag='a', text_regex='^Add a new authentication device'))
1212
1313
14def click_remove_button():
15 # this button is actually a link
16 click_link(get_element(tag='a', text_regex='Remove'))
17
18
14def assert_device(name):19def assert_device(name):
15 assert_element(tag="td", text=name)20 assert_element(tag="td", text=name)
1621
1722
=== modified file 'identityprovider/tests/acceptance/shared/helpers.py'
--- identityprovider/tests/acceptance/shared/helpers.py 2012-02-13 15:58:27 +0000
+++ identityprovider/tests/acceptance/shared/helpers.py 2012-02-14 18:17:19 +0000
@@ -8,6 +8,9 @@
8from sst.actions import *8from sst.actions import *
99
1010
11_LAST_EMAIL = None
12_LAST_PASSWORD = None
13
11def register_account(email_address=None, displayname="My Name",14def register_account(email_address=None, displayname="My Name",
12 password="Admin007", fetch_email=True, verify=True):15 password="Admin007", fetch_email=True, verify=True):
13 """Register an account and verify the token by default.16 """Register an account and verify the token by default.
@@ -16,6 +19,8 @@
1619
17 If you pass in an `email_address` then the verification token is returned.20 If you pass in an `email_address` then the verification token is returned.
18 """21 """
22 global _LAST_EMAIL, _LAST_PASSWORD
23
19 return_email = False24 return_email = False
20 if email_address is None:25 if email_address is None:
21 return_email = True26 return_email = True
@@ -38,6 +43,10 @@
38 if verify:43 if verify:
39 write_textfield(get_element(name='confirmation_code'), vcode)44 write_textfield(get_element(name='confirmation_code'), vcode)
40 click_button(get_element(css_class='btn'))45 click_button(get_element(css_class='btn'))
46
47 _LAST_EMAIL = email_address
48 _LAST_PASSWORD = password
49
41 if return_email:50 if return_email:
42 return email_address51 return email_address
43 return vcode52 return vcode
@@ -101,6 +110,15 @@
101 click_button(get_element(name='continue'))110 click_button(get_element(name='continue'))
102111
103112
113def logout_and_in():
114 """Log out and then back in again, using the email and password from the
115 last registered account. Returns to the initial url after login."""
116 url = get_current_url()
117 logout()
118 login(_LAST_EMAIL, _LAST_PASSWORD)
119 go_to(url)
120
121
104def logout():122def logout():
105 go_to('/+logout')123 go_to('/+logout')
106124
107125
=== modified file 'identityprovider/tests/unit/test_devices.py'
--- identityprovider/tests/unit/test_devices.py 2012-02-14 13:45:37 +0000
+++ identityprovider/tests/unit/test_devices.py 2012-02-14 18:17:19 +0000
@@ -114,6 +114,7 @@
114 mock_request = self._get_mock_request()114 mock_request = self._get_mock_request()
115 mock_request.method = 'POST'115 mock_request.method = 'POST'
116 mock_request.POST.update(post_data)116 mock_request.POST.update(post_data)
117 mock_request.session = {twofactor.TWOFACTOR_KEY: True}
117 return mock_request118 return mock_request
118119
119 def _post_device_addition(self, mock_request):120 def _post_device_addition(self, mock_request):
@@ -146,6 +147,8 @@
146 otp='755224',147 otp='755224',
147 hex_key=self.KEY148 hex_key=self.KEY
148 )149 )
150 mock_request.session = {}
151 mock_request.user.has_twofactor_devices.return_value = False
149152
150 response, MockDevice = self._post_device_addition(mock_request)153 response, MockDevice = self._post_device_addition(mock_request)
151154
@@ -158,6 +161,7 @@
158 account=mock_request.user,161 account=mock_request.user,
159 counter=1162 counter=1
160 )163 )
164 self.assertEqual(mock_request.session[twofactor.TWOFACTOR_KEY], True)
161165
162 def test_unrecognised_type(self):166 def test_unrecognised_type(self):
163 mock_request = self._get_post_request(167 mock_request = self._get_post_request(
164168
=== modified file 'identityprovider/views/devices.py'
--- identityprovider/views/devices.py 2012-02-14 13:31:35 +0000
+++ identityprovider/views/devices.py 2012-02-14 18:17:19 +0000
@@ -163,6 +163,7 @@
163 key=hex_key,163 key=hex_key,
164 counter=new_counter164 counter=new_counter
165 )165 )
166 twofactor.login(request)
166 return HttpResponseSeeOther(reverse(DEVICE_LIST))167 return HttpResponseSeeOther(reverse(DEVICE_LIST))
167 # Otherwise, set the error flag and fall through...168 # Otherwise, set the error flag and fall through...
168 error = _("The one-time password didn't match the AES key.")169 error = _("The one-time password didn't match the AES key.")