← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~enriqueesanchz/launchpad:add-vulnerabilities-export-data into launchpad:master

 

Enrique Sánchez has proposed merging ~enriqueesanchz/launchpad:add-vulnerabilities-export-data into launchpad:master.

Commit message:
Add vulnerabilities exportData endpoint
    
Implement the API endpoint to create a ExportVulnerabilityJob using
vulnerability set.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~enriqueesanchz/launchpad/+git/launchpad/+merge/492761
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~enriqueesanchz/launchpad:add-vulnerabilities-export-data into launchpad:master.
diff --git a/lib/lp/bugs/model/tests/test_vulnerability.py b/lib/lp/bugs/model/tests/test_vulnerability.py
index 2b69862..bae25c1 100644
--- a/lib/lp/bugs/model/tests/test_vulnerability.py
+++ b/lib/lp/bugs/model/tests/test_vulnerability.py
@@ -33,6 +33,7 @@ from lp.bugs.interfaces.vulnerabilityjob import (
     IImportVulnerabilityJobSource,
     VulnerabilityJobInProgress,
 )
+from lp.bugs.model.exportvulnerabilityjob import ExportVulnerabilityJob
 from lp.bugs.model.importvulnerabilityjob import ImportVulnerabilityJob
 from lp.bugs.model.vulnerability import (
     VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG,
@@ -982,6 +983,93 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
             )
 
 
+class TestVulnerabilitySetExportData(TestCaseWithFactory):
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super().setUp()
+        self.requester = self.factory.makePerson()
+        self.handler = VulnerabilityHandlerEnum.SOSS
+
+    def test_exportData(self):
+        """Test that we can create a ExportVulnerabilityJob using exportData
+        method.
+        """
+        self.useContext(feature_flags())
+        set_feature_flag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG, "true")
+
+        self.factory.makeDistribution(name="soss", owner=self.requester)
+
+        with person_logged_in(self.requester):
+            getUtility(IVulnerabilitySet).exportData(
+                self.requester,
+                self.handler,
+                None,
+            )
+
+        job = getUtility(IExportVulnerabilityJobSource).get(self.handler)
+        naked_job = removeSecurityProxy(job)
+        self.assertIsInstance(naked_job, ExportVulnerabilityJob)
+        self.assertEqual(naked_job.handler, self.handler)
+
+    def test_exportData_feature_flag_disabled(self):
+        """Test that if the feature flag is disabled it raises the
+        NotImplementedError exception."""
+
+        # All parameters None, feature flag is the first check
+        self.assertRaisesWithContent(
+            NotImplementedError,
+            "Vulnerability export API is currently disabled",
+            getUtility(IVulnerabilitySet).exportData,
+            self.requester,
+            None,
+            None,
+        )
+
+    def test_exportData_handler_unauthorized(self):
+        """Test that we cannot create a ExportVulnerabilityJob if the user is
+        not authorized to use the handler.
+        """
+        self.useContext(feature_flags())
+        set_feature_flag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG, "true")
+
+        self.factory.makeDistribution(name="soss")
+
+        self.assertRaisesWithContent(
+            UnauthorizedVulnerabilityHandler,
+            f"You don't have enough rights to use {self.handler}",
+            getUtility(IVulnerabilitySet).exportData,
+            self.requester,
+            self.handler,
+            None,
+        )
+
+    def test_exportData_duplicated(self):
+        """Test that we cannot create a duplicated ExportVulnerabilityJob
+        if there is already a peding one for the same handler.
+        """
+        self.useContext(feature_flags())
+        set_feature_flag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG, "true")
+
+        self.factory.makeDistribution(name="soss", owner=self.requester)
+
+        vulnerability_set = getUtility(IVulnerabilitySet)
+        with person_logged_in(self.requester):
+            vulnerability_set.exportData(
+                self.requester,
+                self.handler,
+                None,
+            )
+
+            self.assertRaises(
+                VulnerabilityJobInProgress,
+                vulnerability_set.exportData,
+                self.requester,
+                self.handler,
+                None,
+            )
+
+
 class TestVulnerabilitySetWebService(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
@@ -1137,6 +1225,67 @@ class TestVulnerabilitySetWebService(TestCaseWithFactory):
             set(response.body.decode().split("\n")),
         )
 
