William Grant has proposed merging lp:~wgrant/launchpad/bug-918843 into
lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #918843 in Launchpad itself: "IntegrityError: new row for relation
"person" violates check constraint "teams_have_no_account" "
https://bugs.launchpad.net/launchpad/+bug/918843
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/bug-918843/+merge/89392
My refactoring of the login machinery last week broke some untested and
probably unintentional behaviour: if the OpenID provider returned an email
address associated with a team, a new person would be created and steal the
team's address. After my refactoring this case crashes instead.
Since stealing email addresses from teams is probably not something that we
want to happen accidentally, this branch rejects login attempts that try to use
a team email address. feedback@ can then sort out the SSO account or team email
address to let authentication succeed.
SCA also uses that API, but https://pastebin.canonical.com/58485/ and
https://pastebin.canonical.com/58486/ suggest that it will handle it like
AccountSuspendedError, and this is done before payment.
A proper solution that does not induce nausea is achievable, but as discussed
in bug #556680 it is somewhat non-trivial.
--
https://code.launchpad.net/~wgrant/launchpad/bug-918843/+merge/89392
Your team Launchpad code reviewers is requested to review the proposed merge of
lp:~wgrant/launchpad/bug-918843 into lp:launchpad.
=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py 2012-01-15 11:52:24 +0000
+++ lib/lp/registry/interfaces/person.py 2012-01-20 07:03:32 +0000
@@ -35,6 +35,7 @@
'PersonalStanding',
'PRIVATE_TEAM_PREFIX',
'TeamContactMethod',
+ 'TeamEmailAddressError',
'TeamMembershipRenewalPolicy',
'TeamSubscriptionPolicy',
'validate_person',
@@ -2542,6 +2543,10 @@
_message_prefix = "No such person"
+class TeamEmailAddressError(Exception):
+ """The person cannot be created as a team owns its email address."""
+
+
# Fix value_type.schema of IPersonViewRestricted attributes.
for name in [
'all_members_prepopulated',
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py 2012-01-17 23:55:44 +0000
+++ lib/lp/registry/model/person.py 2012-01-20 07:03:32 +0000
@@ -180,6 +180,7 @@
PersonalStanding,
PersonCreationRationale,
PersonVisibility,
+ TeamEmailAddressError,
TeamMembershipRenewalPolicy,
TeamSubscriptionPolicy,
validate_public_person,
@@ -3094,6 +3095,13 @@
identifier = IStore(OpenIdIdentifier).find(
OpenIdIdentifier, identifier=openid_identifier).one()
+ # XXX wgrant 2012-01-20 bug=556680: This is awful, as it can
+ # lock people out of their account until they change their
+ # SSO address. But stealing addresses from other accounts is
+ # probably worse.
+ if email is not None and email.person.is_team:
+ raise TeamEmailAddressError()
+
if email is None:
if identifier is None:
# Neither the Email Address not the OpenId Identifier
=== modified file 'lib/lp/registry/tests/test_personset.py'
--- lib/lp/registry/tests/test_personset.py 2012-01-12 08:13:59 +0000
+++ lib/lp/registry/tests/test_personset.py 2012-01-20 07:03:32 +0000
@@ -33,6 +33,7 @@
IPersonSet,
PersonCreationRationale,
PersonVisibility,
+ TeamEmailAddressError,
)
from lp.registry.interfaces.personnotification import IPersonNotificationSet
from lp.registry.model.accesspolicy import AccessPolicyGrant
@@ -793,6 +794,18 @@
self.assertIs(found.account, self.identifier.account)
self.assertIn(self.identifier, list(found.account.openid_identifiers))
+ def testTeamEmailAddress(self):
+ # If the EmailAddress is linked to a team, login fails. There is
+ # no way to automatically recover -- someone must manually fix
+ # the email address of the team or the SSO account.
+ self.factory.makeTeam(email="[email protected]")
+
+ self.assertRaises(
+ TeamEmailAddressError,
+ self.person_set.getOrCreateByOpenIDIdentifier,
+ u'other-openid-identifier', '[email protected]', 'New Name',
+ PersonCreationRationale.UNKNOWN, 'No Comment')
+
class TestCreatePersonAndEmail(TestCase):
"""Test `IPersonSet`.createPersonAndEmail()."""
=== modified file 'lib/lp/registry/tests/test_xmlrpc.py'
--- lib/lp/registry/tests/test_xmlrpc.py 2012-01-05 00:23:45 +0000
+++ lib/lp/registry/tests/test_xmlrpc.py 2012-01-20 07:03:32 +0000
@@ -86,7 +86,7 @@
removeSecurityProxy(
person.account).openid_identifiers.any().identifier)
- def test_getOrCreateSoftwareCenterCustomer_xmlrpc_error(self):
+ def test_getOrCreateSoftwareCenterCustomer_xmlrpc_suspended(self):
# A suspended account results in an appropriate xmlrpc fault.
suspended_person = self.factory.makePerson(
displayname='Joe Blogs', email='[email protected]',
@@ -106,6 +106,22 @@
self.assertTrue(fault_raised)
+ def test_getOrCreateSoftwareCenterCustomer_xmlrpc_team(self):
+ # A team email address results in an appropriate xmlrpc fault.
+ self.factory.makeTeam(email='[email protected]')
+
+ # assertRaises doesn't let us check the type of Fault.
+ fault_raised = False
+ try:
+ self.rpc_proxy.getOrCreateSoftwareCenterCustomer(
+ 'foo', '[email protected]', 'Joe Blogs')
+ except xmlrpclib.Fault, e:
+ fault_raised = True
+ self.assertEqual(400, e.faultCode)
+ self.assertIn('[email protected]', e.faultString)
+
+ self.assertTrue(fault_raised)
+
def test_not_available_on_public_api(self):
# The person set api is not available on the public xmlrpc
# service.
=== modified file 'lib/lp/registry/xmlrpc/softwarecenteragent.py'
--- lib/lp/registry/xmlrpc/softwarecenteragent.py 2012-01-01 02:58:52 +0000
+++ lib/lp/registry/xmlrpc/softwarecenteragent.py 2012-01-20 07:03:32 +0000
@@ -17,6 +17,7 @@
ISoftwareCenterAgentAPI,
ISoftwareCenterAgentApplication,
PersonCreationRationale,
+ TeamEmailAddressError,
)
from lp.services.identity.interfaces.account import AccountSuspendedError
from lp.services.webapp import LaunchpadXMLRPCView
@@ -38,6 +39,8 @@
"when purchasing an application via Software Center.")
except AccountSuspendedError:
return faults.AccountSuspended(openid_identifier)
+ except TeamEmailAddressError:
+ return faults.TeamEmailAddress(email, openid_identifier)
return person.name
@@ -47,4 +50,3 @@
implements(ISoftwareCenterAgentApplication)
title = "Software Center Agent API"
-
=== modified file 'lib/lp/services/webapp/login.py'
--- lib/lp/services/webapp/login.py 2012-01-03 11:08:31 +0000
+++ lib/lp/services/webapp/login.py 2012-01-20 07:03:32 +0000
@@ -42,6 +42,7 @@
from lp.registry.interfaces.person import (
IPersonSet,
PersonCreationRationale,
+ TeamEmailAddressError,
)
from lp.services.config import config
from lp.services.database.readonly import is_read_only
@@ -295,6 +296,9 @@
suspended_account_template = ViewPageTemplateFile(
'templates/login-suspended-account.pt')
+ team_email_address_template = ViewPageTemplateFile(
+ 'templates/login-team-email-address.pt')
+
def _gather_params(self, request):
params = dict(request.form)
for key, value in request.query_string_params.iteritems():
@@ -381,6 +385,8 @@
should_update_last_write = db_updated
except AccountSuspendedError:
return self.suspended_account_template()
+ except TeamEmailAddressError:
+ return self.team_email_address_template()
with MasterDatabasePolicy():
self.login(person)
=== added file 'lib/lp/services/webapp/templates/login-team-email-address.pt'
--- lib/lp/services/webapp/templates/login-team-email-address.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/services/webapp/templates/login-team-email-address.pt 2012-01-20 07:03:32 +0000
@@ -0,0 +1,22 @@
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ metal:use-macro="view/macro:page/locationless"
+ i18n:domain="launchpad">
+
+ <body>
+ <div class="top-portlet" metal:fill-slot="main">
+
+ <h1>Team email address conflict</h1>
+ <p class="error">
+ Your can't log in because your OpenID account's email address is
+ associated with a Launchpad team.
+ Contact <a href="mailto:[email protected]?subject=Team%20email%20address%20conflict"
+ >Launchpad Support</a> about this issue.
+ </p>
+
+ </div>
+ </body>
+</html>
=== modified file 'lib/lp/services/webapp/tests/test_login.py'
--- lib/lp/services/webapp/tests/test_login.py 2012-01-14 12:47:39 +0000
+++ lib/lp/services/webapp/tests/test_login.py 2012-01-20 07:03:32 +0000
@@ -424,6 +424,20 @@
main_content = extract_text(find_main_content(html))
self.assertIn('This account has been suspended', main_content)
+ def test_account_with_team_email_address(self):
+ # If the email address from the OpenID provider is owned by a
+ # team, there's not much we can do. See bug #556680 for
+ # discussions about a proper solution.
+ self.factory.makeTeam(email="[email protected]")
+ person = self.factory.makePerson()
+
+ with SRegResponse_fromSuccessResponse_stubbed():
+ view, html = self._createViewWithResponse(
+ person.account, email="[email protected]")
+ self.assertFalse(view.login_called)
+ main_content = extract_text(find_main_content(html))
+ self.assertIn('Team email address conflict', main_content)
+
def test_negative_openid_assertion(self):
# The OpenID provider responded with a negative assertion, so the
# login error page is shown.
=== modified file 'lib/lp/xmlrpc/configure.zcml'
--- lib/lp/xmlrpc/configure.zcml 2011-12-24 17:49:30 +0000
+++ lib/lp/xmlrpc/configure.zcml 2012-01-20 07:03:32 +0000
@@ -201,6 +201,9 @@
<class class="lp.xmlrpc.faults.AccountSuspended">
<require like_class="xmlrpclib.Fault" />
</class>
+ <class class="lp.xmlrpc.faults.TeamEmailAddress">
+ <require like_class="xmlrpclib.Fault" />
+ </class>
<class class="lp.xmlrpc.faults.InvalidSourcePackageName">
<require like_class="xmlrpclib.Fault" />
</class>
=== modified file 'lib/lp/xmlrpc/faults.py'
--- lib/lp/xmlrpc/faults.py 2011-08-11 21:24:28 +0000
+++ lib/lp/xmlrpc/faults.py 2012-01-20 07:03:32 +0000
@@ -39,6 +39,7 @@
'NotInTeam',
'NoUrlForBranch',
'RequiredParameterMissing',
+ 'TeamEmailAddress',
'UnexpectedStatusReport',
]
@@ -487,3 +488,17 @@
def __init__(self, name):
self.name = name
LaunchpadFault.__init__(self, name=name)
+
+
+class TeamEmailAddress(LaunchpadFault):
+ """Raised by `ISoftwareCenterAgentAPI` when an email address is a team."""
+
+ error_code = 400
+ msg_template = (
+ 'The email address \'%(email)s\' '
+ '(openid_identifier \'%(openid_identifier)s\') '
+ 'is linked to a Launchpad team.')
+
+ def __init__(self, email, openid_identifier):
+ LaunchpadFault.__init__(
+ self, email=email, openid_identifier=openid_identifier)
_______________________________________________
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : [email protected]
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help : https://help.launchpad.net/ListHelp