launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #10685
[Merge] lp:~deryck/launchpad/reauth-for-email-363916 into lp:launchpad
Deryck Hodge has proposed merging lp:~deryck/launchpad/reauth-for-email-363916 into lp:launchpad with lp:~deryck/launchpad/support-pape-max-auth-age as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~deryck/launchpad/reauth-for-email-363916/+merge/118612
This branch ensures that a user has to re-authenticate when editing email info. This builds on the work I did in the pre-req branch to add support for PAPE extension's max_auth_age option when logging in.
The work here is really to add a check on the freshness of a login. If the login is less than 2 minutes old, we allow the user to edit his or her email settings. If not, we signal for a fresh login. To do this, the lp session is updated to store the logintime, and then a helper method is added to check the logintime in the session. There is a test added for this. The only other new work is to use this isFreshLogin helper in the view and redirect too +login if a fresher login is required. All the rest is updating doctests to ensure we have a fresh login to avoid the isFreshLogin, and there is a new setupBrowser function to help do this in doctests.
In later branches, I'll apply this for ssh and gpg and will factor out the code in lp.registry.browser.person to a function to ensure_fresh_login. But I thought it was nice to get this reviewed as is, to see how it fits together better.
The work does add about 100 lines of code, but I have preceeding branches that refactored tests to prepare for this work. And in total, I'm still about -200 LOC, even after this branch lands.
There is a bit of lint still in the long subscriptions doctest, but none that I added. I didn't, however, fix the existing lint because the diff would have grown significantly for very minor lint changes (mostly too long lines in the documentation parts of the test).
--
https://code.launchpad.net/~deryck/launchpad/reauth-for-email-363916/+merge/118612
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~deryck/launchpad/reauth-for-email-363916 into lp:launchpad.
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2012-08-07 02:31:56 +0000
+++ lib/lp/registry/browser/person.py 2012-08-07 18:27:30 +0000
@@ -257,7 +257,10 @@
ILaunchBag,
IOpenLaunchBag,
)
-from lp.services.webapp.login import logoutPerson
+from lp.services.webapp.login import (
+ isFreshLogin,
+ logoutPerson,
+ )
from lp.services.webapp.menu import get_current_view
from lp.services.webapp.publisher import LaunchpadView
from lp.services.worlddata.interfaces.country import ICountry
@@ -2810,6 +2813,11 @@
label = 'Change your e-mail settings'
def initialize(self):
+ if not isFreshLogin(self.request):
+ reauth_query = '+login?reauth=1'
+ base_url = canonical_url(self.context, view_name='+editemails')
+ login_url = '%s/%s' % (base_url, reauth_query)
+ self.request.response.redirect(login_url)
if self.context.is_team:
# +editemails is not available on teams.
name = self.request['PATH_INFO'].split('/')[-1]
=== modified file 'lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt'
--- lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt 2012-06-29 14:34:11 +0000
+++ lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt 2012-08-07 18:27:30 +0000
@@ -141,7 +141,13 @@
added as an email address:
>>> from lp.testing.pages import strip_label
+ >>> from lp.registry.interfaces.person import IPersonSet
+ >>> from lp.testing.pages import setupBrowserFreshLogin
+ >>> login(ANONYMOUS)
+ >>> person = getUtility(IPersonSet).getByEmail('test@xxxxxxxxxxxxx')
+ >>> logout()
+ >>> browser = setupBrowserFreshLogin(person)
>>> browser.open("http://launchpad.dev/~name12/+editemails")
>>> control = browser.getControl(name='field.UNVALIDATED_SELECTED')
>>> [strip_label(label) for label in sorted(control.displayOptions)]
=== modified file 'lib/lp/registry/stories/mailinglists/subscriptions.txt'
--- lib/lp/registry/stories/mailinglists/subscriptions.txt 2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/mailinglists/subscriptions.txt 2012-08-07 18:27:30 +0000
@@ -60,8 +60,15 @@
>>> admin_browser.getControl('New member').value = 'no-team-memberships'
>>> admin_browser.getControl('Add Member').click()
- >>> no_team_browser = setupBrowser(
- ... auth="Basic no-team-memberships@xxxxxxxx:test")
+ >>> from lp.testing.pages import setupBrowserFreshLogin
+ >>> from zope.component import getUtility
+ >>> from lp.registry.interfaces.person import IPersonSet
+ >>> login(ANONYMOUS)
+ >>> person = getUtility(IPersonSet).getByEmail(
+ ... 'no-team-memberships@xxxxxxxx')
+ >>> logout()
+ >>> no_team_browser = setupBrowserFreshLogin(person)
+
>>> no_team_browser.open('http://launchpad.dev/people/+me/+editemails')
>>> rosetta_admins = no_team_browser.getControl(
... name='field.subscription.rosetta-admins')
@@ -77,15 +84,19 @@
he's a member of. Mailing lists show up in this list regardless of whether
it's currently the team contact method.
- >>> browser.open('http://launchpad.dev/~carlos')
- >>> browser.getLink(url="+editemails").click()
+ >>> login(ANONYMOUS)
+ >>> carlos = getUtility(IPersonSet).getByName('carlos')
+ >>> logout()
+ >>> carlos_browser = setupBrowserFreshLogin(carlos)
+ >>> carlos_browser.open('http://launchpad.dev/~carlos')
+ >>> carlos_browser.getLink(url="+editemails").click()
>>> from lp.services.helpers import backslashreplace
- >>> print backslashreplace(browser.title)
+ >>> print backslashreplace(carlos_browser.title)
Change your e-mail settings...
- >>> admins = browser.getControl(name='field.subscription.admins')
- >>> rosetta_admins = browser.getControl(
+ >>> admins = carlos_browser.getControl(name='field.subscription.admins')
+ >>> rosetta_admins = carlos_browser.getControl(
... name='field.subscription.rosetta-admins')
>>> admins.displayOptions
@@ -100,7 +111,7 @@
However, testing-spanish-team's list doesn't show up because its creation has
not been completed (specifically, Mailman hasn't constructed it yet).
- >>> browser.getControl(name='field.subscription.testing-spanish-team')
+ >>> carlos_browser.getControl(name='field.subscription.testing-spanish-team')
Traceback (most recent call last):
...
LookupError: name 'field.subscription.testing-spanish-team'
@@ -110,16 +121,16 @@
him to update his subscription. So this is not the same as subscribing
explicitly with whatever is his preferred email address.
- >>> admins = browser.getControl(name='field.subscription.admins')
+ >>> admins = carlos_browser.getControl(name='field.subscription.admins')
>>> admins.value = ['Preferred address']
- >>> browser.getControl('Update Subscriptions').click()
+ >>> carlos_browser.getControl('Update Subscriptions').click()
- >>> for msg in get_feedback_messages(browser.contents):
+ >>> for msg in get_feedback_messages(carlos_browser.contents):
... print msg
Subscriptions updated.
- >>> admins = browser.getControl(name='field.subscription.admins')
- >>> rosetta_admins = browser.getControl(
+ >>> admins = carlos_browser.getControl(name='field.subscription.admins')
+ >>> rosetta_admins = carlos_browser.getControl(
... name='field.subscription.rosetta-admins')
>>> print admins.value
['Preferred address']
@@ -131,10 +142,10 @@
>>> admins.value = ['carlos@xxxxxxxxxxxxx']
>>> rosetta_admins.value = ['carlos@xxxxxxxx']
- >>> browser.getControl('Update Subscriptions').click()
+ >>> carlos_browser.getControl('Update Subscriptions').click()
- >>> admins = browser.getControl(name='field.subscription.admins')
- >>> rosetta_admins = browser.getControl(
+ >>> admins = carlos_browser.getControl(name='field.subscription.admins')
+ >>> rosetta_admins = carlos_browser.getControl(
... name='field.subscription.rosetta-admins')
>>> print admins.value
['carlos@xxxxxxxxxxxxx']
@@ -146,10 +157,10 @@
>>> admins.value = ['Preferred address']
>>> rosetta_admins.value = ['carlos@xxxxxxxxxxxxx']
- >>> browser.getControl('Update Subscriptions').click()
+ >>> carlos_browser.getControl('Update Subscriptions').click()
- >>> admins = browser.getControl(name='field.subscription.admins')
- >>> rosetta_admins = browser.getControl(
+ >>> admins = carlos_browser.getControl(name='field.subscription.admins')
+ >>> rosetta_admins = carlos_browser.getControl(
... name='field.subscription.rosetta-admins')
>>> print admins.value
['Preferred address']
@@ -159,15 +170,15 @@
Finally, he can unsubscribe from any mailing list by setting the subscription
menu item to "Don't subscribe".
- >>> admins = browser.getControl(name='field.subscription.admins')
- >>> rosetta_admins = browser.getControl(
+ >>> admins = carlos_browser.getControl(name='field.subscription.admins')
+ >>> rosetta_admins = carlos_browser.getControl(
... name='field.subscription.rosetta-admins')
>>> admins.value = ["Don't subscribe"]
>>> rosetta_admins.value = ["Don't subscribe"]
- >>> browser.getControl('Update Subscriptions').click()
+ >>> carlos_browser.getControl('Update Subscriptions').click()
- >>> admins = browser.getControl(name='field.subscription.admins')
- >>> rosetta_admins = browser.getControl(
+ >>> admins = carlos_browser.getControl(name='field.subscription.admins')
+ >>> rosetta_admins = carlos_browser.getControl(
... name='field.subscription.rosetta-admins')
>>> print admins.value
["Don't subscribe"]
@@ -186,12 +197,12 @@
to. We will use Carlos, as he is an administrator for the Rosetta
Admins team, and he should know if the list is available.
- >>> browser.open('http://launchpad.dev/~carlos')
- >>> browser.getLink(url="+editemails").click()
- >>> print backslashreplace(browser.title)
+ >>> carlos_browser.open('http://launchpad.dev/~carlos')
+ >>> carlos_browser.getLink(url="+editemails").click()
+ >>> print backslashreplace(carlos_browser.title)
Change your e-mail settings...
- >>> rosetta_admins = browser.getControl(
+ >>> rosetta_admins = carlos_browser.getControl(
... name='field.subscription.rosetta-admins')
>>> rosetta_admins.displayOptions
['Preferred address', "Don't subscribe",
@@ -220,12 +231,16 @@
the list. The list does not show up on his Subscription Management
screen.
- >>> browser.open('http://launchpad.dev/~jdub')
- >>> browser.getLink(url="+editemails").click()
- >>> print browser.title
+ >>> login(ANONYMOUS)
+ >>> jdub = getUtility(IPersonSet).getByName('jdub')
+ >>> logout()
+ >>> jdub_browser = setupBrowserFreshLogin(jdub)
+ >>> jdub_browser.open('http://launchpad.dev/~jdub')
+ >>> jdub_browser.getLink(url="+editemails").click()
+ >>> print jdub_browser.title
Change your e-mail settings...
- >>> browser.getControl(
+ >>> jdub_browser.getControl(
... name='field.subscription.rosetta-admins')
Traceback (most recent call last):
...
@@ -244,12 +259,12 @@
His mailing list subscription is now available to be managed.
- >>> browser.open('http://launchpad.dev/~jdub')
- >>> browser.getLink(url="+editemails").click()
- >>> print browser.title
+ >>> jdub_browser.open('http://launchpad.dev/~jdub')
+ >>> jdub_browser.getLink(url="+editemails").click()
+ >>> print jdub_browser.title
Change your e-mail settings...
- >>> rosetta_team = browser.getControl(
+ >>> rosetta_team = jdub_browser.getControl(
... name='field.subscription.rosetta-admins')
>>> rosetta_team.displayOptions
@@ -309,60 +324,60 @@
Carlos can see the subscribe link on the admin team's Overview
page, because he is not subscribed to the team mailing list.
- >>> browser = setupBrowser(auth='Basic carlos@xxxxxxxxxxxxx:test')
- >>> browser.open('http://launchpad.dev/~admins')
- >>> browser.getLink('Subscribe to mailing list').click()
- >>> print browser.url
+ >>> carlos_browser.open('http://launchpad.dev/~admins')
+ >>> carlos_browser.getLink('Subscribe to mailing list').click()
+ >>> print carlos_browser.url
http://launchpad.dev/~carlos/+editemails
The unsubscribe link is visible for the rosetta admins team, which
has an active mailing list.
# Subscribe to the list using the normal technique.
- >>> browser.open('http://launchpad.dev/~carlos')
- >>> browser.getLink(url="+editemails").click()
- >>> rosetta_admins = browser.getControl(
+ >>> carlos_browser.open('http://launchpad.dev/~carlos')
+ >>> carlos_browser.getLink(url="+editemails").click()
+ >>> rosetta_admins = carlos_browser.getControl(
... name='field.subscription.rosetta-admins')
>>> rosetta_admins.value = ['Preferred address']
- >>> browser.getControl('Update Subscriptions').click()
+ >>> carlos_browser.getControl('Update Subscriptions').click()
>>> print rosetta_admins.value
['Preferred address']
- >>> for tag in find_tags_by_class(browser.contents, 'informational'):
+ >>> for tag in find_tags_by_class(
+ ... carlos_browser.contents, 'informational'):
... print tag.renderContents()
Subscriptions updated.
- >>> browser.open('http://launchpad.dev/~rosetta-admins')
- >>> browser.getControl('Unsubscribe')
+ >>> carlos_browser.open('http://launchpad.dev/~rosetta-admins')
+ >>> carlos_browser.getControl('Unsubscribe')
<SubmitControl name='unsubscribe' type='submit'>
Clicking the link will unsubscribe you from the list immediately.
- >>> browser.getControl('Unsubscribe').click()
- >>> print get_feedback_messages(browser.contents)
+ >>> carlos_browser.getControl('Unsubscribe').click()
+ >>> print get_feedback_messages(carlos_browser.contents)
[u'You have been unsubscribed from the team mailing list.']
- >>> browser.open('http://launchpad.dev/~rosetta-admins')
+ >>> carlos_browser.open('http://launchpad.dev/~rosetta-admins')
>>> print extract_text(
- ... find_tag_by_id(browser.contents, 'mailing-lists'))
+ ... find_tag_by_id(carlos_browser.contents, 'mailing-lists'))
Mailing list...
Subscribe to mailing list...
The Ubuntu translators team, which does not have any lists configured,
does not show either link.
- >>> browser.open('http://launchpad.dev/~ubuntu-translators')
+ >>> carlos_browser.open('http://launchpad.dev/~ubuntu-translators')
>>> print extract_text(
- ... find_portlet(browser.contents, 'Mailing list'))
+ ... find_portlet(carlos_browser.contents, 'Mailing list'))
Mailing list
This team does not use Launchpad to host a mailing list.
Create a mailing list
- >>> browser.getLink('Subscribe')
+ >>> carlos_browser.getLink('Subscribe')
Traceback (most recent call last):
...
LinkNotFoundError
- >>> browser.getLink('Unsubscribe')
+ >>> carlos_browser.getLink('Unsubscribe')
Traceback (most recent call last):
...
LinkNotFoundError
@@ -375,9 +390,9 @@
mailing list subscribers, if there is an active mailing list. The
rosetta admins team has such a list and carlos is the owner.
- >>> browser.open('http://launchpad.dev/~rosetta-admins')
+ >>> carlos_browser.open('http://launchpad.dev/~rosetta-admins')
>>> print extract_text(
- ... find_portlet(browser.contents, 'Mailing list'))
+ ... find_portlet(carlos_browser.contents, 'Mailing list'))
Mailing list
rosetta-admins@xxxxxxxxxxxxxxxxxxx
Policy: You must be a team member to subscribe to the team mailing list.
@@ -389,11 +404,11 @@
(Jeff Waugh has asked to subscribe but he's not considered a subscriber
because his membership on Rosetta Admins hasn't been approved)
- >>> browser.getLink('View subscribers').click()
- >>> print browser.title
+ >>> carlos_browser.getLink('View subscribers').click()
+ >>> print carlos_browser.title
Mailing list subscribers for the Rosetta Administrators team...
- >>> print extract_text(find_tag_by_id(browser.contents, 'subscribers'))
+ >>> print extract_text(find_tag_by_id(carlos_browser.contents, 'subscribers'))
Nobody has subscribed to this team's mailing list yet.
If it had subscribers, though, they'd be shown on that page, in a batched
@@ -407,8 +422,6 @@
... """)
>>> login('foo.bar@xxxxxxxxxxxxx')
- >>> from lp.registry.interfaces.person import IPersonSet
- >>> from zope.component import getUtility
>>> person_set = getUtility(IPersonSet)
>>> jdub = person_set.getByName('jdub')
>>> mark = person_set.getByName('mark')
@@ -420,14 +433,14 @@
>>> ignored = rosetta_admins.addMember(jordi, reviewer=mark)
>>> rosetta_admins.mailing_list.subscribe(jordi)
>>> logout()
- >>> browser.reload()
- >>> print extract_text(find_tag_by_id(browser.contents, 'subscribers'))
+ >>> carlos_browser.reload()
+ >>> print extract_text(find_tag_by_id(carlos_browser.contents, 'subscribers'))
The following people are subscribed...
Guilherme Salgado
1 of 2 results...
- >>> browser.getLink('Next').click()
- >>> print extract_text(find_tag_by_id(browser.contents, 'subscribers'))
+ >>> carlos_browser.getLink('Next').click()
+ >>> print extract_text(find_tag_by_id(carlos_browser.contents, 'subscribers'))
The following people are subscribed...
Jordi Mallach
2 of 2 results...
@@ -452,15 +465,14 @@
Launchpad may automatically subscribe a person to a team's mailing
list based on a setting in the person's Email preferences page.
- >>> browser = setupBrowser(auth='Basic carlos@xxxxxxxxxxxxx:test')
- >>> browser.open('http://launchpad.dev/~carlos')
- >>> browser.getLink(url="+editemails").click()
- >>> print backslashreplace(browser.title)
+ >>> carlos_browser.open('http://launchpad.dev/~carlos')
+ >>> carlos_browser.getLink(url="+editemails").click()
+ >>> print backslashreplace(carlos_browser.title)
Change your e-mail settings...
Carlos's default setting, 'Ask me when I join a team', is still in place.
- >>> print_radio_button_field(browser.contents,
+ >>> print_radio_button_field(carlos_browser.contents,
... 'mailing_list_auto_subscribe_policy')
( ) Never subscribe to mailing lists
(*) Ask me when I join a team
@@ -471,32 +483,32 @@
# A convenient helper for setting and submitting a new
# auto-subscribe policy.
- >>> def set_autosubscribe_policy_and_submit(newvalue):
- ... control = browser.getControl(
+ >>> def set_autosubscribe_policy_and_submit(newvalue, current_browser):
+ ... control = current_browser.getControl(
... name='field.mailing_list_auto_subscribe_policy')
... control.value = [newvalue]
- ... browser.getControl('Update Policy').click()
- ... print_radio_button_field(browser.contents,
+ ... current_browser.getControl('Update Policy').click()
+ ... print_radio_button_field(current_browser.contents,
... 'mailing_list_auto_subscribe_policy')
- >>> original_value = browser.getControl(
+ >>> original_value = carlos_browser.getControl(
... name='field.mailing_list_auto_subscribe_policy').value.pop()
- >>> set_autosubscribe_policy_and_submit('ALWAYS')
+ >>> set_autosubscribe_policy_and_submit('ALWAYS', carlos_browser)
( ) Never subscribe to mailing lists
( ) Ask me when I join a team
(*) Always subscribe me to mailing lists
# We only need to check this once.
- >>> get_feedback_messages(browser.contents)
+ >>> get_feedback_messages(carlos_browser.contents)
[u'Your auto-subscription policy has been updated.']
- >>> set_autosubscribe_policy_and_submit('NEVER')
+ >>> set_autosubscribe_policy_and_submit('NEVER', carlos_browser)
(*) Never subscribe to mailing lists
( ) Ask me when I join a team
( ) Always subscribe me to mailing lists
- >>> set_autosubscribe_policy_and_submit('ON_REGISTRATION')
+ >>> set_autosubscribe_policy_and_submit('ON_REGISTRATION', carlos_browser)
( ) Never subscribe to mailing lists
(*) Ask me when I join a team
( ) Always subscribe me to mailing lists
@@ -505,7 +517,7 @@
# Restores the original value while performing the test.
>>> assert original_value == 'ON_REGISTRATION'
- >>> set_autosubscribe_policy_and_submit(original_value)
+ >>> set_autosubscribe_policy_and_submit(original_value, carlos_browser)
( ) Never subscribe to mailing lists
(*) Ask me when I join a team
( ) Always subscribe me to mailing lists
@@ -516,7 +528,7 @@
behavior.
>>> print extract_text(
- ... find_tag_by_id(browser.contents, 'notification-info'))
+ ... find_tag_by_id(carlos_browser.contents, 'notification-info'))
When a team you are a member of creates a new mailing list, you will
receive an email notification offering you the opportunity to join the new
mailing list. Launchpad can also automatically subscribe you to a team's
@@ -528,9 +540,11 @@
team. Users who have chosen the 'On registration' or 'Always'
subscription settings will see the box checked by default.
- >>> browser = setupBrowser(
- ... auth='Basic james.blackwell@xxxxxxxxxxxxxxx:test')
-
+ >>> login(ANONYMOUS)
+ >>> james = getUtility(IPersonSet).getByEmail(
+ ... 'james.blackwell@xxxxxxxxxxxxxxx')
+ >>> logout()
+ >>> browser = setupBrowserFreshLogin(james)
>>> browser.open('http://launchpad.dev/~jblack')
>>> browser.getLink(url="+editemails").click()
>>> print_radio_button_field(browser.contents,
@@ -550,7 +564,7 @@
# Change James' setting
>>> browser.open('http://launchpad.dev/~jblack')
>>> browser.getLink(url="+editemails").click()
- >>> set_autosubscribe_policy_and_submit('ALWAYS')
+ >>> set_autosubscribe_policy_and_submit('ALWAYS', browser)
( ) Never subscribe to mailing lists
( ) Ask me when I join a team
(*) Always subscribe me to mailing lists
@@ -566,7 +580,7 @@
# Change James' setting
>>> browser.open('http://launchpad.dev/~jblack')
>>> browser.getLink(url="+editemails").click()
- >>> set_autosubscribe_policy_and_submit('NEVER')
+ >>> set_autosubscribe_policy_and_submit('NEVER', browser)
(*) Never subscribe to mailing lists
( ) Ask me when I join a team
( ) Always subscribe me to mailing lists
@@ -579,7 +593,7 @@
# Restore James' setting.
>>> browser.open('http://launchpad.dev/~jblack')
>>> browser.getLink(url="+editemails").click()
- >>> set_autosubscribe_policy_and_submit('ON_REGISTRATION')
+ >>> set_autosubscribe_policy_and_submit('ON_REGISTRATION', browser)
( ) Never subscribe to mailing lists
(*) Ask me when I join a team
( ) Always subscribe me to mailing lists
=== modified file 'lib/lp/services/webapp/login.py'
--- lib/lp/services/webapp/login.py 2012-08-07 18:27:27 +0000
+++ lib/lp/services/webapp/login.py 2012-08-07 18:27:30 +0000
@@ -338,13 +338,13 @@
finally:
timeline_action.finish()
- def login(self, person):
+ def login(self, person, when=None):
loginsource = getUtility(IPlacelessLoginSource)
# We don't have a logged in principal, so we must remove the security
# proxy of the account's preferred email.
email = removeSecurityProxy(person.preferredemail).email
logInPrincipal(
- self.request, loginsource.getPrincipalByLogin(email), email)
+ self.request, loginsource.getPrincipalByLogin(email), email, when)
@cachedproperty
def sreg_response(self):
@@ -475,7 +475,18 @@
template = ViewPageTemplateFile("templates/login-already.pt")
-def logInPrincipal(request, principal, email):
+def isFreshLogin(request):
+ """Return True if the principal login happened in the last 120 seconds."""
+ session = ISession(request)
+ authdata = session['launchpad.authenticateduser']
+ logintime = authdata.get('logintime', None)
+ if logintime is not None:
+ now = datetime.utcnow()
+ return logintime > now - timedelta(seconds=120)
+ return False
+
+
+def logInPrincipal(request, principal, email, when=None):
"""Log the principal in. Password validation must be done in callsites."""
# Force a fresh session, per Bug #828638. Any changes to any
# existing session made this request will be lost, but that should
@@ -488,8 +499,10 @@
authdata = session['launchpad.authenticateduser']
assert principal.id is not None, 'principal.id is None!'
request.setPrincipal(principal)
+ if when is None:
+ when = datetime.utcnow()
authdata['accountid'] = principal.id
- authdata['logintime'] = datetime.utcnow()
+ authdata['logintime'] = when
authdata['login'] = email
notify(CookieAuthLoggedInEvent(request, email))
=== modified file 'lib/lp/services/webapp/tests/test_session.py'
--- lib/lp/services/webapp/tests/test_session.py 2012-03-22 23:21:24 +0000
+++ lib/lp/services/webapp/tests/test_session.py 2012-08-07 18:27:30 +0000
@@ -1,14 +1,25 @@
# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
+import datetime
+
from testtools import TestCase
from testtools.matchers import Contains
+from lp.services.webapp.login import (
+ isFreshLogin,
+ OpenIDCallbackView,
+ )
from lp.services.webapp.servers import LaunchpadTestRequest
from lp.services.webapp.session import (
get_cookie_domain,
LaunchpadCookieClientIdManager,
)
+from lp.testing import (
+ person_logged_in,
+ TestCaseWithFactory,
+ )
+from lp.testing.layers import DatabaseFunctionalLayer
class GetCookieDomainTestCase(TestCase):
@@ -55,3 +66,34 @@
self.assertThat(
dict(request.response.getHeaders())['Set-Cookie'],
Contains('; httponly;'))
+
+
+class TestSessionRelatedFunctions(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def setupLoggedInRequest(self, user, request, when=None):
+ """Test helper to login a user for a request."""
+ with person_logged_in(user):
+ view = OpenIDCallbackView(user, request)
+ view.login(user, when)
+
+ def test_isFreshLogin_returns_false_for_anonymous(self):
+ """isFreshLogin should return False for anonymous views."""
+ request = LaunchpadTestRequest()
+ self.assertFalse(isFreshLogin(request))
+
+ def test_isFreshLogin_returns_true(self):
+ """isFreshLogin should return True with a fresh logged in user."""
+ user = self.factory.makePerson()
+ request = LaunchpadTestRequest()
+ self.setupLoggedInRequest(user, request)
+ self.assertTrue(isFreshLogin(request))
+
+ def test_isFreshLogin_returns_false(self):
+ """isFreshLogin should be False for users logged in over 2 minutes."""
+ user = self.factory.makePerson()
+ request = LaunchpadTestRequest()
+ when = datetime.datetime.utcnow() - datetime.timedelta(seconds=180)
+ self.setupLoggedInRequest(user, request, when)
+ self.assertFalse(isFreshLogin(request))
=== modified file 'lib/lp/testing/pages.py'
--- lib/lp/testing/pages.py 2012-06-06 16:04:34 +0000
+++ lib/lp/testing/pages.py 2012-08-07 18:27:30 +0000
@@ -5,6 +5,7 @@
__metaclass__ = type
+from datetime import datetime
import doctest
import os
import pdb
@@ -37,18 +38,21 @@
SimpleCookie,
)
from zope.component import getUtility
+from zope.session.interfaces import ISession
from zope.security.proxy import removeSecurityProxy
from zope.testbrowser.testing import Browser
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
from lp.registry.errors import NameAlreadyTaken
from lp.registry.interfaces.teammembership import TeamMembershipStatus
+from lp.services.config import config
from lp.services.oauth.interfaces import (
IOAuthConsumerSet,
OAUTH_REALM,
)
from lp.services.webapp import canonical_url
from lp.services.webapp.interfaces import OAuthPermission
+from lp.services.webapp.servers import LaunchpadTestRequest
from lp.services.webapp.url import urlsplit
from lp.testing import (
ANONYMOUS,
@@ -684,6 +688,24 @@
return setupBrowser(auth="Basic %s:test" % str(email))
+def setupBrowserFreshLogin(user):
+ """Create a test browser with a recently logged in user.
+
+ The request is not shared by the browser, so we create
+ a session of the test request and set a cookie to reference
+ the session in the test browser.
+ """
+ request = LaunchpadTestRequest()
+ session = ISession(request)
+ authdata = session['launchpad.authenticateduser']
+ authdata['logintime'] = datetime.utcnow()
+ namespace = config.launchpad_session.cookie
+ cookie = '%s=%s' % (namespace, session.client_id)
+ browser = setupBrowserForUser(user)
+ browser.addHeader('Cookie', cookie)
+ return browser
+
+
def safe_canonical_url(*args, **kwargs):
"""Generate a bytestring URL for an object"""
return str(canonical_url(*args, **kwargs))
Follow ups