+    def test_exportData_webservice(self):
+        """Test that we can create a ExportVulnerabilityJob using the
+        webservice.
+        """
+        self.useContext(feature_flags())
+        set_feature_flag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG, "true")
+
+        webservice = webservice_for_person(
+            self.requester,
+            default_api_version="devel",
+        )
+        response = webservice.named_post(
+            self.vulnerability_set_url,
+            "exportData",
+            handler=self.handler.title,
+        )
+        self.assertEqual(200, response.status)
+
+    def test_exportData_webservice_feature_flag_disabled(self):
+        """Test that we cannot create a ExportVulnerabilityJob using the
+        webservice when the vulnerability import API is disabled.
+        """
+        webservice = webservice_for_person(
+            self.requester,
+            default_api_version="devel",
+        )
+        response = webservice.named_post(
+            self.vulnerability_set_url,
+            "exportData",
+            handler=self.handler.title,
+        )
+        self.assertEqual(500, response.status)
+        self.assertEqual(
+            "Vulnerability export API is currently disabled",
+            response.body.decode().split("\n")[0],
+        )
+
+    def test_exportData_webservice_handler_unauthorized(self):
+        """Test that we can create a ExportVulnerabilityJob using the
+        webservice.
+        """
+        self.useContext(feature_flags())
+        set_feature_flag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG, "true")
+
+        user_unauthorized = self.factory.makePerson()
+
+        webservice = webservice_for_person(
+            user_unauthorized,
+            default_api_version="devel",
+        )
+        response = webservice.named_post(
+            self.vulnerability_set_url,
+            "exportData",
+            handler=self.handler.title,
+        )
+        self.assertEqual(500, response.status)
+        self.assertEqual(
+            f"You don't have enough rights to use {self.handler}",
+            response.body.decode().split("\n")[0],
+        )
+
     def test_getImports_webservice_required_arguments_missing(self):
         """Test that getImports webservice requires missing arguments."""
         webservice = webservice_for_person(
diff --git a/lib/lp/bugs/model/vulnerability.py b/lib/lp/bugs/model/vulnerability.py
index 068bee3..11f5dde 100644
--- a/lib/lp/bugs/model/vulnerability.py
+++ b/lib/lp/bugs/model/vulnerability.py
@@ -42,7 +42,10 @@ from lp.bugs.interfaces.vulnerability import (
     IVulnerabilitySet,
     VulnerabilityChange,
 )
-from lp.bugs.interfaces.vulnerabilityjob import IImportVulnerabilityJobSource
+from lp.bugs.interfaces.vulnerabilityjob import (
+    IExportVulnerabilityJobSource,
+    IImportVulnerabilityJobSource,
+)
 from lp.bugs.model.bug import Bug
 from lp.bugs.model.buglinktarget import BugLinkTargetMixin
 from lp.bugs.model.vulnerabilitysubscription import VulnerabilitySubscription
@@ -482,9 +485,30 @@ class VulnerabilitySet:
     ):
         """See `IVulnerabilitySet`."""
 
-        raise NotImplementedError(
-            "Vulnerability export API is currently disabled"
-        )
+        if not getFeatureFlag(VULNERABILITY_IMPORT_ENABLED_FEATURE_FLAG):
+            raise NotImplementedError(
+                "Vulnerability export API is currently disabled"
+            )
+
+        # Check requester's permissions to handler
+        from lp.bugs.scripts.soss.sossexport import SOSSExporter
+
+        if handler == VulnerabilityHandlerEnum.SOSS:
+            distribution = getUtility(IDistributionSet).getByName(
+                HANDLER_DISTRIBUTION_MAP[handler]
+            )
+            exporter = SOSSExporter()
+        else:
+            raise NotFoundError(f"{handler} not found")
+
+        if not exporter.checkUserPermissions(requester, distribution):
+            raise UnauthorizedVulnerabilityHandler(
+                f"You don't have enough rights to use {handler}"
+            )
+
+        # Trigger the export job after validations pass
+        job_source = getUtility(IExportVulnerabilityJobSource)
+        job_source.create(handler)
 
         return None