launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #06499
[Merge] lp:~wgrant/launchpad/multipolicy-4 into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/multipolicy-4 into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/multipolicy-4/+merge/94721
Introduce AccessPolicyArtifact model and interface and an example of querying AccessPolicyGrantFlat. More APGF stuff is to come once we have defined the needed APIs.
I also fixed AccessArtifactGrant and AccessPolicyGrant's date_created fields to be UTC in Python.
--
https://code.launchpad.net/~wgrant/launchpad/multipolicy-4/+merge/94721
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/multipolicy-4 into lp:launchpad.
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2012-02-24 04:29:13 +0000
+++ lib/lp/registry/configure.zcml 2012-02-27 06:22:21 +0000
@@ -1930,6 +1930,17 @@
interface="lp.registry.interfaces.accesspolicy.IAccessArtifactSource"/>
</securedutility>
<class
+ class="lp.registry.model.accesspolicy.AccessPolicyArtifact">
+ <allow
+ interface="lp.registry.interfaces.accesspolicy.IAccessPolicyArtifact"/>
+ </class>
+ <securedutility
+ component="lp.registry.model.accesspolicy.AccessPolicyArtifact"
+ provides="lp.registry.interfaces.accesspolicy.IAccessPolicyArtifactSource">
+ <allow
+ interface="lp.registry.interfaces.accesspolicy.IAccessPolicyArtifactSource"/>
+ </securedutility>
+ <class
class="lp.registry.model.accesspolicy.AccessArtifactGrant">
<allow
interface="lp.registry.interfaces.accesspolicy.IAccessArtifactGrant"/>
@@ -1951,4 +1962,10 @@
<allow
interface="lp.registry.interfaces.accesspolicy.IAccessPolicyGrantSource"/>
</securedutility>
+ <securedutility
+ component="lp.registry.model.accesspolicy.AccessPolicyGrantFlat"
+ provides="lp.registry.interfaces.accesspolicy.IAccessPolicyGrantFlatSource">
+ <allow
+ interface="lp.registry.interfaces.accesspolicy.IAccessPolicyGrantFlatSource"/>
+ </securedutility>
</configure>
=== modified file 'lib/lp/registry/interfaces/accesspolicy.py'
--- lib/lp/registry/interfaces/accesspolicy.py 2012-02-26 23:55:59 +0000
+++ lib/lp/registry/interfaces/accesspolicy.py 2012-02-27 06:22:21 +0000
@@ -11,7 +11,10 @@
'IAccessArtifactGrantSource',
'IAccessArtifactSource',
'IAccessPolicy',
+ 'IAccessPolicyArtifact',
+ 'IAccessPolicyArtifactSource',
'IAccessPolicyGrant',
+ 'IAccessPolicyGrantFlatSource',
'IAccessPolicyGrantSource',
'IAccessPolicySource',
]
@@ -59,6 +62,17 @@
type = Attribute("Type")
+class IAccessPolicyArtifact(Interface):
+ """An association between an artifact and a policy.
+
+ For example, an security bug in Ubuntu associated with the Ubuntu
+ security policy so people with a grant for that policy can see it.
+ """
+
+ abstract_artifact = Attribute("Abstract artifact")
+ policy = Attribute("Access policy")
+
+
class IAccessPolicyGrant(Interface):
"""A grant for a person or team to access all of a policy's artifacts.
@@ -117,6 +131,29 @@
"""Delete all `IAccessArtifactGrant` objects for the artifacts."""
+class IAccessPolicyArtifactSource(Interface):
+
+ def create(links):
+ """Create `IAccessPolicyArtifacts`s.
+
+ :param links: a collection of (`IAccessArtifact`, `IAccessPolicy`)
+ pairs to link.
+ """
+
+ def find(links):
+ """Return the specified `IAccessPolicyArtifacts`s if they exist.
+
+ :param links: a collection of (`IAccessArtifact`, `IAccessPolicy`)
+ pairs.
+ """
+
+ def findByArtifact(artifacts):
+ """Return all `IAccessPolicyArtifact` objects for the artifacts."""
+
+ def findByPolicy(policies):
+ """Return all `IAccessPolicyArtifact` objects for the policies."""
+
+
class IAccessPolicySource(Interface):
def create(pillars_and_types):
@@ -161,4 +198,16 @@
"""
def findByPolicy(policies):
- """Return all `IAccessPolicyGrant` objects for the artifacts."""
+ """Return all `IAccessPolicyGrant` objects for the policies."""
+
+
+class IAccessPolicyGrantFlatSource(Interface):
+ """Experimental query utility to search through the flattened schema."""
+
+ def findGranteesByPolicy(policies):
+ """Find the `IPerson`s with access grants for the policies.
+
+ This includes grants for artifacts in the policies.
+
+ :param policies: a collection of `IAccesPolicy`s.
+ """
=== modified file 'lib/lp/registry/model/accesspolicy.py'
--- lib/lp/registry/model/accesspolicy.py 2012-02-27 00:01:06 +0000
+++ lib/lp/registry/model/accesspolicy.py 2012-02-27 06:22:21 +0000
@@ -7,9 +7,11 @@
__all__ = [
'AccessArtifact',
'AccessPolicy',
+ 'AccessPolicyArtifact',
'AccessPolicyGrant',
]
+import pytz
from storm.expr import (
And,
Or,
@@ -28,8 +30,10 @@
IAccessArtifactGrant,
IAccessArtifactGrantSource,
IAccessPolicy,
+ IAccessPolicyArtifact,
IAccessPolicyGrant,
)
+from lp.registry.model.person import Person
from lp.services.database.bulk import create
from lp.services.database.enumcol import DBEnum
from lp.services.database.lpstorm import IStore
@@ -178,6 +182,47 @@
Or(*(cls._constraintForPillar(pillar) for pillar in pillars)))
+class AccessPolicyArtifact(StormBase):
+ implements(IAccessPolicyArtifact)
+
+ __storm_table__ = 'AccessPolicyArtifact'
+ __storm_primary__ = 'abstract_artifact_id', 'policy_id'
+
+ abstract_artifact_id = Int(name='artifact')
+ abstract_artifact = Reference(
+ abstract_artifact_id, 'AccessArtifact.id')
+ policy_id = Int(name='policy')
+ policy = Reference(policy_id, 'AccessPolicy.id')
+
+ @classmethod
+ def create(cls, links):
+ """See `IAccessPolicyArtifactSource`."""
+ return create(
+ (cls.abstract_artifact, cls.policy), links,
+ get_objects=True)
+
+ @classmethod
+ def find(cls, links):
+ """See `IAccessArtifactGrantSource`."""
+ return IStore(cls).find(
+ cls,
+ Or(*(
+ And(cls.abstract_artifact == artifact, cls.policy == policy)
+ for (artifact, policy) in links)))
+
+ @classmethod
+ def findByArtifact(cls, artifacts):
+ """See `IAccessPolicyArtifactSource`."""
+ ids = [artifact.id for artifact in artifacts]
+ return IStore(cls).find(cls, cls.abstract_artifact_id.is_in(ids))
+
+ @classmethod
+ def findByPolicy(cls, policies):
+ """See `IAccessPolicyArtifactSource`."""
+ ids = [policy.id for policy in policies]
+ return IStore(cls).find(cls, cls.policy_id.is_in(ids))
+
+
class AccessArtifactGrant(StormBase):
implements(IAccessArtifactGrant)
@@ -191,7 +236,7 @@
grantee = Reference(grantee_id, 'Person.id')
grantor_id = Int(name='grantor')
grantor = Reference(grantor_id, 'Person.id')
- date_created = DateTime()
+ date_created = DateTime(tzinfo=pytz.UTC)
@property
def concrete_artifact(self):
@@ -238,7 +283,7 @@
grantee = Reference(grantee_id, 'Person.id')
grantor_id = Int(name='grantor')
grantor = Reference(grantor_id, 'Person.id')
- date_created = DateTime()
+ date_created = DateTime(tzinfo=pytz.UTC)
@classmethod
def grant(cls, grants):
@@ -260,3 +305,23 @@
"""See `IAccessPolicyGrantSource`."""
ids = [policy.id for policy in policies]
return IStore(cls).find(cls, cls.policy_id.is_in(ids))
+
+
+class AccessPolicyGrantFlat(StormBase):
+ __storm_table__ = 'AccessPolicyGrantFlat'
+
+ id = Int(primary=True)
+ policy_id = Int(name='policy')
+ policy = Reference(policy_id, 'AccessPolicy.id')
+ abstract_artifact_id = Int(name='artifact')
+ abstract_artifact = Reference(
+ abstract_artifact_id, 'AccessArtifact.id')
+ grantee_id = Int(name='grantee')
+ grantee = Reference(grantee_id, 'Person.id')
+
+ @classmethod
+ def findGranteesByPolicy(cls, policies):
+ """See `IAccessPolicyGrantFlatSource`."""
+ ids = [policy.id for policy in policies]
+ return IStore(cls).find(
+ Person, Person.id == cls.grantee_id, cls.policy_id.is_in(ids))
=== modified file 'lib/lp/registry/tests/test_accesspolicy.py'
--- lib/lp/registry/tests/test_accesspolicy.py 2012-02-26 23:46:11 +0000
+++ lib/lp/registry/tests/test_accesspolicy.py 2012-02-27 06:22:21 +0000
@@ -4,19 +4,20 @@
__metaclass__ = type
from storm.exceptions import LostObjectError
-from testtools.matchers import (
- AllMatch,
- )
+from testtools.matchers import AllMatch
from zope.component import getUtility
from lp.registry.enums import AccessPolicyType
from lp.registry.interfaces.accesspolicy import (
- IAccessPolicy,
IAccessArtifact,
IAccessArtifactGrant,
IAccessArtifactGrantSource,
IAccessArtifactSource,
+ IAccessPolicy,
+ IAccessPolicyArtifact,
+ IAccessPolicyArtifactSource,
IAccessPolicyGrant,
+ IAccessPolicyGrantFlatSource,
IAccessPolicyGrantSource,
IAccessPolicySource,
)
@@ -264,6 +265,61 @@
getUtility(IAccessArtifactGrantSource).findByArtifact([artifact]))
+class TestAccessPolicyArtifact(TestCaseWithFactory):
+ layer = DatabaseFunctionalLayer
+
+ def test_provides_interface(self):
+ self.assertThat(
+ self.factory.makeAccessPolicyArtifact(),
+ Provides(IAccessPolicyArtifact))
+
+
+class TestAccessPolicyArtifactSource(TestCaseWithFactory):
+ layer = DatabaseFunctionalLayer
+
+ def test_create(self):
+ wanted = [
+ (self.factory.makeAccessArtifact(),
+ self.factory.makeAccessPolicy()),
+ (self.factory.makeAccessArtifact(),
+ self.factory.makeAccessPolicy()),
+ ]
+ links = getUtility(IAccessPolicyArtifactSource).create(wanted)
+ self.assertContentEqual(
+ wanted,
+ [(link.abstract_artifact, link.policy) for link in links])
+
+ def test_find(self):
+ links = [self.factory.makeAccessPolicyArtifact() for i in range(3)]
+ self.assertContentEqual(
+ links,
+ getUtility(IAccessPolicyArtifactSource).find(
+ [(link.abstract_artifact, link.policy) for link in links]))
+
+ def test_findByArtifact(self):
+ # findByArtifact() finds only the relevant links.
+ artifact = self.factory.makeAccessArtifact()
+ links = [
+ self.factory.makeAccessPolicyArtifact(artifact=artifact)
+ for i in range(3)]
+ self.factory.makeAccessPolicyArtifact()
+ self.assertContentEqual(
+ links,
+ getUtility(IAccessPolicyArtifactSource).findByArtifact(
+ [artifact]))
+
+ def test_findByPolicy(self):
+ # findByPolicy() finds only the relevant links.
+ policy = self.factory.makeAccessPolicy()
+ links = [
+ self.factory.makeAccessPolicyArtifact(policy=policy)
+ for i in range(3)]
+ self.factory.makeAccessPolicyArtifact()
+ self.assertContentEqual(
+ links,
+ getUtility(IAccessPolicyArtifactSource).findByPolicy([policy]))
+
+
class TestAccessPolicyGrant(TestCaseWithFactory):
layer = DatabaseFunctionalLayer
@@ -305,3 +361,30 @@
self.assertContentEqual(
grants,
getUtility(IAccessPolicyGrantSource).findByPolicy([policy]))
+
+
+class TestAccessPolicyGrantFlatSource(TestCaseWithFactory):
+ layer = DatabaseFunctionalLayer
+
+ def test_findGranteesByPolicy(self):
+ # findGranteesByPolicy() returns anyone with a grant for any of
+ # the policies or the policies' artifacts.
+ apgfs = getUtility(IAccessPolicyGrantFlatSource)
+
+ # People with grants on the policy show up.
+ policy = self.factory.makeAccessPolicy()
+ policy_grant = self.factory.makeAccessPolicyGrant(policy=policy)
+ self.assertContentEqual(
+ [policy_grant.grantee], apgfs.findGranteesByPolicy([policy]))
+
+ # But not people with grants on artifacts.
+ artifact_grant = self.factory.makeAccessArtifactGrant()
+ self.assertContentEqual(
+ [policy_grant.grantee], apgfs.findGranteesByPolicy([policy]))
+
+ # Unless the artifacts are linked to the policy.
+ self.factory.makeAccessPolicyArtifact(
+ artifact=artifact_grant.abstract_artifact, policy=policy)
+ self.assertContentEqual(
+ [policy_grant.grantee, artifact_grant.grantee],
+ apgfs.findGranteesByPolicy([policy]))
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2012-02-27 05:16:39 +0000
+++ lib/lp/testing/factory.py 2012-02-27 06:22:21 +0000
@@ -146,6 +146,7 @@
from lp.registry.interfaces.accesspolicy import (
IAccessArtifactGrantSource,
IAccessArtifactSource,
+ IAccessPolicyArtifactSource,
IAccessPolicyGrantSource,
IAccessPolicySource,
)
@@ -4390,6 +4391,15 @@
artifacts = getUtility(IAccessArtifactSource).ensure([concrete])
return artifacts[0]
+ def makeAccessPolicyArtifact(self, artifact=None, policy=None):
+ if artifact is None:
+ artifact = self.makeAccessArtifact()
+ if policy is None:
+ policy = self.makeAccessPolicy()
+ [link] = getUtility(IAccessPolicyArtifactSource).create(
+ [(artifact, policy)])
+ return link
+
def makeAccessArtifactGrant(self, artifact=None, grantee=None,
grantor=None):
if artifact is None: