launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #26601
[Merge] ~pappacena/launchpad:ocirecipe-private-accesspolicy into launchpad:master
Thiago F. Pappacena has proposed merging ~pappacena/launchpad:ocirecipe-private-accesspolicy into launchpad:master.
Commit message:
Allowing OCI recipes to be access artifacts
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/399394
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:ocirecipe-private-accesspolicy into launchpad:master.
diff --git a/lib/lp/oci/interfaces/ocirecipe.py b/lib/lp/oci/interfaces/ocirecipe.py
index a23fa03..26e48e0 100644
--- a/lib/lp/oci/interfaces/ocirecipe.py
+++ b/lib/lp/oci/interfaces/ocirecipe.py
@@ -61,6 +61,7 @@ from zope.schema import (
from zope.security.interfaces import Unauthorized
from lp import _
+from lp.app.enums import InformationType
from lp.app.errors import NameLookupFailed
from lp.app.validators.name import name_validator
from lp.app.validators.path import path_does_not_escape
@@ -380,6 +381,12 @@ class IOCIRecipeEditableAttributes(IHasOwner):
description=_("The owner of this OCI recipe."),
readonly=False))
+ information_type = exported(Choice(
+ title=_("Information type"), vocabulary=InformationType,
+ required=True, readonly=False, default=InformationType.PUBLIC,
+ description=_(
+ "The type of information contained in this OCI recipe.")))
+
oci_project = exported(Reference(
IOCIProject,
title=_("OCI project"),
@@ -492,7 +499,8 @@ class IOCIRecipeSet(Interface):
def new(name, registrant, owner, oci_project, git_ref, build_file,
description=None, official=False, require_virtualized=True,
build_daily=False, processors=None, date_created=DEFAULT,
- allow_internet=True, build_args=None):
+ allow_internet=True, build_args=None,
+ information_type=InformationType.PUBLIC):
"""Create an IOCIRecipe."""
def exists(owner, oci_project, name):
diff --git a/lib/lp/oci/model/ocirecipe.py b/lib/lp/oci/model/ocirecipe.py
index e4edcae..67fe0f8 100644
--- a/lib/lp/oci/model/ocirecipe.py
+++ b/lib/lp/oci/model/ocirecipe.py
@@ -44,6 +44,7 @@ from zope.security.proxy import (
removeSecurityProxy,
)
+from lp.app.enums import InformationType
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
from lp.app.interfaces.security import IAuthorization
from lp.app.validators.validation import validate_oci_branch_name
@@ -85,6 +86,7 @@ from lp.oci.model.ocirecipejob import OCIRecipeJob
from lp.registry.interfaces.distribution import IDistributionSet
from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.role import IPersonRoles
+from lp.registry.model.accesspolicy import reconcile_access_for_artifact
from lp.registry.model.distribution import Distribution
from lp.registry.model.distroseries import DistroSeries
from lp.registry.model.person import Person
@@ -95,6 +97,7 @@ from lp.services.database.constants import (
UTC_NOW,
)
from lp.services.database.decoratedresultset import DecoratedResultSet
+from lp.services.database.enumcol import DBEnum
from lp.services.database.interfaces import (
IMasterStore,
IStore,
@@ -141,6 +144,10 @@ class OCIRecipe(Storm, WebhookTargetMixin):
owner_id = Int(name='owner', allow_none=False)
owner = Reference(owner_id, 'Person.id')
+ _information_type = DBEnum(
+ enum=InformationType, default=InformationType.PUBLIC,
+ name="information_type")
+
oci_project_id = Int(name='oci_project', allow_none=False)
oci_project = Reference(oci_project_id, "OCIProject.id")
@@ -171,13 +178,14 @@ class OCIRecipe(Storm, WebhookTargetMixin):
description=None, official=False, require_virtualized=True,
build_file=None, build_daily=False, date_created=DEFAULT,
allow_internet=True, build_args=None, build_path=None,
- image_name=None):
+ image_name=None, information_type=InformationType.PUBLIC):
if not getFeatureFlag(OCI_RECIPE_ALLOW_CREATE):
raise OCIRecipeFeatureDisabled()
super(OCIRecipe, self).__init__()
self.name = name
self.registrant = registrant
self.owner = owner
+ self.information_type = information_type
self.oci_project = oci_project
self.description = description
self.build_file = build_file
@@ -198,6 +206,20 @@ class OCIRecipe(Storm, WebhookTargetMixin):
self.oci_project.name, self.name)
@property
+ def information_type(self):
+ if self._information_type is None:
+ return InformationType.PUBLIC
+ return self._information_type
+
+ @information_type.setter
+ def information_type(self, information_type):
+ self._information_type = information_type
+
+ @property
+ def pillar(self):
+ return self.oci_project.pillar
+
+ @property
def valid_webhook_event_types(self):
return ["oci-recipe:build:0.1"]
@@ -220,6 +242,15 @@ class OCIRecipe(Storm, WebhookTargetMixin):
self._build_args = {k: six.text_type(v)
for k, v in (value or {}).items()}
+ def _reconcileAccess(self):
+ """Reconcile the snap's sharing information.
+
+ Takes the privacy and pillar and makes the related AccessArtifact
+ and AccessPolicyArtifacts match.
+ """
+ reconcile_access_for_artifact(self, self.information_type,
+ [self.pillar])
+
def destroySelf(self):
"""See `IOCIRecipe`."""
# XXX twom 2019-11-26 This needs to expand as more build artifacts
@@ -609,7 +640,7 @@ class OCIRecipeSet:
description=None, official=False, require_virtualized=True,
build_daily=False, processors=None, date_created=DEFAULT,
allow_internet=True, build_args=None, build_path=None,
- image_name=None):
+ image_name=None, information_type=InformationType.PUBLIC):
"""See `IOCIRecipeSet`."""
if not registrant.inTeam(owner):
if owner.is_team:
@@ -634,8 +665,10 @@ class OCIRecipeSet:
oci_recipe = OCIRecipe(
name, registrant, owner, oci_project, git_ref, description,
official, require_virtualized, build_file, build_daily,
- date_created, allow_internet, build_args, build_path, image_name)
+ date_created, allow_internet, build_args, build_path, image_name,
+ information_type)
store.add(oci_recipe)
+ oci_recipe._reconcileAccess()
if processors is None:
processors = [
diff --git a/lib/lp/registry/browser/pillar.py b/lib/lp/registry/browser/pillar.py
index 52b1554..c5bfd3f 100644
--- a/lib/lp/registry/browser/pillar.py
+++ b/lib/lp/registry/browser/pillar.py
@@ -439,15 +439,22 @@ class PillarPersonSharingView(LaunchpadView):
def _loadSharedArtifacts(self):
# As a concrete can by linked via more than one policy, we use sets to
# filter out dupes.
- (self.bugtasks, self.branches, self.gitrepositories, self.snaps,
- self.specifications) = (
- self.sharing_service.getSharedArtifacts(
- self.pillar, self.person, self.user))
+ artifacts = self.sharing_service.getSharedArtifacts(
+ self.pillar, self.person, self.user)
+ self.bugtasks = artifacts["bugtasks"]
+ self.branches = artifacts["branches"]
+ self.gitrepositories = artifacts["gitrepositories"]
+ self.snaps = artifacts["snaps"]
+ self.specifications = artifacts["specifications"]
+ self.ocirecipes = artifacts["ocirecipes"]
+
bug_ids = set([bugtask.bug.id for bugtask in self.bugtasks])
self.shared_bugs_count = len(bug_ids)
self.shared_branches_count = len(self.branches)
self.shared_gitrepositories_count = len(self.gitrepositories)
+ self.shared_snaps_count = len(self.snaps)
self.shared_specifications_count = len(self.specifications)
+ self.shared_ocirecipe_count = len(self.ocirecipes)
def _build_specification_template_data(self, specs, request):
spec_data = []
diff --git a/lib/lp/registry/interfaces/accesspolicy.py b/lib/lp/registry/interfaces/accesspolicy.py
index 0e2c8c8..8705584 100644
--- a/lib/lp/registry/interfaces/accesspolicy.py
+++ b/lib/lp/registry/interfaces/accesspolicy.py
@@ -1,4 +1,4 @@
-# Copyright 2011-2015 Canonical Ltd. This software is licensed under the
+# Copyright 2011-2021 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Interfaces for pillar and artifact access policies."""
@@ -38,6 +38,7 @@ class IAccessArtifact(Interface):
gitrepository_id = Attribute("gitrepository_id")
snap_id = Attribute("snap_id")
specification_id = Attribute("specification_id")
+ ocirecipe_id = Attribute("ocirecipe_id")
class IAccessArtifactGrant(Interface):
diff --git a/lib/lp/registry/interfaces/sharingservice.py b/lib/lp/registry/interfaces/sharingservice.py
index f6c39fb..b000090 100644
--- a/lib/lp/registry/interfaces/sharingservice.py
+++ b/lib/lp/registry/interfaces/sharingservice.py
@@ -35,6 +35,7 @@ from lp.blueprints.interfaces.specification import ISpecification
from lp.bugs.interfaces.bug import IBug
from lp.code.interfaces.branch import IBranch
from lp.code.interfaces.gitrepository import IGitRepository
+from lp.oci.interfaces.ocirecipe import IOCIRecipe
from lp.registry.enums import (
BranchSharingPolicy,
BugSharingPolicy,
@@ -161,6 +162,14 @@ class ISharingService(IService):
:return: a collection of Git repositories.
"""
+ def getSharedSnaps(pillar, person, user):
+ """Return the Snap recipes shared between the pillar and person.
+
+ :param user: the user making the request. Only Snap recipes visible
+ to the user will be included in the result.
+ :return: a collection of OCI recipes.
+ """
+
@export_read_operation()
@call_with(user=REQUEST_USER)
@operation_parameters(
@@ -176,9 +185,17 @@ class ISharingService(IService):
:return: a collection of specifications.
"""
+ def getSharedOCIRecipes(pillar, person, user):
+ """Return the OCI recipes shared between the pillar and person.
+
+ :param user: the user making the request. Only OCI recipes visible
+ to the user will be included in the result.
+ :return: a collection of OCI recipes.
+ """
+
def getVisibleArtifacts(person, bugs=None, branches=None,
gitrepositories=None, snaps=None,
- specifications=None):
+ specifications=None, ocirecipes=None):
"""Return the artifacts shared with person.
Given lists of artifacts, return those a person has access to either
@@ -192,6 +209,8 @@ class ISharingService(IService):
:param snaps: the snap recipes to check for which a person has access.
:param specifications: the specifications to check for which a
person has access.
+ :param ocirecipes: the OCI recipes to check for which a person
+ has access.
:return: a collection of artifacts the person can see.
"""
@@ -335,11 +354,14 @@ class ISharingService(IService):
title=_('Snap recipes'), required=False),
specifications=List(
Reference(schema=ISpecification), title=_('Specifications'),
- required=False))
+ required=False),
+ ocirecipes=List(
+ Reference(schema=IOCIRecipe),
+ title=_('Snap recipes'), required=False))
@operation_for_version('devel')
def revokeAccessGrants(pillar, grantee, user, bugs=None, branches=None,
gitrepositories=None, snaps=None,
- specifications=None):
+ specifications=None, ocirecipes=None):
"""Remove a grantee's access to the specified artifacts.
:param pillar: the pillar from which to remove access
@@ -350,6 +372,7 @@ class ISharingService(IService):
:param gitrepositories: the Git repositories for which to revoke access
:param snaps: The snap recipes for which to revoke access
:param specifications: the specifications for which to revoke access
+ :param snaps: The OCI recipes for which to revoke access
"""
@export_write_operation()
@@ -367,12 +390,15 @@ class ISharingService(IService):
title=_('Git repositories'), required=False),
snaps=List(
Reference(schema=ISnap),
- title=_('Snap recipes'), required=False)
+ title=_('Snap recipes'), required=False),
+ ocirecipes=List(
+ Reference(schema=ISnap),
+ title=_('OCI recipes'), required=False)
)
@operation_for_version('devel')
def ensureAccessGrants(grantees, user, bugs=None, branches=None,
gitrepositories=None, snaps=None,
- specifications=None):
+ specifications=None, ocirecipes=None):
"""Ensure a grantee has an access grant to the specified artifacts.
:param grantees: the people or teams for whom to grant access
@@ -382,6 +408,7 @@ class ISharingService(IService):
:param gitrepositories: the Git repositories for which to grant access
:param snaps: the snap recipes for which to grant access
:param specifications: the specifications for which to grant access
+ :param ocirecipes: the OCI recipes for which to grant access
"""
@export_write_operation()
diff --git a/lib/lp/registry/model/accesspolicy.py b/lib/lp/registry/model/accesspolicy.py
index 7bb5e9f..64b69c8 100644
--- a/lib/lp/registry/model/accesspolicy.py
+++ b/lib/lp/registry/model/accesspolicy.py
@@ -102,6 +102,8 @@ class AccessArtifact(StormBase):
snap = Reference(snap_id, 'Snap.id')
specification_id = Int(name='specification')
specification = Reference(specification_id, 'Specification.id')
+ ocirecipe_id = Int(name="ocirecipe")
+ ocirecipe = Reference(ocirecipe_id, 'OCIRecipe.id')
@property
def concrete_artifact(self):
@@ -117,6 +119,7 @@ class AccessArtifact(StormBase):
from lp.code.interfaces.branch import IBranch
from lp.code.interfaces.gitrepository import IGitRepository
from lp.snappy.interfaces.snap import ISnap
+ from lp.oci.interfaces.ocirecipe import IOCIRecipe
if IBug.providedBy(concrete_artifact):
col = cls.bug
elif IBranch.providedBy(concrete_artifact):
@@ -127,6 +130,8 @@ class AccessArtifact(StormBase):
col = cls.snap
elif ISpecification.providedBy(concrete_artifact):
col = cls.specification
+ elif IOCIRecipe.providedBy(concrete_artifact):
+ col = cls.ocirecipe
else:
raise ValueError(
"%r is not a valid artifact" % concrete_artifact)
@@ -149,6 +154,7 @@ class AccessArtifact(StormBase):
from lp.code.interfaces.branch import IBranch
from lp.code.interfaces.gitrepository import IGitRepository
from lp.snappy.interfaces.snap import ISnap
+ from lp.oci.interfaces.ocirecipe import IOCIRecipe
existing = list(cls.find(concrete_artifacts))
if len(existing) == len(concrete_artifacts):
@@ -162,19 +168,21 @@ class AccessArtifact(StormBase):
insert_values = []
for concrete in needed:
if IBug.providedBy(concrete):
- insert_values.append((concrete, None, None, None, None))
+ insert_values.append((concrete, None, None, None, None, None))
elif IBranch.providedBy(concrete):
- insert_values.append((None, concrete, None, None, None))
+ insert_values.append((None, concrete, None, None, None, None))
elif IGitRepository.providedBy(concrete):
- insert_values.append((None, None, concrete, None, None))
+ insert_values.append((None, None, concrete, None, None, None))
elif ISnap.providedBy(concrete):
- insert_values.append((None, None, None, concrete, None))
+ insert_values.append((None, None, None, concrete, None, None))
elif ISpecification.providedBy(concrete):
- insert_values.append((None, None, None, None, concrete))
+ insert_values.append((None, None, None, None, concrete, None))
+ elif IOCIRecipe.providedBy(concrete):
+ insert_values.append((None, None, None, None, None, concrete))
else:
raise ValueError("%r is not a supported artifact" % concrete)
columns = (cls.bug, cls.branch, cls.gitrepository, cls.snap,
- cls.specification)
+ cls.specification, cls.ocirecipe)
new = create(columns, insert_values, get_objects=True)
return list(existing) + new
diff --git a/lib/lp/registry/personmerge.py b/lib/lp/registry/personmerge.py
index 949e8d9..5fa7293 100644
--- a/lib/lp/registry/personmerge.py
+++ b/lib/lp/registry/personmerge.py
@@ -924,6 +924,10 @@ def merge_people(from_person, to_person, reviewer, delete=False):
_mergeOCIRecipe(cur, from_person, to_person)
skip.append(('ocirecipe', 'owner'))
+ # XXX pappacena 2021-03-05: We need to implement the proper handling for
+ # this once we have OCIRecipeSubscription implemented.
+ skip.append(('ocirecipesubscription', 'person'))
+
# Sanity check. If we have a reference that participates in a
# UNIQUE index, it must have already been handled by this point.
# We can tell this by looking at the skip list.
diff --git a/lib/lp/registry/services/sharingservice.py b/lib/lp/registry/services/sharingservice.py
index 01e90da..542066d 100644
--- a/lib/lp/registry/services/sharingservice.py
+++ b/lib/lp/registry/services/sharingservice.py
@@ -39,6 +39,8 @@ from lp.bugs.interfaces.bugtask import IBugTaskSet
from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
from lp.code.interfaces.branchcollection import IAllBranches
from lp.code.interfaces.gitcollection import IAllGitRepositories
+from lp.oci.interfaces.ocirecipe import IOCIRecipe
+from lp.oci.model.ocirecipe import OCIRecipe
from lp.registry.enums import (
BranchSharingPolicy,
BugSharingPolicy,
@@ -201,13 +203,15 @@ class SharingService:
@available_with_permission('launchpad.Driver', 'pillar')
def getSharedArtifacts(self, pillar, person, user, include_bugs=True,
include_branches=True, include_gitrepositories=True,
- include_snaps=True, include_specifications=True):
+ include_snaps=True, include_specifications=True,
+ include_ocirecipes=True):
"""See `ISharingService`."""
bug_ids = set()
branch_ids = set()
gitrepository_ids = set()
snap_ids = set()
specification_ids = set()
+ ocirecipe_ids = set()
for artifact in self.getArtifactGrantsForPersonOnPillar(
pillar, person):
if artifact.bug_id and include_bugs:
@@ -220,6 +224,8 @@ class SharingService:
snap_ids.add(artifact.snap_id)
elif artifact.specification_id and include_specifications:
specification_ids.add(artifact.specification_id)
+ elif artifact.ocirecipe_id and include_ocirecipes:
+ ocirecipe_ids.add(artifact.ocirecipe_id)
# Load the bugs.
bugtasks = []
@@ -248,48 +254,68 @@ class SharingService:
specifications = []
if specification_ids:
specifications = load(Specification, specification_ids)
+ ocirecipes = []
+ if ocirecipe_ids:
+ ocirecipes = load(OCIRecipe, ocirecipe_ids)
- return bugtasks, branches, gitrepositories, snaps, specifications
+ return {"bugtasks": bugtasks, "branches": branches,
+ "gitrepositories": gitrepositories, "snaps": snaps,
+ "specifications": specifications, "ocirecipes": ocirecipes}
@available_with_permission('launchpad.Driver', 'pillar')
def getSharedBugs(self, pillar, person, user):
"""See `ISharingService`."""
- bugtasks, _, _, _, _ = self.getSharedArtifacts(
+ artifacts = self.getSharedArtifacts(
pillar, person, user, include_branches=False,
- include_gitrepositories=False, include_specifications=False)
- return bugtasks
+ include_gitrepositories=False,
+ include_specifications=False, include_snaps=False,
+ include_ocirecipes=False)
+ return artifacts["bugtasks"]
@available_with_permission('launchpad.Driver', 'pillar')
def getSharedBranches(self, pillar, person, user):
"""See `ISharingService`."""
- _, branches, _, _, _ = self.getSharedArtifacts(
+ artifacts = self.getSharedArtifacts(
pillar, person, user, include_bugs=False,
- include_gitrepositories=False, include_specifications=False)
- return branches
+ include_gitrepositories=False, include_specifications=False,
+ include_snaps=False, include_ocirecipes=False)
+ return artifacts["branches"]
@available_with_permission('launchpad.Driver', 'pillar')
def getSharedGitRepositories(self, pillar, person, user):
"""See `ISharingService`."""
- _, _, gitrepositories, _, _ = self.getSharedArtifacts(
+ artifacts = self.getSharedArtifacts(
pillar, person, user, include_bugs=False, include_branches=False,
- include_specifications=False)
- return gitrepositories
+ include_specifications=False, include_snaps=False,
+ include_ocirecipes=False)
+ return artifacts["gitrepositories"]
@available_with_permission('launchpad.Driver', 'pillar')
def getSharedSnaps(self, pillar, person, user):
"""See `ISharingService`."""
- _, _, _, snaps, _ = self.getSharedArtifacts(
+ artifacts = self.getSharedArtifacts(
pillar, person, user, include_bugs=False, include_branches=False,
- include_gitrepositories=False)
- return snaps
+ include_gitrepositories=False, include_specifications=False,
+ include_ocirecipes=False)
+ return artifacts["snaps"]
@available_with_permission('launchpad.Driver', 'pillar')
def getSharedSpecifications(self, pillar, person, user):
"""See `ISharingService`."""
- _, _, _, _, specifications = self.getSharedArtifacts(
+ artifacts = self.getSharedArtifacts(
pillar, person, user, include_bugs=False, include_branches=False,
- include_gitrepositories=False)
- return specifications
+ include_gitrepositories=False, include_snaps=False,
+ include_ocirecipes=False)
+ return artifacts["specifications"]
+
+ @available_with_permission('launchpad.Driver', 'pillar')
+ def getSharedOCIRecipes(self, pillar, person, user):
+ """See `ISharingService`."""
+ artifacts = self.getSharedArtifacts(
+ pillar, person, user, include_bugs=False, include_branches=False,
+ include_gitrepositories=False, include_snaps=False,
+ include_specifications=False)
+ return artifacts["ocirecipes"]
def _getVisiblePrivateSpecificationIDs(self, person, specifications):
store = Store.of(specifications[0])
@@ -327,7 +353,8 @@ class SharingService:
def getVisibleArtifacts(self, person, bugs=None, branches=None,
gitrepositories=None, snaps=None,
- specifications=None, ignore_permissions=False):
+ specifications=None, ignore_permissions=False,
+ ocirecipes=None):
"""See `ISharingService`."""
bug_ids = []
branch_ids = []
@@ -772,11 +799,11 @@ class SharingService:
@available_with_permission('launchpad.Edit', 'pillar')
def revokeAccessGrants(self, pillar, grantee, user, bugs=None,
branches=None, gitrepositories=None, snaps=None,
- specifications=None):
+ specifications=None, ocirecipes=None):
"""See `ISharingService`."""
if (not bugs and not branches and not gitrepositories and not snaps and
- not specifications):
+ not specifications and not ocirecipes):
raise ValueError(
"Either bugs, branches, gitrepositories, or specifications "
"must be specified")
@@ -792,6 +819,8 @@ class SharingService:
artifacts.extend(snaps)
if specifications:
artifacts.extend(specifications)
+ if ocirecipes:
+ artifacts.extend(ocirecipes)
# Find the access artifacts associated with the bugs, branches, Git
# repositories, and specifications.
accessartifact_source = getUtility(IAccessArtifactSource)
@@ -806,6 +835,12 @@ class SharingService:
if not artifacts:
return
+ # XXX: Pappacena 2021-03-09: OCI recipes should not trigger this job,
+ # since we do not have a "OCIRecipeSubscription" yet.
+ artifacts = [i for i in artifacts if not IOCIRecipe.providedBy(i)]
+ if not artifacts:
+ return
+
# Create a job to remove subscriptions for artifacts the grantee can no
# longer see.
return getUtility(IRemoveArtifactSubscriptionsJobSource).create(
@@ -813,7 +848,8 @@ class SharingService:
def ensureAccessGrants(self, grantees, user, bugs=None, branches=None,
gitrepositories=None, snaps=None,
- specifications=None, ignore_permissions=False):
+ specifications=None, ocirecipes=None,
+ ignore_permissions=False):
"""See `ISharingService`."""
artifacts = []
@@ -827,6 +863,8 @@ class SharingService:
artifacts.extend(snaps)
if specifications:
artifacts.extend(specifications)
+ if ocirecipes:
+ artifacts.extend(ocirecipes)
if not ignore_permissions:
# The user needs to have launchpad.Edit permission on all supplied
# bugs and branches or else we raise an Unauthorized exception.
diff --git a/lib/lp/registry/services/tests/test_sharingservice.py b/lib/lp/registry/services/tests/test_sharingservice.py
index 9d0e07c..ccb4765 100644
--- a/lib/lp/registry/services/tests/test_sharingservice.py
+++ b/lib/lp/registry/services/tests/test_sharingservice.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2015 Canonical Ltd. This software is licensed under the
+# Copyright 2012-2021 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -25,6 +25,7 @@ from lp.code.enums import (
)
from lp.code.interfaces.branch import IBranch
from lp.code.interfaces.gitrepository import IGitRepository
+from lp.oci.tests.helpers import OCIConfigHelperMixin
from lp.registry.enums import (
BranchSharingPolicy,
BugSharingPolicy,
@@ -41,11 +42,11 @@ from lp.registry.interfaces.accesspolicy import (
from lp.registry.interfaces.person import TeamMembershipPolicy
from lp.registry.interfaces.role import IPersonRoles
from lp.registry.services.sharingservice import SharingService
-from lp.services.features.testing import FeatureFixture
from lp.services.job.tests import block_on_job
from lp.services.webapp.interaction import ANONYMOUS
from lp.services.webapp.interfaces import ILaunchpadRoot
from lp.services.webapp.publisher import canonical_url
+from lp.snappy.interfaces.snap import SNAP_TESTING_FLAGS
from lp.testing import (
admin_logged_in,
login,
@@ -64,7 +65,7 @@ from lp.testing.matchers import HasQueryCount
from lp.testing.pages import LaunchpadWebServiceCaller
-class TestSharingService(TestCaseWithFactory):
+class TestSharingService(TestCaseWithFactory, OCIConfigHelperMixin):
"""Tests for the SharingService."""
layer = CeleryJobLayer
@@ -72,9 +73,12 @@ class TestSharingService(TestCaseWithFactory):
def setUp(self):
super(TestSharingService, self).setUp()
self.service = getUtility(IService, 'sharing')
- self.useFixture(FeatureFixture({
+ # Set test flags and configurations for Snaps and OCI.
+ flags = SNAP_TESTING_FLAGS.copy()
+ flags.update({
'jobs.celery.enabled_classes': 'RemoveArtifactSubscriptionsJob',
- }))
+ })
+ self.setConfig(feature_flags=flags)
def _makeGranteeData(self, grantee, policy_permissions,
shared_artifact_types):
@@ -1414,12 +1418,27 @@ class TestSharingService(TestCaseWithFactory):
target=product, owner=product.owner,
information_type=InformationType.USERDATA)
gitrepositories.append(gitrepository)
+ snaps = []
+ for x in range(0, 10):
+ snap = self.factory.makeSnap(
+ project=product, owner=product.owner, registrant=product.owner,
+ information_type=InformationType.USERDATA)
+ snaps.append(snap)
specs = []
for x in range(0, 10):
spec = self.factory.makeSpecification(
product=product, owner=product.owner,
information_type=InformationType.PROPRIETARY)
specs.append(spec)
+ ocirecipes = []
+ for x in range(0, 10):
+ ociproject = self.factory.makeOCIProject(
+ pillar=product, registrant=product.owner)
+ ocirecipe = self.factory.makeOCIRecipe(
+ oci_project=ociproject, owner=product.owner,
+ registrant=product.owner,
+ information_type=InformationType.USERDATA)
+ ocirecipes.append(ocirecipe)
# Grant access to grantee as well as the person who will be doing the
# query. The person who will be doing the query is not granted access
@@ -1442,8 +1461,12 @@ class TestSharingService(TestCaseWithFactory):
for i, gitrepository in enumerate(gitrepositories):
grant_access(gitrepository, i == 9)
getUtility(IService, 'sharing').ensureAccessGrants(
+ [grantee], product.owner, snaps=snaps[:9])
+ getUtility(IService, 'sharing').ensureAccessGrants(
[grantee], product.owner, specifications=specs[:9])
- return bug_tasks, branches, gitrepositories, specs
+ getUtility(IService, 'sharing').ensureAccessGrants(
+ [grantee], product.owner, ocirecipes=ocirecipes[:9])
+ return bug_tasks, branches, gitrepositories, snaps, specs, ocirecipes
def test_getSharedArtifacts(self):
# Test the getSharedArtifacts method.
@@ -1454,17 +1477,24 @@ class TestSharingService(TestCaseWithFactory):
login_person(owner)
grantee = self.factory.makePerson()
user = self.factory.makePerson()
- bug_tasks, branches, gitrepositories, specs = (
+ bug_tasks, branches, gitrepositories, snaps, specs, ocirecipes = (
self.create_shared_artifacts(product, grantee, user))
# Check the results.
- (shared_bugtasks, shared_branches, shared_gitrepositories,
- shared_snaps, shared_specs) = (
- self.service.getSharedArtifacts(product, grantee, user))
+ artifacts = self.service.getSharedArtifacts(product, grantee, user)
+ shared_bugtasks = artifacts["bugtasks"]
+ shared_branches = artifacts["branches"]
+ shared_gitrepositories = artifacts["gitrepositories"]
+ shared_snaps = artifacts["snaps"]
+ shared_specs = artifacts["specifications"]
+ shared_ocirecipes = artifacts["ocirecipes"]
+
self.assertContentEqual(bug_tasks[:9], shared_bugtasks)
self.assertContentEqual(branches[:9], shared_branches)
self.assertContentEqual(gitrepositories[:9], shared_gitrepositories)
+ self.assertContentEqual(snaps[:9], shared_snaps)
self.assertContentEqual(specs[:9], shared_specs)
+ self.assertContentEqual(ocirecipes[:9], shared_ocirecipes)
def _assert_getSharedProjects(self, product, who=None):
# Test that 'who' can query the shared products for a grantee.
@@ -1603,7 +1633,7 @@ class TestSharingService(TestCaseWithFactory):
login_person(owner)
grantee = self.factory.makePerson()
user = self.factory.makePerson()
- bug_tasks, _, _, _ = self.create_shared_artifacts(
+ bug_tasks, _, _, _, _, _ = self.create_shared_artifacts(
product, grantee, user)
# Check the results.
@@ -1619,7 +1649,7 @@ class TestSharingService(TestCaseWithFactory):
login_person(owner)
grantee = self.factory.makePerson()
user = self.factory.makePerson()
- _, branches, _, _ = self.create_shared_artifacts(
+ _, branches, _, _, _, _ = self.create_shared_artifacts(
product, grantee, user)
# Check the results.
@@ -1636,7 +1666,7 @@ class TestSharingService(TestCaseWithFactory):
login_person(owner)
grantee = self.factory.makePerson()
user = self.factory.makePerson()
- _, _, gitrepositories, _ = self.create_shared_artifacts(
+ _, _, gitrepositories, _, _, _ = self.create_shared_artifacts(
product, grantee, user)
# Check the results.
@@ -1644,6 +1674,23 @@ class TestSharingService(TestCaseWithFactory):
product, grantee, user)
self.assertContentEqual(gitrepositories[:9], shared_gitrepositories)
+ def test_getSharedSnaps(self):
+ # Test the getSharedSnaps method.
+ owner = self.factory.makePerson()
+ product = self.factory.makeProduct(
+ owner=owner, specification_sharing_policy=(
+ SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
+ login_person(owner)
+ grantee = self.factory.makePerson()
+ user = self.factory.makePerson()
+ _, _, _, snaps, _, _ = self.create_shared_artifacts(
+ product, grantee, user)
+
+ # Check the results.
+ shared_snaps = self.service.getSharedSnaps(
+ product, grantee, user)
+ self.assertContentEqual(snaps[:9], shared_snaps)
+
def test_getSharedSpecifications(self):
# Test the getSharedSpecifications method.
owner = self.factory.makePerson()
@@ -1653,7 +1700,7 @@ class TestSharingService(TestCaseWithFactory):
login_person(owner)
grantee = self.factory.makePerson()
user = self.factory.makePerson()
- _, _, _, specifications = self.create_shared_artifacts(
+ _, _, _, _, specifications, _ = self.create_shared_artifacts(
product, grantee, user)
# Check the results.
@@ -1661,6 +1708,23 @@ class TestSharingService(TestCaseWithFactory):
product, grantee, user)
self.assertContentEqual(specifications[:9], shared_specifications)
+ def test_getSharedOCIRecipes(self):
+ # Test the getSharedSnaps method.
+ owner = self.factory.makePerson()
+ product = self.factory.makeProduct(
+ owner=owner, specification_sharing_policy=(
+ SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
+ login_person(owner)
+ grantee = self.factory.makePerson()
+ user = self.factory.makePerson()
+ _, _, _, _, _, ocirecipes = self.create_shared_artifacts(
+ product, grantee, user)
+
+ # Check the results.
+ shared_ocirecipes = self.service.getSharedOCIRecipes(
+ product, grantee, user)
+ self.assertContentEqual(ocirecipes[:9], shared_ocirecipes)
+
def test_getPeopleWithAccessBugs(self):
# Test the getPeopleWithoutAccess method with bugs.
owner = self.factory.makePerson()
diff --git a/lib/lp/snappy/model/snap.py b/lib/lp/snappy/model/snap.py
index a96eace..b0d9f92 100644
--- a/lib/lp/snappy/model/snap.py
+++ b/lib/lp/snappy/model/snap.py
@@ -1368,7 +1368,7 @@ class SnapSet:
def findByIds(self, snap_ids):
"""See `ISnapSet`."""
- return IStore(ISnap).find(Snap, Snap.id.is_in(snap_ids))
+ return IStore(Snap).find(Snap, Snap.id.is_in(snap_ids))
def findByOwner(self, owner):
"""See `ISnapSet`."""
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 6948419..3e580fe 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -4961,7 +4961,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
oci_project=None, git_ref=None, description=None,
official=False, require_virtualized=True,
build_file=None, date_created=DEFAULT,
- allow_internet=True, build_args=None, build_path=None):
+ allow_internet=True, build_args=None, build_path=None,
+ information_type=InformationType.PUBLIC):
"""Make a new OCIRecipe."""
if name is None:
name = self.getUniqueString(u"oci-recipe-name")
@@ -4994,7 +4995,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
require_virtualized=require_virtualized,
date_created=date_created,
allow_internet=allow_internet,
- build_args=build_args)
+ build_args=build_args,
+ information_type=information_type)
def makeOCIRecipeArch(self, recipe=None, processor=None):
"""Make a new OCIRecipeArch."""
Follow ups