launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #26896
[Merge] ~cjwatson/launchpad:authserver-fixture into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:authserver-fixture into launchpad:master.
Commit message:
Move in-process authserver to lp.services.authserver.testing
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/401189
Since this is used by multiple test files, it should have a proper home rather than being imported from lp.snappy.tests.test_snapbuildbehaviour. Also make the fixture a bit more general by pointing more configuration entries at the in-process authserver, and refactor some librarian tests to use it.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:authserver-fixture into launchpad:master.
diff --git a/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py b/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
index 2337a42..1fe69f3 100644
--- a/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
+++ b/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
@@ -77,13 +77,13 @@ from lp.buildmaster.tests.test_buildfarmjobbehaviour import (
from lp.oci.interfaces.ocirecipe import OCI_RECIPE_ALLOW_CREATE
from lp.oci.model.ocirecipebuildbehaviour import OCIRecipeBuildBehaviour
from lp.registry.interfaces.series import SeriesStatus
+from lp.services.authserver.testing import InProcessAuthServerFixture
from lp.services.config import config
from lp.services.features.testing import FeatureFixture
from lp.services.log.logger import DevNullLogger
from lp.services.propertycache import get_property_cache
from lp.services.statsd.tests import StatsMixin
from lp.services.webapp import canonical_url
-from lp.snappy.tests.test_snapbuildbehaviour import InProcessAuthServerFixture
from lp.soyuz.adapters.archivedependencies import (
get_sources_list_for_building,
)
diff --git a/lib/lp/services/authserver/testing.py b/lib/lp/services/authserver/testing.py
new file mode 100644
index 0000000..ab47c87
--- /dev/null
+++ b/lib/lp/services/authserver/testing.py
@@ -0,0 +1,60 @@
+# Copyright 2021 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""In-process authserver fixture."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'InProcessAuthServerFixture',
+ ]
+
+from textwrap import dedent
+
+import fixtures
+from twisted.internet import reactor
+from twisted.web import (
+ server,
+ xmlrpc,
+ )
+from zope.component import getUtility
+from zope.publisher.xmlrpc import TestRequest
+
+from lp.services.authserver.xmlrpc import AuthServerAPIView
+from lp.services.config import config
+from lp.xmlrpc.interfaces import IPrivateApplication
+
+
+class InProcessAuthServer(xmlrpc.XMLRPC):
+
+ def __init__(self, *args, **kwargs):
+ xmlrpc.XMLRPC.__init__(self, *args, **kwargs)
+ private_root = getUtility(IPrivateApplication)
+ self.authserver = AuthServerAPIView(
+ private_root.authserver, TestRequest())
+
+ def __getattr__(self, name):
+ if name.startswith("xmlrpc_"):
+ return getattr(self.authserver, name[len("xmlrpc_"):])
+ else:
+ raise AttributeError("%r has no attribute '%s'" % name)
+
+
+class InProcessAuthServerFixture(fixtures.Fixture, xmlrpc.XMLRPC):
+ """A fixture that runs an in-process authserver."""
+
+ def _setUp(self):
+ listener = reactor.listenTCP(0, server.Site(InProcessAuthServer()))
+ self.addCleanup(listener.stopListening)
+ config.push("in-process-auth-server-fixture", dedent("""
+ [builddmaster]
+ authentication_endpoint: http://localhost:{port}/
+
+ [codehosting]
+ authentication_endpoint: http://localhost:{port}/
+
+ [librarian]
+ authentication_endpoint: http://localhost:{port}/
+ """).format(port=listener.getHost().port))
+ self.addCleanup(config.pop, "in-process-auth-server-fixture")
diff --git a/lib/lp/services/librarianserver/tests/test_db.py b/lib/lp/services/librarianserver/tests/test_db.py
index ac1552e..5329ae4 100644
--- a/lib/lp/services/librarianserver/tests/test_db.py
+++ b/lib/lp/services/librarianserver/tests/test_db.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2021 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import absolute_import, print_function, unicode_literals
@@ -10,23 +10,25 @@ from pymacaroons import Macaroon
from testtools.testcase import ExpectedException
from testtools.twistedsupport import AsynchronousDeferredRunTest
import transaction
-from twisted.internet import (
- defer,
- reactor,
- )
+from twisted.internet import defer
from twisted.internet.threads import deferToThread
-from twisted.web import (
- server,
- xmlrpc,
- )
+from zope.interface import implementer
+from lp.services.authserver.testing import InProcessAuthServerFixture
from lp.services.database.interfaces import IStore
+from lp.services.librarian.interfaces import ILibraryFileAlias
from lp.services.librarian.model import LibraryFileContent
from lp.services.librarianserver import db
+from lp.services.macaroons.interfaces import (
+ BadMacaroonContext,
+ IMacaroonIssuer,
+ NO_USER,
+ )
+from lp.services.macaroons.model import MacaroonIssuerBase
from lp.testing import TestCase
from lp.testing.dbuser import switch_dbuser
+from lp.testing.fixture import ZopeUtilityFixture
from lp.testing.layers import LaunchpadZopelessLayer
-from lp.xmlrpc import faults
class DBTestCase(TestCase):
@@ -61,26 +63,31 @@ class DBTestCase(TestCase):
self.assertEqual('text/unknown', alias.mimetype)
-class FakeAuthServer(xmlrpc.XMLRPC):
- """A fake authserver.
+@implementer(IMacaroonIssuer)
+class DummyMacaroonIssuer(MacaroonIssuerBase):
- This saves us from needing to start an appserver in tests.
- """
+ identifier = 'test'
+ _root_secret = 'test'
+ _verified_user = NO_USER
- def __init__(self):
- xmlrpc.XMLRPC.__init__(self)
- self.macaroons = set()
+ def checkIssuingContext(self, context, **kwargs):
+ """See `MacaroonIssuerBase`."""
+ if not ILibraryFileAlias.providedBy(context):
+ raise BadMacaroonContext(context)
+ return context.id
- def registerMacaroon(self, macaroon, context):
- self.macaroons.add((macaroon.serialize(), context))
+ def checkVerificationContext(self, context, **kwargs):
+ """See `IMacaroonIssuerBase`."""
+ if not ILibraryFileAlias.providedBy(context):
+ raise BadMacaroonContext(context)
+ return context
- def xmlrpc_verifyMacaroon(self, macaroon_raw, context_type, context):
- if context_type != 'LibraryFileAlias':
- return faults.Unauthorized()
- if (macaroon_raw, context) in self.macaroons:
- return True
- else:
- return faults.Unauthorized()
+ def verifyPrimaryCaveat(self, verified, caveat_value, context, **kwargs):
+ """See `MacaroonIssuerBase`."""
+ ok = caveat_value == str(context.id)
+ if ok:
+ verified.user = self._verified_user
+ return ok
class TestLibrarianStuff(TestCase):
@@ -144,28 +151,19 @@ class TestLibrarianStuff(TestCase):
self.assertRaises(
LookupError, unrestricted_library.getAlias, 1, None, '/')
- def setUpAuthServer(self):
- authserver = FakeAuthServer()
- authserver_listener = reactor.listenTCP(0, server.Site(authserver))
- self.addCleanup(authserver_listener.stopListening)
- authserver_port = authserver_listener.getHost().port
- authserver_url = 'http://localhost:%d/' % authserver_port
- self.pushConfig('librarian', authentication_endpoint=authserver_url)
- return authserver
-
@defer.inlineCallbacks
def test_getAlias_with_macaroon(self):
# Library.getAlias() uses the authserver to verify macaroons.
- authserver = self.setUpAuthServer()
+ issuer = DummyMacaroonIssuer()
+ self.useFixture(ZopeUtilityFixture(
+ issuer, IMacaroonIssuer, name='test'))
+ self.useFixture(InProcessAuthServerFixture())
unrestricted_library = db.Library(restricted=False)
alias = unrestricted_library.getAlias(1, None, '/')
alias.restricted = True
transaction.commit()
restricted_library = db.Library(restricted=True)
- macaroon = Macaroon()
- with ExpectedException(LookupError):
- yield deferToThread(restricted_library.getAlias, 1, macaroon, '/')
- authserver.registerMacaroon(macaroon, 1)
+ macaroon = issuer.issueMacaroon(alias)
alias = yield deferToThread(
restricted_library.getAlias, 1, macaroon, '/')
self.assertEqual(1, alias.id)
@@ -173,13 +171,16 @@ class TestLibrarianStuff(TestCase):
@defer.inlineCallbacks
def test_getAlias_with_wrong_macaroon(self):
# A macaroon for a different LFA doesn't work.
- authserver = self.setUpAuthServer()
+ issuer = DummyMacaroonIssuer()
+ self.useFixture(ZopeUtilityFixture(
+ issuer, IMacaroonIssuer, name='test'))
+ self.useFixture(InProcessAuthServerFixture())
unrestricted_library = db.Library(restricted=False)
alias = unrestricted_library.getAlias(1, None, '/')
alias.restricted = True
+ other_alias = unrestricted_library.getAlias(2, None, '/')
transaction.commit()
- macaroon = Macaroon()
- authserver.registerMacaroon(macaroon, 2)
+ macaroon = issuer.issueMacaroon(other_alias)
restricted_library = db.Library(restricted=True)
with ExpectedException(LookupError):
yield deferToThread(restricted_library.getAlias, 1, macaroon, '/')
diff --git a/lib/lp/snappy/tests/test_snapbuildbehaviour.py b/lib/lp/snappy/tests/test_snapbuildbehaviour.py
index c64749d..d223596 100644
--- a/lib/lp/snappy/tests/test_snapbuildbehaviour.py
+++ b/lib/lp/snappy/tests/test_snapbuildbehaviour.py
@@ -1,4 +1,4 @@
-# Copyright 2015-2020 Canonical Ltd. This software is licensed under the
+# Copyright 2015-2021 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Test snap package build behaviour."""
@@ -34,17 +34,9 @@ from testtools.twistedsupport import (
AsynchronousDeferredRunTestForBrokenTwisted,
)
import transaction
-from twisted.internet import (
- defer,
- reactor,
- )
-from twisted.web import (
- server,
- xmlrpc,
- )
+from twisted.internet import defer
from zope.component import getUtility
from zope.proxy import isProxy
-from zope.publisher.xmlrpc import TestRequest
from zope.security.proxy import removeSecurityProxy
from lp.app.enums import InformationType
@@ -78,7 +70,7 @@ from lp.buildmaster.tests.test_buildfarmjobbehaviour import (
)
from lp.registry.interfaces.pocket import PackagePublishingPocket
from lp.registry.interfaces.series import SeriesStatus
-from lp.services.authserver.xmlrpc import AuthServerAPIView
+from lp.services.authserver.testing import InProcessAuthServerFixture
from lp.services.config import config
from lp.services.features.testing import FeatureFixture
from lp.services.log.logger import (
@@ -114,35 +106,6 @@ from lp.testing.gpgkeys import (
)
from lp.testing.keyserver import InProcessKeyServerFixture
from lp.testing.layers import LaunchpadZopelessLayer
-from lp.xmlrpc.interfaces import IPrivateApplication
-
-
-class InProcessAuthServer(xmlrpc.XMLRPC):
-
- def __init__(self, *args, **kwargs):
- xmlrpc.XMLRPC.__init__(self, *args, **kwargs)
- private_root = getUtility(IPrivateApplication)
- self.authserver = AuthServerAPIView(
- private_root.authserver, TestRequest())
-
- def __getattr__(self, name):
- if name.startswith("xmlrpc_"):
- return getattr(self.authserver, name[len("xmlrpc_"):])
- else:
- raise AttributeError("%r has no attribute '%s'" % name)
-
-
-class InProcessAuthServerFixture(fixtures.Fixture, xmlrpc.XMLRPC):
- """A fixture that runs an in-process authserver."""
-
- def _setUp(self):
- listener = reactor.listenTCP(0, server.Site(InProcessAuthServer()))
- self.addCleanup(listener.stopListening)
- config.push("in-process-auth-server-fixture", dedent("""
- [builddmaster]
- authentication_endpoint: http://localhost:%d/
- """) % listener.getHost().port)
- self.addCleanup(config.pop, "in-process-auth-server-fixture")
class FormatAsRfc3339TestCase(TestCase):