Merge lp:~maxiberta/canonical-identity-provider/revert-r1653 into lp:canonical-identity-provider/release

Proposed by Maximiliano Bertacchini
Status: Merged
Approved by: Maximiliano Bertacchini
Approved revision: no longer in the source branch.
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: lp:~maxiberta/canonical-identity-provider/revert-r1653
Merge into: lp:canonical-identity-provider/release
Diff against target: 814 lines (+112/-87)
11 files modified
django_project/settings_test.py (+0/-2)
src/api/v10/handlers.py (+2/-1)
src/api/v10/tests/utils.py (+1/-2)
src/api/v20/handlers.py (+7/-4)
src/api/v20/registration.py (+3/-3)
src/api/v20/tests/test_handlers.py (+5/-3)
src/api/v20/tests/test_registration.py (+31/-26)
src/identityprovider/emailutils.py (+22/-21)
src/identityprovider/tests/test_emailutils.py (+34/-23)
src/webui/views/account.py (+5/-1)
src/webui/views/ui.py (+2/-1)
To merge this branch: bzr merge lp:~maxiberta/canonical-identity-provider/revert-r1653
Reviewer Review Type Date Requested Status
Celso Providelo (community) Approve
Review via email: mp+353345@code.launchpad.net

Commit message

Revert using settings.SSO_ROOT_URL as the base for all URLs in emails (r1653).

Description of the change

