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
1=== modified file 'identityprovider/models/twofactor.py'
2--- identityprovider/models/twofactor.py 2012-02-07 14:39:20 +0000
3+++ identityprovider/models/twofactor.py 2012-02-14 18:17:19 +0000
4@@ -79,5 +79,3 @@
5 self.counter = new_counter
6 self.save()
7 return valid
8-
9-
10
11=== modified file 'identityprovider/tests/acceptance/devices/add_device.py'
12--- identityprovider/tests/acceptance/devices/add_device.py 2012-02-13 16:12:21 +0000
13+++ identityprovider/tests/acceptance/devices/add_device.py 2012-02-14 18:17:19 +0000
14@@ -4,7 +4,9 @@
15 from sst.actions import *
16
17 import helpers
18-from devices import click_add_device_button, click_add_new_device_link
19+from devices import (
20+ click_add_device_button, click_add_new_device_link, click_remove_button
21+)
22
23
24 config.set_base_url_from_env()
25@@ -100,3 +102,15 @@
26 assert_table_has_rows('device-list', 1)
27 assert_table_row_contains_text('device-list', 0,
28 ['My Generic Device', 'Remove'], regex=True)
29+
30+# Click on the remove button
31+click_remove_button()
32+
33+# Click on ok - this will only work if adding a device also authenticates us.
34+# If not we will need to authenticate and this next step will fail.
35+click_button(get_element(tag='button', text='Remove this device'))
36+# Check we are back on the device-list page
37+assert_url('/device-list')
38+
39+# Check that our device has been removed
40+assert_table_has_rows('device-list', 0)
41\ No newline at end of file
42
43=== modified file 'identityprovider/tests/acceptance/devices/add_two_devices.py'
44--- identityprovider/tests/acceptance/devices/add_two_devices.py 2012-02-13 17:07:37 +0000
45+++ identityprovider/tests/acceptance/devices/add_two_devices.py 2012-02-14 18:17:19 +0000
46@@ -19,6 +19,9 @@
47 # Add an authentication device
48 aes_key = add_device('This device')
49
50+# Logout and back in so we are no longer 2f-authenticated
51+helpers.logout_and_in()
52+
53 # Click on "Add a new authentication device" link
54 click_add_new_device_link()
55
56
57=== modified file 'identityprovider/tests/acceptance/devices/prefs.py'
58--- identityprovider/tests/acceptance/devices/prefs.py 2012-02-13 16:12:21 +0000
59+++ identityprovider/tests/acceptance/devices/prefs.py 2012-02-14 18:17:19 +0000
60@@ -11,7 +11,6 @@
61 # TODO: remove once staging / production have two-factor authentication
62 check_flags('TWOFACTOR')
63
64-
65 # Create account and login
66 helpers.register_account()
67
68@@ -42,6 +41,9 @@
69 assert_attribute(get_element(tag="input", name="two-factor", value="always"),
70 'checked', None)
71
72+# Logout and back in so we are no longer 2f-authenticated
73+helpers.logout_and_in()
74+
75 # Change the preference to always require 2 factor authentication and save
76 set_radio_value(get_element(tag="input", name="two-factor", value="always"))
77 click_button("authentication-prefs")
78
79=== modified file 'identityprovider/tests/acceptance/devices/remove_device.py'
80--- identityprovider/tests/acceptance/devices/remove_device.py 2012-02-13 16:12:21 +0000
81+++ identityprovider/tests/acceptance/devices/remove_device.py 2012-02-14 18:17:19 +0000
82@@ -5,12 +5,8 @@
83 from oath import hotp
84
85 import helpers
86-from devices import add_device
87-
88-
89-def click_remove_button():
90- # this button is actually a link
91- click_link(get_element(tag='a', text_regex='Remove'))
92+from devices import add_device, click_remove_button
93+
94
95 config.set_base_url_from_env()
96 # TODO: remove once staging / production have two-factor authentication
97@@ -23,6 +19,9 @@
98 # Add an authentication device
99 aes_key = add_device('This device')
100
101+# Logout and back in so we are no longer 2f-authenticated
102+helpers.logout_and_in()
103+
104 # Click on the remove button
105 click_remove_button()
106
107
108=== modified file 'identityprovider/tests/acceptance/shared/devices.py'
109--- identityprovider/tests/acceptance/shared/devices.py 2012-02-13 15:58:27 +0000
110+++ identityprovider/tests/acceptance/shared/devices.py 2012-02-14 18:17:19 +0000
111@@ -11,6 +11,11 @@
112 get_element(tag='a', text_regex='^Add a new authentication device'))
113
114
115+def click_remove_button():
116+ # this button is actually a link
117+ click_link(get_element(tag='a', text_regex='Remove'))
118+
119+
120 def assert_device(name):
121 assert_element(tag="td", text=name)
122
123
124=== modified file 'identityprovider/tests/acceptance/shared/helpers.py'
125--- identityprovider/tests/acceptance/shared/helpers.py 2012-02-13 15:58:27 +0000
126+++ identityprovider/tests/acceptance/shared/helpers.py 2012-02-14 18:17:19 +0000
127@@ -8,6 +8,9 @@
128 from sst.actions import *
129
130
131+_LAST_EMAIL = None
132+_LAST_PASSWORD = None
133+
134 def register_account(email_address=None, displayname="My Name",
135 password="Admin007", fetch_email=True, verify=True):
136 """Register an account and verify the token by default.
137@@ -16,6 +19,8 @@
138
139 If you pass in an `email_address` then the verification token is returned.
140 """
141+ global _LAST_EMAIL, _LAST_PASSWORD
142+
143 return_email = False
144 if email_address is None:
145 return_email = True
146@@ -38,6 +43,10 @@
147 if verify:
148 write_textfield(get_element(name='confirmation_code'), vcode)
149 click_button(get_element(css_class='btn'))
150+
151+ _LAST_EMAIL = email_address
152+ _LAST_PASSWORD = password
153+
154 if return_email:
155 return email_address
156 return vcode
157@@ -101,6 +110,15 @@
158 click_button(get_element(name='continue'))
159
160
161+def logout_and_in():
162+ """Log out and then back in again, using the email and password from the
163+ last registered account. Returns to the initial url after login."""
164+ url = get_current_url()
165+ logout()
166+ login(_LAST_EMAIL, _LAST_PASSWORD)
167+ go_to(url)
168+
169+
170 def logout():
171 go_to('/+logout')
172
173
174=== modified file 'identityprovider/tests/unit/test_devices.py'
175--- identityprovider/tests/unit/test_devices.py 2012-02-14 13:45:37 +0000
176+++ identityprovider/tests/unit/test_devices.py 2012-02-14 18:17:19 +0000
177@@ -114,6 +114,7 @@
178 mock_request = self._get_mock_request()
179 mock_request.method = 'POST'
180 mock_request.POST.update(post_data)
181+ mock_request.session = {twofactor.TWOFACTOR_KEY: True}
182 return mock_request
183
184 def _post_device_addition(self, mock_request):
185@@ -146,6 +147,8 @@
186 otp='755224',
187 hex_key=self.KEY
188 )
189+ mock_request.session = {}
190+ mock_request.user.has_twofactor_devices.return_value = False
191
192 response, MockDevice = self._post_device_addition(mock_request)
193
194@@ -158,6 +161,7 @@
195 account=mock_request.user,
196 counter=1
197 )
198+ self.assertEqual(mock_request.session[twofactor.TWOFACTOR_KEY], True)
199
200 def test_unrecognised_type(self):
201 mock_request = self._get_post_request(
202
203=== modified file 'identityprovider/views/devices.py'
204--- identityprovider/views/devices.py 2012-02-14 13:31:35 +0000
205+++ identityprovider/views/devices.py 2012-02-14 18:17:19 +0000
206@@ -163,6 +163,7 @@
207 key=hex_key,
208 counter=new_counter
209 )
210+ twofactor.login(request)
211 return HttpResponseSeeOther(reverse(DEVICE_LIST))
212 # Otherwise, set the error flag and fall through...
213 error = _("The one-time password didn't match the AES key.")