← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~pappacena/launchpad:oci-project-admin into launchpad:master

 

Thiago F. Pappacena has proposed merging ~pappacena/launchpad:oci-project-admin into launchpad:master.

Commit message:
Adding the attribute and method to check if a given person has permission to manage OCI projects for a given Distribution.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/381321

This MP only adds the new Distribution.oci_project_admin attribute and Distribution.canAdministerOCIProjects, so we can unlock the development of UI and API for OCI project creation.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:oci-project-admin into launchpad:master.
diff --git a/lib/lp/registry/interfaces/distribution.py b/lib/lp/registry/interfaces/distribution.py
index a7106f6..85e503d 100644
--- a/lib/lp/registry/interfaces/distribution.py
+++ b/lib/lp/registry/interfaces/distribution.py
@@ -249,6 +249,11 @@ class IDistributionPublic(
         description=_("The person or team that has the rights to review and "
                       "mark this distribution's mirrors as official."),
         required=True, vocabulary='ValidPersonOrTeam'))
+    oci_project_admin = exported(PublicPersonChoice(
+        title=_("OCI Project Administrator"),
+        description=_("The person or team that has the rights to manage OCI "
+                      "Projects."),
+        required=False, vocabulary='ValidPersonOrTeam'))
     archive_mirrors = exported(doNotSnapshot(
         CollectionField(
             description=_("All enabled and official ARCHIVE mirrors "
@@ -655,6 +660,10 @@ class IDistributionPublic(
     def userCanEdit(user):
         """Can the user edit this distribution?"""
 
+    def canAdministerOCIProjects(person):
+        """Checks if the given person can administer OCI Projects of this
+        distro."""
+
 
 class IDistribution(
     IDistributionEditRestricted, IDistributionPublic, IHasBugSupervisor,
@@ -710,7 +719,8 @@ class IDistributionSet(Interface):
         """Return the IDistribution with the given name or None."""
 
     def new(name, display_name, title, description, summary, domainname,
-            members, owner, registrant, mugshot=None, logo=None, icon=None):
+            members, owner, registrant, mugshot=None, logo=None, icon=None,
+            oci_project_admin=None):
         """Create a new distribution."""
 
     def getCurrentSourceReleases(distro_to_source_packagenames):
diff --git a/lib/lp/registry/model/distribution.py b/lib/lp/registry/model/distribution.py
index f78908b..ab0ad26 100644
--- a/lib/lp/registry/model/distribution.py
+++ b/lib/lp/registry/model/distribution.py
@@ -236,6 +236,9 @@ class Distribution(SQLBase, BugTargetBase, MakesAnnouncements,
     mirror_admin = ForeignKey(
         dbName='mirror_admin', foreignKey='Person',
         storm_validator=validate_public_person, notNull=True)
+    oci_project_admin = ForeignKey(
+        dbName='oci_project_admin', foreignKey='Person',
+        storm_validator=validate_public_person, notNull=False)
     translationgroup = ForeignKey(
         dbName='translationgroup', foreignKey='TranslationGroup',
         notNull=False, default=None)
@@ -1360,6 +1363,16 @@ class Distribution(SQLBase, BugTargetBase, MakesAnnouncements,
         admins = getUtility(ILaunchpadCelebrities).admin
         return user.inTeam(self.owner) or user.inTeam(admins)
 
+    def canAdministerOCIProjects(self, person):
+        """See `IDistribution`."""
+        if person is None or self.oci_project_admin is None:
+            return False
+        if person == self.oci_project_admin:
+            return True
+        if self.oci_project_admin.is_team:
+            return person in self.oci_project_admin.activemembers
+        return False
+
     def newSeries(self, name, display_name, title, summary,
                   description, version, previous_series, registrant):
         """See `IDistribution`."""
@@ -1490,7 +1503,7 @@ class DistributionSet:
 
     def new(self, name, display_name, title, description, summary, domainname,
             members, owner, registrant, mugshot=None, logo=None, icon=None,
-            vcs=None):
+            vcs=None, oci_project_admin=None):
         """See `IDistributionSet`."""
         distro = Distribution(
             name=name,
@@ -1506,7 +1519,8 @@ class DistributionSet:
             mugshot=mugshot,
             logo=logo,
             icon=icon,
-            vcs=vcs)
+            vcs=vcs,
+            oci_project_admin=oci_project_admin)
         getUtility(IArchiveSet).new(distribution=distro,
             owner=owner, purpose=ArchivePurpose.PRIMARY)
         policies = itertools.product(
diff --git a/lib/lp/registry/tests/test_distribution.py b/lib/lp/registry/tests/test_distribution.py
index c7bf86f..b13d89b 100644
--- a/lib/lp/registry/tests/test_distribution.py
+++ b/lib/lp/registry/tests/test_distribution.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for Distribution."""
@@ -689,3 +689,43 @@ class TestWebService(WebServiceTestCase):
         self.assertEqual(
             [],
             ws_distro.findReferencedOOPS(start_date=now - day, end_date=now))
+
+
+class DistributionOCIProjectAdminPermission(TestCaseWithFactory):
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(DistributionOCIProjectAdminPermission, self).setUp()
+
+    def test_check_oci_project_admin_person(self):
+        person1 = self.factory.makePerson()
+        person2 = self.factory.makePerson()
+        distro = self.factory.makeDistribution(oci_project_admin=person1)
+
+        self.assertTrue(distro.canAdministerOCIProjects(person1))
+        self.assertFalse(distro.canAdministerOCIProjects(person2))
+        self.assertFalse(distro.canAdministerOCIProjects(None))
+
+    def test_check_oci_project_admin_team(self):
+        person1 = self.factory.makePerson()
+        person2 = self.factory.makePerson()
+        person3 = self.factory.makePerson()
+        team = self.factory.makeTeam(owner=person1)
+        distro = self.factory.makeDistribution(oci_project_admin=team)
+
+        admin = self.factory.makeAdministrator()
+        with person_logged_in(admin):
+            person2.join(team)
+
+        self.assertTrue(distro.canAdministerOCIProjects(team))
+        self.assertTrue(distro.canAdministerOCIProjects(person1))
+        self.assertTrue(distro.canAdministerOCIProjects(person2))
+        self.assertFalse(distro.canAdministerOCIProjects(person3))
+        self.assertFalse(distro.canAdministerOCIProjects(None))
+
+    def test_check_oci_project_admin_without_any_admin(self):
+        person1 = self.factory.makePerson()
+        distro = self.factory.makeDistribution(oci_project_admin=None)
+
+        self.assertFalse(distro.canAdministerOCIProjects(person1))
+        self.assertFalse(distro.canAdministerOCIProjects(None))
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index c65c941..b7d8faf 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -2660,7 +2660,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
                          aliases=None, bug_supervisor=None, driver=None,
                          publish_root_dir=None, publish_base_url=None,
                          publish_copy_base_url=None, no_pubconf=False,
-                         icon=None, summary=None, vcs=None):
+                         icon=None, summary=None, vcs=None,
+                         oci_project_admin=None):
         """Make a new distribution."""
         if name is None:
             name = self.getUniqueString(prefix="distribution")
@@ -2680,7 +2681,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             members = self.makeTeam(owner)
         distro = getUtility(IDistributionSet).new(
             name, displayname, title, description, summary, domainname,
-            members, owner, registrant, icon=icon, vcs=vcs)
+            members, owner, registrant, icon=icon, vcs=vcs,
+            oci_project_admin=oci_project_admin)
         naked_distro = removeSecurityProxy(distro)
         if aliases is not None:
             naked_distro.setAliases(aliases)