r1653 broke emails from login.launchpad.net by using settings.SSO_ROOT_URL (ie. login.ubuntu.com) unconditionally (see: https://code.launchpad.net/~maxiberta/canonical-identity-provider/canonical-email-urls/+merge/353119).

Revert procedure:
  # Make sure we're on trunk r1653
  $ bzr revno
  1653

  # Revert
  $ bzr revert -r1652
   M django_project/settings_test.py
   M po/api/api.pot
   M po/identityprovider/identityprovider.pot
   M po/ubuntu_sso_saml/ubuntu_sso_saml.pot
   M po/webui/webui.pot
   M src/api/v10/handlers.py
   M src/api/v10/tests/utils.py
   M src/api/v20/handlers.py
   M src/api/v20/registration.py
   M src/api/v20/tests/test_handlers.py
   M src/api/v20/tests/test_registration.py
   M src/identityprovider/emailutils.py
   M src/identityprovider/tests/test_emailutils.py
   M src/webui/views/account.py
   M src/webui/views/ui.py

  # Ignore changes in po/
  $ bzr revert po
   M po/api/api.pot
   M po/identityprovider/identityprovider.pot
   M po/ubuntu_sso_saml/ubuntu_sso_saml.pot
   M po/webui/webui.pot

  $ bzr commit

To post a comment you must log in.
Revision history for this message
Celso Providelo (cprov) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'django_project/settings_test.py'
2--- django_project/settings_test.py 2018-08-14 21:03:03 +0000
3+++ django_project/settings_test.py 2018-08-17 18:50:19 +0000
4@@ -2,8 +2,6 @@
5
6 from settings_devel import * # noqa
7
8-SSO_ROOT_URL = 'http://testserver'
9-
10 # https://github.com/sunlightlabs/django-honeypot/issues/12
11 # until django-honeypot provides a way of using the middleware in tests,
12 # we'll remove the middleware
13
14=== modified file 'src/api/v10/handlers.py'
15--- src/api/v10/handlers.py 2018-08-14 20:27:59 +0000
16+++ src/api/v10/handlers.py 2018-08-17 18:50:19 +0000
17@@ -224,8 +224,9 @@
18
19 account = Account.objects.create_account(
20 displayname, email, password, email_validated=False)
21+ root_url = request.build_absolute_uri('/')
22 emailutils.send_new_user_email(
23- account=account, email=email,
24+ root_url, account=account, email=email,
25 redirection_url=redirection_url, platform=platform)
26 account_created.send(
27 sender=self, openid_identifier=account.openid_identifier)
28
29=== modified file 'src/api/v10/tests/utils.py'
30--- src/api/v10/tests/utils.py 2018-08-14 20:27:59 +0000
31+++ src/api/v10/tests/utils.py 2018-08-17 18:50:19 +0000
32@@ -40,8 +40,7 @@
33 self.addCleanup(urllib_intercept.uninstall_opener)
34 httplib2_intercept.install()
35 self.addCleanup(httplib2_intercept.uninstall)
36- # This should intercept requests to http://testserver on port 80.
37- add_wsgi_intercept(parse.hostname, parse.port or 80, WSGIHandler)
38+ add_wsgi_intercept(parse.hostname, parse.port, WSGIHandler)
39 self.addCleanup(remove_wsgi_intercept, parse.hostname, parse.port)
40 self.api = ServiceRoot(None, host_url + '/api/1.0')
41
42
43=== modified file 'src/api/v20/handlers.py'
44--- src/api/v20/handlers.py 2018-08-14 20:27:59 +0000
45+++ src/api/v20/handlers.py 2018-08-17 18:50:19 +0000
46@@ -215,7 +215,8 @@
47 if account is None:
48 # they've tried to reset with an invalid email, so send them
49 # an email on how to create an account
50- emailutils.send_invitation_after_password_reset(email)
51+ root_url = request.build_absolute_uri('/')
52+ emailutils.send_invitation_after_password_reset(root_url, email)
53
54 if invalidated_email.exists():
55 # there is no account with this email
56@@ -263,8 +264,9 @@
57
58 # create new token and send email to user
59 redirection_url = redirection_url_for_token(token_string)
60+ root_url = request.build_absolute_uri('/')
61 auth_token, inv_auth_token = emailutils.send_password_reset_email(
62- account, email, redirection_url)
63+ root_url, account, email, redirection_url)
64
65 # return response
66 data = dict(email=auth_token.email)
67@@ -333,7 +335,7 @@
68 root_url = request.build_absolute_uri('/')
69 try:
70 account = registration.register(
71- email, password, displayname, username=username,
72+ root_url, email, password, displayname, username=username,
73 creation_source=data.get('creation_source'),
74 oid_token=data.get('oid_token')
75 )
76@@ -376,9 +378,10 @@
77 missing = dict((k, [FIELD_REQUIRED]) for k in expected - set(data))
78 return errors.INVALID_DATA(**missing)
79
80+ root_url = request.build_absolute_uri('/')
81 try:
82 account = registration.register(
83- email, password=None, displayname=displayname,
84+ root_url, email, password=None, displayname=displayname,
85 email_validated=True, send_email=False,
86 creation_source=data.get('creation_source'),
87 require_password=False,
88
89=== modified file 'src/api/v20/registration.py'
90--- src/api/v20/registration.py 2018-08-14 20:27:59 +0000
91+++ src/api/v20/registration.py 2018-08-17 18:50:19 +0000
92@@ -29,7 +29,7 @@
93
94
95 def register(
96- email, password, displayname, username=None,
97+ root_url, email, password, displayname, username=None,
98 email_validated=False, send_email=True, creation_source=None,
99 require_password=True, oid_token=None):
100 """Register a new account"""
101@@ -39,7 +39,7 @@
102 # Only send email if the account is active; otherwise, a disabled
103 # account can be spammed.
104 if other_email.account.is_active and send_email:
105- send_impersonation_email(other_email.email)
106+ send_impersonation_email(root_url, other_email.email)
107 raise EmailAlreadyRegistered(other_email.account)
108
109 data = {
110@@ -81,7 +81,7 @@
111 account_created.send('api', openid_identifier=account.openid_identifier)
112
113 if send_email:
114- send_new_user_email(account=account, email=email,
115+ send_new_user_email(root_url=root_url, account=account, email=email,
116 oid_token=oid_token)
117
118 return account
119
120=== modified file 'src/api/v20/tests/test_handlers.py'
121--- src/api/v20/tests/test_handlers.py 2018-08-14 20:27:59 +0000
122+++ src/api/v20/tests/test_handlers.py 2018-08-17 18:50:19 +0000
123@@ -1767,7 +1767,8 @@
124
125 extra = {'email': ['No account associated with invalid@foo.com']}
126 self.assert_bad_request(data={'email': 'invalid@foo.com'}, extra=extra)
127- mock_send_invitation.assert_called_once_with('invalid@foo.com')
128+ mock_send_invitation.assert_called_once_with(
129+ self.sso_root_url, 'invalid@foo.com')
130
131 def test_suspended_account(self):
132 self.account.suspend()
133@@ -1909,7 +1910,8 @@
134 content = self.do_post(
135 {'email': invalidated_email.email}, status_code=403)
136
137- mock_send_invitation.assert_called_once_with(self.email.email)
138+ mock_send_invitation.assert_called_once_with(self.sso_root_url,
139+ self.email.email)
140 expected = {
141 'message': ('This email address has been invalidated. '
142 'Please contact login support.'),
143@@ -1928,7 +1930,7 @@
144 {'email': self.email.email, 'token': token}, status_code=201)
145
146 mock_send_password_reset_email.assert_called_once_with(
147- self.account, self.email.email, redirection_url)
148+ self.sso_root_url, self.account, self.email.email, redirection_url)
149
150 def test_invalid_email_throttle_decorator(self):
151 self.assert_bad_request(
152
153=== modified file 'src/api/v20/tests/test_registration.py'
154--- src/api/v20/tests/test_registration.py 2018-08-14 20:27:59 +0000
155+++ src/api/v20/tests/test_registration.py 2018-08-17 18:50:19 +0000
156@@ -38,7 +38,7 @@
157 super(RegistrationTestCase, self).setUp()
158 self.email = self.factory.make_email_address()
159 self.data = [
160- self.email, 'SecretPassword1', 'displayname']
161+ self.sso_root_url, self.email, 'SecretPassword1', 'displayname']
162
163 self.mock_send_impersonation_email = self.patch(
164 'api.v20.registration.send_impersonation_email')
165@@ -60,7 +60,8 @@
166 if password is not None:
167 account.user.check_password(password)
168 self.mock_send_new_user_email.assert_called_once_with(
169- account=account, email=self.email, oid_token=None)
170+ root_url=self.sso_root_url, account=account, email=self.email,
171+ oid_token=None)
172 self.assertFalse(self.mock_send_impersonation_email.called)
173
174 def test_register_already_registered_and_inactive(self):
175@@ -70,7 +71,8 @@
176
177 with self.assertRaises(registration.EmailAlreadyRegistered):
178 registration.register(
179- email=self.email, password="", displayname="")
180+ self.sso_root_url, email=self.email, password="",
181+ displayname="")
182
183 self.assertFalse(self.mock_send_impersonation_email.called)
184 self.assertFalse(self.mock_send_new_user_email.called)
185@@ -81,9 +83,11 @@
186
187 with self.assertRaises(registration.EmailAlreadyRegistered):
188 registration.register(
189- email=self.email, password="", displayname="")
190+ self.sso_root_url, email=self.email, password="",
191+ displayname="")
192
193- self.mock_send_impersonation_email.assert_called_once_with(self.email)
194+ self.mock_send_impersonation_email.assert_called_once_with(
195+ self.sso_root_url, self.email)
196 self.assertFalse(self.mock_send_new_user_email.called)
197
198 def test_register_success(self):
199@@ -265,7 +269,7 @@
200 username = 'InvalidUsername'
201 with self.assertRaises(ValidationError) as ctx:
202 registration.register(
203- email=self.email, password=None,
204+ self.sso_root_url, email=self.email, password=None,
205 displayname='displayname', username=username)
206 e = ctx.exception
207 self.assertEqual(e.message_dict['username'], [INVALID_NAME])
208@@ -285,7 +289,7 @@
209 return_value=fake_lp_response)
210
211 account = registration.register(
212- email=self.email, password=None,
213+ self.sso_root_url, email=self.email, password=None,
214 displayname='displayname', username=username,
215 require_password=False)
216
217@@ -294,7 +298,7 @@
218 @switches(USERNAME_UI=False)
219 def test_can_create_account_without_pw_with_feature_flag_disabled(self):
220 account = registration.register(
221- email=self.email, password=None,
222+ self.sso_root_url, email=self.email, password=None,
223 displayname='displayname', username='validusername',
224 require_password=False)
225
226@@ -312,7 +316,7 @@
227 return_value=fake_lp_response)
228
229 account = registration.register(
230- email=self.email, password='',
231+ self.sso_root_url, email=self.email, password='',
232 displayname='displayname', username=username,
233 require_password=False)
234
235@@ -321,7 +325,7 @@
236 @switches(USERNAME_UI=False)
237 def test_can_create_account_with_empty_pw_with_feature_flag_disabled(self):
238 account = registration.register(
239- email=self.email, password='',
240+ self.sso_root_url, email=self.email, password='',
241 displayname='displayname', require_password=False)
242
243 self.assert_correct_account_content(account)
244@@ -329,7 +333,7 @@
245 def test_password_required_by_default(self):
246 with self.assertRaises(ValidationError) as ctx:
247 registration.register(
248- email=self.email, password=None,
249+ self.sso_root_url, email=self.email, password=None,
250 displayname='displayname', username='validusername')
251
252 e = ctx.exception
253@@ -339,7 +343,7 @@
254 def test_form_validation_with_valid_pw_with_feature_flag_enabled(self):
255 with self.assertRaises(ValidationError) as ctx:
256 registration.register(
257- email='bad.email', password='TheSecret1',
258+ self.sso_root_url, email='bad.email', password='TheSecret1',
259 displayname=None, username='InvalidUsername')
260 e = ctx.exception
261 self.assertEqual(e.message_dict['displayname'], [FIELD_REQUIRED])
262@@ -352,7 +356,7 @@
263 def test_form_validation_with_valid_pw_with_feature_flag_disabled(self):
264 with self.assertRaises(ValidationError) as ctx:
265 registration.register(
266- email='bad.email', password='TheSecret1',
267+ self.sso_root_url, email='bad.email', password='TheSecret1',
268 displayname=None, username='InvalidUsername')
269
270 e = ctx.exception
271@@ -368,7 +372,7 @@
272 def test_form_validation_with_no_pw_and_with_feature_flag_enabled(self):
273 with self.assertRaises(ValidationError) as ctx:
274 registration.register(
275- email='bad.email', password=None,
276+ self.sso_root_url, email='bad.email', password=None,
277 displayname=None, username='InvalidUsername')
278
279 e = ctx.exception
280@@ -383,7 +387,7 @@
281 def test_form_validation_with_no_pw_and_feature_flag_disabled(self):
282 with self.assertRaises(ValidationError) as ctx:
283 registration.register(
284- email='bad.email', password=None,
285+ self.sso_root_url, email='bad.email', password=None,
286 displayname=None, username='InvalidUsername',
287 require_password=False)
288
289@@ -415,7 +419,7 @@
290 def test_invalid_email_and_no_pw_with_default_require_password(self):
291 with self.assertRaises(ValidationError) as ctx:
292 registration.register(
293- email="", password=None,
294+ self.sso_root_url, email="", password=None,
295 displayname='displayname', require_password=True)
296
297 e = ctx.exception
298@@ -424,8 +428,9 @@
299
300 def test_invalid_email_and_password_with_no_require_password(self):
301 with self.assertRaises(ValidationError) as ctx:
302- registration.register(email="", password=123,
303- displayname='displayname')
304+ registration.register(
305+ self.sso_root_url, email="", password=123,
306+ displayname='displayname')
307
308 e = ctx.exception
309 self.assertEqual(e.message_dict['email'], [FIELD_REQUIRED])
310@@ -437,7 +442,7 @@
311 assert Account.objects.all().count() == 0
312
313 registration.register(
314- self.email, 'SecretPassword1', 'displayname',
315+ self.sso_root_url, self.email, 'SecretPassword1', 'displayname',
316 oid_token="fake_oid_token")
317
318 self.assertEqual(Account.objects.all().count(), 1)
319@@ -450,13 +455,13 @@
320 self.client.login(username=self.email, password='SecretPassword1'),
321 'Password was not properly set on new account.')
322 self.mock_send_new_user_email.assert_called_once_with(
323- oid_token="fake_oid_token",
324+ root_url=self.sso_root_url, oid_token="fake_oid_token",
325 account=account, email=self.email)
326 self.assertFalse(self.mock_send_impersonation_email.called)
327
328 def test_invalid_email_and_pw(self):
329 with self.assertRaises(ValidationError) as ctx:
330- registration.register("", '', 'displayname')
331+ registration.register(self.sso_root_url, "", '', 'displayname')
332
333 e = ctx.exception
334 self.assertEqual(e.message_dict['email'], [FIELD_REQUIRED])
335@@ -466,7 +471,7 @@
336 self.factory.make_leaked_credential(email='foo@example.com',
337 password=DEFAULT_USER_PASSWORD)
338 with self.assertRaises(PasswordPolicyError) as ctx:
339- registration.register('foo@example.com',
340+ registration.register(self.sso_root_url, 'foo@example.com',
341 DEFAULT_USER_PASSWORD, 'displayname')
342
343 self.assertIn('there was a security breach', str(ctx.exception))
344@@ -474,26 +479,26 @@
345
346 def test_email_not_validated(self):
347 account = registration.register(
348- self.email, 'SecretPassword1', 'displayname',
349+ self.sso_root_url, self.email, 'SecretPassword1', 'displayname',
350 email_validated=False)
351 self.assertFalse(account.is_verified)
352
353 def test_email_validated(self):
354 account = registration.register(
355- self.email, 'SecretPassword1', 'displayname',
356+ self.sso_root_url, self.email, 'SecretPassword1', 'displayname',
357 email_validated=True)
358 self.assertTrue(account.is_verified)
359
360 def test_creation_rationale_unset_source(self):
361 account = registration.register(
362- self.email, 'SecretPassword1', 'displayname')
363+ self.sso_root_url, self.email, 'SecretPassword1', 'displayname')
364 self.assertEqual(
365 account.creation_rationale, AccountCreationRationale.API_CREATED)
366 self.assertIsNone(account.creation_source)
367
368 def test_creation_rationale_set_source(self):
369 account = registration.register(
370- self.email, 'SecretPassword1', 'displayname',
371+ self.sso_root_url, self.email, 'SecretPassword1', 'displayname',
372 creation_source='foo bar baz')
373 self.assertEqual(
374 account.creation_rationale, AccountCreationRationale.API_CREATED)
375
376=== modified file 'src/identityprovider/emailutils.py'
377--- src/identityprovider/emailutils.py 2018-08-14 20:27:59 +0000
378+++ src/identityprovider/emailutils.py 2018-08-17 18:50:19 +0000
379@@ -96,7 +96,7 @@
380 return result
381
382
383-def _context_for_email_request(account, email, token_type,
384+def _context_for_email_request(root_url, account, email, token_type,
385 redirection_url, requester_email=None,
386 oid_token=None,
387 **kwargs):
388@@ -117,7 +117,7 @@
389 token_path = token.get_absolute_url()
390 if oid_token:
391 token_path = oid_token + token_path
392- token_url = urljoin(settings.SSO_ROOT_URL, token_path)
393+ token_url = urljoin(root_url, token_path)
394 context = {
395 'requester': name,
396 'requester_email': token.requester_email,
397@@ -136,15 +136,15 @@
398 email=email, token_type=AuthTokenType.INVALIDATEEMAIL,
399 redirection_url=redirection_url)
400 token_url = urljoin(
401- settings.SSO_ROOT_URL, invalidate_email_token.get_absolute_url())
402+ root_url, invalidate_email_token.get_absolute_url())
403 context['invalidate_email_url'] = token_url
404
405 return context, token, invalidate_email_token
406
407
408-def send_impersonation_email(email):
409+def send_impersonation_email(root_url, email):
410 """Send an email to user warning of attempted registration"""
411- url = urljoin(settings.SSO_ROOT_URL, reverse('forgot_password'))
412+ url = urljoin(root_url, reverse('forgot_password'))
413 context = {
414 'forgotten_password_url': url, 'to_email': email,
415 }
416@@ -152,7 +152,7 @@
417 _("Warning"), 'email/impersonate-warning.txt', context, email)
418
419
420-def send_new_user_email(account, email, redirection_url=None,
421+def send_new_user_email(root_url, account, email, redirection_url=None,
422 platform='all', oid_token=None):
423 """
424 Send an email to a new user.
425@@ -165,7 +165,7 @@
426 """
427
428 context, token, invalidate_email_token = _context_for_email_request(
429- account, email, AuthTokenType.VALIDATEEMAIL, redirection_url,
430+ root_url, account, email, AuthTokenType.VALIDATEEMAIL, redirection_url,
431 oid_token=oid_token)
432
433 if platform != 'desktop':
434@@ -194,7 +194,7 @@
435 return {'subject': subject, 'template': template}
436
437
438-def send_password_reset_email(account, email, redirection_url=None,
439+def send_password_reset_email(root_url, account, email, redirection_url=None,
440 reason='forgot_password'):
441 """
442 Send an email with password reset instructions.
443@@ -213,7 +213,7 @@
444 template = data['template']
445
446 context, token, invalidate_email_token = _context_for_email_request(
447- account, email, AuthTokenType.PASSWORDRECOVERY,
448+ root_url, account, email, AuthTokenType.PASSWORDRECOVERY,
449 redirection_url, requester_email=email)
450 send_fancy_email(subject, template, context, email)
451 return token, invalidate_email_token
452@@ -235,7 +235,7 @@
453 context=context, email=email.email)
454
455
456-def send_printed_codes_renewal_email(account, devices):
457+def send_printed_codes_renewal_email(root_url, account, devices):
458 email = account.preferredemail
459 if email is None:
460 logger.warning(
461@@ -246,7 +246,7 @@
462 device_urls = []
463 for device in devices:
464 device_urls.append(
465- urljoin(settings.SSO_ROOT_URL,
466+ urljoin(root_url,
467 reverse('device-print', kwargs=dict(device_id=device.id))))
468 context = dict(display_name=account.displayname, device_urls=device_urls)
469 send_fancy_email(
470@@ -255,7 +255,7 @@
471 context=context, email=email)
472
473
474-def send_validation_email_request(account, email,
475+def send_validation_email_request(root_url, account, email,
476 redirection_url=None, oid_token=None):
477 """
478 Send an email asking to validate the email account.
479@@ -272,7 +272,7 @@
480 preferredemail_email = account.preferredemail.email
481
482 context, token, invalidate_email_token = _context_for_email_request(
483- account, email, AuthTokenType.VALIDATEEMAIL, redirection_url,
484+ root_url, account, email, AuthTokenType.VALIDATEEMAIL, redirection_url,
485 requester_email=preferredemail_email, oid_token=oid_token)
486 send_fancy_email(
487 _("Validate your email address"), 'email/validate-email.txt',
488@@ -280,7 +280,7 @@
489 return token, invalidate_email_token
490
491
492-def send_invalidation_email_notice(account, invalidated_email):
493+def send_invalidation_email_notice(root_url, account, invalidated_email):
494 preferredemail = account.preferredemail
495 assert preferredemail is not None
496 email = preferredemail.email
497@@ -289,7 +289,7 @@
498 to_email=email,
499 )
500 if account.unverified_emails().count() > 0:
501- url = urljoin(settings.SSO_ROOT_URL, reverse('account-emails'))
502+ url = urljoin(root_url, reverse('account-emails'))
503 context['verify_emails_link'] = url
504 send_fancy_email(
505 _("The email address {email} was removed from your account").format(
506@@ -297,7 +297,8 @@
507 'email/email-invalidated.txt', context=context, email=email)
508
509
510-def send_notification_to_invalidated_email_address(account, invalidated_email):
511+def send_notification_to_invalidated_email_address(root_url, account,
512+ invalidated_email):
513 context = dict(
514 display_name=account.displayname, invalidated_email=invalidated_email,
515 to_email=invalidated_email,
516@@ -318,15 +319,15 @@
517 {'new_preferred': new_preferred}, email)
518
519
520-def send_invitation_after_password_reset(email):
521- url = urljoin(settings.SSO_ROOT_URL, reverse('new_account'))
522+def send_invitation_after_password_reset(root_url, email):
523+ url = urljoin(root_url, reverse('new_account'))
524 send_fancy_email(
525 _("Password reset request"), 'email/invitation.txt',
526 {'email': email, 'signup': url}, email,
527 )
528
529
530-def send_action_required_warning(account, days_of_warning, action):
531+def send_action_required_warning(root_url, account, days_of_warning, action):
532 """
533 Send an email to warn the user that an action is required.
534
535@@ -342,10 +343,10 @@
536 assert preferredemail is not None
537 email = preferredemail.email
538 context, token, invalidate_email_token = _context_for_email_request(
539- account, email, AuthTokenType.VALIDATEEMAIL,
540+ root_url, account, email, AuthTokenType.VALIDATEEMAIL,
541 redirection_url=None)
542 context.update(dict(
543- emails_url=urljoin(settings.SSO_ROOT_URL, reverse('account-emails')),
544+ emails_url=urljoin(root_url, reverse('account-emails')),
545 created=account.date_created, action=action,
546 days_of_warning=days_of_warning,
547 ))
548
549=== modified file 'src/identityprovider/tests/test_emailutils.py'
550--- src/identityprovider/tests/test_emailutils.py 2018-08-14 20:27:59 +0000
551+++ src/identityprovider/tests/test_emailutils.py 2018-08-17 18:50:19 +0000
552@@ -321,7 +321,7 @@
553 self.assertNotIn(invalidate_msg, email.body)
554
555 def test_send_impersonation_email(self):
556- send_impersonation_email(self.email)
557+ send_impersonation_email(self.sso_root_url, self.email)
558
559 url = self.request.build_absolute_uri(
560 reverse('forgot_password'))
561@@ -338,7 +338,7 @@
562
563 def test_send_new_user_email_no_platform(self):
564 token, invalidate_token = send_new_user_email(
565- self.account, self.email, self.redirection_url)
566+ self.sso_root_url, self.account, self.email, self.redirection_url)
567
568 self.assert_tokens()
569 self.assert_email_properly_formatted(
570@@ -349,7 +349,8 @@
571 @patch('identityprovider.emailutils.logger')
572 def test_send_new_user_email_invalid_platform(self, mock_logger):
573 token, invalidate_token = send_new_user_email(
574- self.account, self.email, self.redirection_url, platform='foo')
575+ self.sso_root_url, self.account, self.email,
576+ self.redirection_url, platform='foo')
577
578 self.assert_tokens()
579 self.assert_email_properly_formatted(
580@@ -359,7 +360,8 @@
581
582 def test_send_new_user_email_platform_desktop(self):
583 token, invalidate_token = send_new_user_email(
584- self.account, self.email, self.redirection_url, platform='desktop')
585+ self.sso_root_url, self.account, self.email, self.redirection_url,
586+ platform='desktop')
587
588 self.assert_tokens()
589 self.assert_email_properly_formatted(
590@@ -370,7 +372,8 @@
591 def test_send_new_user_email_platform_mobile(self):
592 self.account = None
593 token, invalidate_token = send_new_user_email(
594- self.account, self.email, self.redirection_url, platform='mobile')
595+ self.sso_root_url, self.account, self.email, self.redirection_url,
596+ platform='mobile')
597
598 self.assert_tokens()
599 self.assert_email_properly_formatted(
600@@ -381,7 +384,7 @@
601 def test_send_new_user_email_with_oid_token(self):
602 oid_token = "iama_oid_token"
603 token, invalidate_token = send_new_user_email(
604- self.account, self.email, self.redirection_url,
605+ self.sso_root_url, self.account, self.email, self.redirection_url,
606 oid_token=oid_token)
607
608 self.assert_tokens()
609@@ -393,7 +396,7 @@
610
611 def test_send_password_reset_email_with_redirection_url(self):
612 token, invalidate_token = send_password_reset_email(
613- self.account, self.email, self.redirection_url)
614+ self.sso_root_url, self.account, self.email, self.redirection_url)
615
616 self.assert_tokens(requester_email=self.email)
617 self.assert_email_properly_formatted(
618@@ -403,7 +406,7 @@
619
620 def test_send_password_reset_email_no_redirection_url(self):
621 token, invalidate_token = send_password_reset_email(
622- self.account, self.email)
623+ self.sso_root_url, self.account, self.email)
624
625 self.assert_tokens(requester_email=self.email, redirection_url=None)
626 self.assert_email_properly_formatted(
627@@ -413,7 +416,8 @@
628
629 def test_send_password_reset_email_with_custom_reason(self):
630 token, invalidate_token = send_password_reset_email(
631- self.account, self.email, reason='password_leak|adobe')
632+ self.sso_root_url, self.account, self.email,
633+ reason='password_leak|adobe')
634
635 self.assert_tokens(requester_email=self.email, redirection_url=None)
636 token_type = AuthTokenType.PASSWORDRECOVERY
637@@ -429,7 +433,8 @@
638
639 def test_send_password_reset_email_with_generic_leak_template(self):
640 token, invalidate_token = send_password_reset_email(
641- self.account, self.email, reason='password_leak|foo')
642+ self.sso_root_url, self.account, self.email,
643+ reason='password_leak|foo')
644
645 self.assert_tokens(requester_email=self.email, redirection_url=None)
646 token_type = AuthTokenType.PASSWORDRECOVERY
647@@ -444,14 +449,15 @@
648 context=context, token_type=token_type)
649
650 def test_send_password_reset_email_with_invalid_reason(self):
651- send_password_reset_email(self.account, self.email, reason='invalid')
652+ send_password_reset_email(self.sso_root_url, self.account, self.email,
653+ reason='invalid')
654
655 self.assert_tokens(requester_email=self.email, redirection_url=None,
656 tokens_count=0)
657 self.assertEqual(len(mail.outbox), 0)
658
659 def test_leaked_password_reset_email_without_invalidation_link(self):
660- send_password_reset_email(self.account, self.email,
661+ send_password_reset_email(self.sso_root_url, self.account, self.email,
662 reason='password_leak|adobe')
663
664 self.assertEqual(len(mail.outbox), 1)
665@@ -463,7 +469,7 @@
666 assert self.account.preferredemail is not None
667
668 token, invalidate_token = send_validation_email_request(
669- self.account, self.email, self.redirection_url)
670+ self.sso_root_url, self.account, self.email, self.redirection_url)
671
672 email = self.account.preferredemail.email
673 self.assert_tokens(requester_email=email)
674@@ -478,7 +484,7 @@
675 assert self.account.preferredemail is None
676
677 token, invalidate_token = send_validation_email_request(
678- self.account, self.email, self.redirection_url)
679+ self.sso_root_url, self.account, self.email, self.redirection_url)
680
681 # two tokens are created, one for validation, and one for invalidation,
682 # since the email being validated is not in the system, so the real
683@@ -491,7 +497,7 @@
684
685 def test_send_validation_email_request_no_redirection_url(self):
686 token, invalidate_token = send_validation_email_request(
687- self.account, self.email)
688+ self.sso_root_url, self.account, self.email)
689
690 email = self.account.preferredemail.email
691 self.assert_tokens(requester_email=email, redirection_url=None)
692@@ -503,7 +509,7 @@
693 def test_send_validation_email_request_with_oid_token(self):
694 oid_token = 'a' * 16
695 token, invalidate_token = send_validation_email_request(
696- self.account, self.email, self.redirection_url,
697+ self.sso_root_url, self.account, self.email, self.redirection_url,
698 oid_token=oid_token)
699
700 email = self.account.preferredemail.email
701@@ -516,7 +522,8 @@
702
703 def _assert_send_invalidation_email_notice(self):
704 invalidated_email = 'invalid@example.com'
705- send_invalidation_email_notice(self.account, invalidated_email)
706+ send_invalidation_email_notice(self.sso_root_url, self.account,
707+ invalidated_email)
708
709 verify_emails_link = self.request.build_absolute_uri(
710 reverse('account-emails'))
711@@ -554,7 +561,8 @@
712
713 def test_send_notification_to_invalidated_email_address(self):
714 invalidated_email = 'invalid@example.com'
715- send_notification_to_invalidated_email_address(self.account,
716+ send_notification_to_invalidated_email_address(self.sso_root_url,
717+ self.account,
718 invalidated_email)
719 context = dict(
720 display_name=self.account.displayname,
721@@ -580,7 +588,7 @@
722
723 def test_send_invitation_after_password_reset(self):
724 url = self.request.build_absolute_uri(reverse('new_account'))
725- send_invitation_after_password_reset(self.email)
726+ send_invitation_after_password_reset(self.sso_root_url, self.email)
727
728 context = {
729 'email': self.email, 'signup': url,
730@@ -599,7 +607,7 @@
731 url = self.request.build_absolute_uri(reverse('account-emails'))
732
733 token, invalidate_token = send_action_required_warning(
734- self.account, days_of_warning=8, action=action)
735+ self.sso_root_url, self.account, days_of_warning=8, action=action)
736
737 # tokens will always be 2, since we need the validate email token *and*
738 # the invalidate email
739@@ -647,7 +655,8 @@
740 settings.TWOFACTOR_PAPER_CODES_WARN_RENEWAL + 1)
741 device = self.factory.make_device(
742 account=self.account, device_type='paper', counter=counter)
743- send_printed_codes_renewal_email(self.account, [device])
744+ send_printed_codes_renewal_email(
745+ self.sso_root_url, self.account, [device])
746 url = self.request.build_absolute_uri(
747 reverse('device-print', kwargs=dict(device_id=device.id)))
748 context = dict(display_name=self.account.displayname,
749@@ -671,7 +680,8 @@
750 account=self.account, device_type='paper', counter=counter)
751 device2 = self.factory.make_device(
752 account=self.account, device_type='paper', counter=counter)
753- send_printed_codes_renewal_email(self.account, [device1, device2])
754+ send_printed_codes_renewal_email(
755+ self.sso_root_url, self.account, [device1, device2])
756 url1 = self.request.build_absolute_uri(
757 reverse('device-print', kwargs=dict(device_id=device1.id)))
758 url2 = self.request.build_absolute_uri(
759@@ -702,7 +712,8 @@
760 device = self.factory.make_device(
761 account=self.account, device_type='paper', counter=counter)
762 with patch('identityprovider.emailutils.logger') as mock_logger:
763- send_printed_codes_renewal_email(self.account, [device])
764+ send_printed_codes_renewal_email(
765+ self.sso_root_url, self.account, [device])
766 self.assertEqual(len(mail.outbox), 0)
767 mock_logger.warning.assert_called_once_with(
768 'Unable to find a suitable email address to send OTP reminder '
769
770=== modified file 'src/webui/views/account.py'
771--- src/webui/views/account.py 2018-08-14 20:27:59 +0000
772+++ src/webui/views/account.py 2018-08-17 18:50:19 +0000
773@@ -305,7 +305,8 @@
774
775 redirection_url = redirection_url_for_token(token)
776
777- send_validation_email_request(request.user, email,
778+ root_url = request.build_absolute_uri('/')
779+ send_validation_email_request(root_url, request.user, email,
780 redirection_url, oid_token=token)
781 set_session_email(request.session, email)
782
783@@ -415,13 +416,16 @@
784 return response
785
786 # notify the account of the email invalidation, prioritize validated emails
787+ root_url = request.build_absolute_uri('/')
788 if account.preferredemail is not None:
789 send_invalidation_email_notice(
790+ root_url,
791 account,
792 invalidated_email=email.email,
793 )
794 else:
795 send_notification_to_invalidated_email_address(
796+ root_url,
797 account,
798 invalidated_email=email.email,
799 )
800
801=== modified file 'src/webui/views/ui.py'
802--- src/webui/views/ui.py 2018-08-14 20:27:59 +0000
803+++ src/webui/views/ui.py 2018-08-17 18:50:19 +0000
804@@ -360,8 +360,9 @@
805 url += "?next=" + urlquote(next_url)
806 return HttpResponseRedirect(url)
807 elif paper_devices_needing_renewal:
808+ root_url = request.build_absolute_uri('/')
809 send_printed_codes_renewal_email(
810- request.user, paper_devices_needing_renewal)
811+ root_url, request.user, paper_devices_needing_renewal)
812 if next_url and is_safe_redirect_url(next_url):
813 return HttpResponseRedirect(next_url)
814 elif token: