From e93f2abd1326a128cd04e2c9ef3ed495889526ef Mon Sep 17 00:00:00 2001 From: ozgen Date: Tue, 24 Mar 2026 10:11:12 +0100 Subject: [PATCH] add: add get and modify integration config commands Implement `get_integration_configs` and `modify_integration_config` commands and add unit tests for both GMP requests. --- gvm/protocols/gmp/_gmpnext.py | 66 +++++++ gvm/protocols/gmp/requests/next/__init__.py | 4 + .../gmp/requests/next/_integration_configs.py | 112 ++++++++++++ .../entities/integration_configs/__init__.py | 3 + .../test_get_integration_config.py | 44 +++++ .../test_get_integration_configs.py | 25 +++ .../test_modify_integration_config.py | 170 ++++++++++++++++++ .../entities/test_integration_configs.py | 33 ++++ 8 files changed, 457 insertions(+) create mode 100644 gvm/protocols/gmp/requests/next/_integration_configs.py create mode 100644 tests/protocols/gmpnext/entities/integration_configs/__init__.py create mode 100644 tests/protocols/gmpnext/entities/integration_configs/test_get_integration_config.py create mode 100644 tests/protocols/gmpnext/entities/integration_configs/test_get_integration_configs.py create mode 100644 tests/protocols/gmpnext/entities/integration_configs/test_modify_integration_config.py create mode 100644 tests/protocols/gmpnext/entities/test_integration_configs.py diff --git a/gvm/protocols/gmp/_gmpnext.py b/gvm/protocols/gmp/_gmpnext.py index 8c930118..6656f2be 100644 --- a/gvm/protocols/gmp/_gmpnext.py +++ b/gvm/protocols/gmp/_gmpnext.py @@ -16,6 +16,7 @@ Credentials, CredentialStoreCredentialType, CredentialStores, + IntegrationConfigs, OCIImageTargets, Tasks, ) @@ -927,3 +928,68 @@ def stop_task(self, task_id: EntityID) -> T: return self._send_request_and_transform_response( Tasks.stop_task(task_id=task_id) ) + + def get_integration_config( + self, integration_config_id: EntityID, *, details: Optional[bool] = None + ) -> T: + """Request a single Integration Configuration. + + Args: + integration_config_id: UUID of the integration config to request. + details: Whether to include detail information. + """ + return self._send_request_and_transform_response( + IntegrationConfigs.get_integration_config( + integration_config_id=integration_config_id, details=details + ) + ) + + def get_integration_configs( + self, + *, + filter_string: Optional[str] = None, + filter_id: Optional[EntityID] = None, + ) -> T: + """Request a list of Integration Configurations. + + Args: + filter_string: Filter term to use for the query. + filter_id: UUID of an existing filter to use for the query. + """ + return self._send_request_and_transform_response( + IntegrationConfigs.get_integration_configs( + filter_string=filter_string, + filter_id=filter_id, + ) + ) + + def modify_integration_config( + self, + integration_config_id: EntityID, + *, + service_url: Optional[str] = None, + service_cacert: Optional[str] = None, + oidc_provider_url: Optional[str] = None, + oidc_provider_client_id: Optional[str] = None, + oidc_provider_client_secret: Optional[str] = None, + ) -> T: + """Modify an existing Integration Configuration. + + Args: + integration_config_id: UUID of configuration to modify. + service_url: Integration Service URL. + service_cacert: Integration Service Certificate. + oidc_provider_url: OIDC Provider URL. + oidc_provider_client_id: OIDC Provider Client ID. + oidc_provider_client_secret: OIDC Provider Client Secret. + """ + return self._send_request_and_transform_response( + IntegrationConfigs.modify_integration_config( + integration_config_id=integration_config_id, + service_url=service_url, + service_cacert=service_cacert, + oidc_provider_url=oidc_provider_url, + oidc_provider_client_id=oidc_provider_client_id, + oidc_provider_client_secret=oidc_provider_client_secret, + ) + ) diff --git a/gvm/protocols/gmp/requests/next/__init__.py b/gvm/protocols/gmp/requests/next/__init__.py index b6541fcf..f18fcd1e 100644 --- a/gvm/protocols/gmp/requests/next/__init__.py +++ b/gvm/protocols/gmp/requests/next/__init__.py @@ -10,6 +10,9 @@ Credentials, CredentialStoreCredentialType, ) +from gvm.protocols.gmp.requests.next._integration_configs import ( + IntegrationConfigs, +) from gvm.protocols.gmp.requests.next._oci_image_targets import OCIImageTargets from gvm.protocols.gmp.requests.next._tasks import Tasks @@ -118,6 +121,7 @@ "Hosts", "HostsOrdering", "InfoType", + "IntegrationConfigs", "Notes", "Nvts", "OCIImageTargets", diff --git a/gvm/protocols/gmp/requests/next/_integration_configs.py b/gvm/protocols/gmp/requests/next/_integration_configs.py new file mode 100644 index 00000000..6cfea204 --- /dev/null +++ b/gvm/protocols/gmp/requests/next/_integration_configs.py @@ -0,0 +1,112 @@ +from typing import Optional + +from gvm.errors import RequiredArgument +from gvm.protocols.core import Request +from gvm.protocols.gmp.requests import EntityID +from gvm.utils import to_bool +from gvm.xml import XmlCommand + + +class IntegrationConfigs: + @classmethod + def get_integration_config( + cls, integration_config_id: EntityID, *, details: Optional[bool] = None + ) -> Request: + """Request a single Integration Configuration. + + Args: + integration_config_id: UUID of the integration config to request. + details: Whether to include detail information. + """ + if not integration_config_id: + raise RequiredArgument( + function=cls.get_integration_config.__name__, + argument="integration_config_id", + ) + + cmd = XmlCommand("get_integration_configs") + cmd.set_attribute("integration_config_id", str(integration_config_id)) + + if details is not None: + cmd.set_attribute("details", to_bool(details)) + + return cmd + + @classmethod + def get_integration_configs( + cls, + *, + filter_string: Optional[str] = None, + filter_id: Optional[EntityID] = None, + ) -> Request: + """Request a list of Integration Configurations. + + Args: + filter_string: Filter term to use for the query. + filter_id: UUID of an existing filter to use for the query. + """ + cmd = XmlCommand("get_integration_configs") + cmd.add_filter(filter_string, filter_id) + + return cmd + + @classmethod + def modify_integration_config( + cls, + integration_config_id: EntityID, + *, + service_url: Optional[str] = None, + service_cacert: Optional[str] = None, + oidc_provider_url: Optional[str] = None, + oidc_provider_client_id: Optional[str] = None, + oidc_provider_client_secret: Optional[str] = None, + ) -> Request: + """Modify an existing Integration Configuration. + + Args: + integration_config_id: UUID of configuration to modify. + service_url: Integration Service URL. + service_cacert: Integration Service Certificate. + oidc_provider_url: OIDC Provider URL. + oidc_provider_client_id: OIDC Provider Client ID. + oidc_provider_client_secret: OIDC Provider Client Secret. + """ + if not integration_config_id: + raise RequiredArgument( + function=cls.modify_integration_config.__name__, + argument="integration_config_id", + ) + + cmd = XmlCommand("modify_integration_config") + cmd.set_attribute("uuid", str(integration_config_id)) + + # element + service = cmd.add_element("service") + + if service_url is not None: + service.add_element("url", service_url) + else: + service.add_element("url") + if service_cacert is not None: + service.add_element("cacert", service_cacert) + else: + service.add_element("cacert") + + # element + oidc = cmd.add_element("oidc") + if oidc_provider_url is not None: + oidc.add_element("oidc_provider_url", oidc_provider_url) + else: + oidc.add_element("oidc_provider_url") + + oidc_client = oidc.add_element("client") + if oidc_provider_client_id is not None: + oidc_client.add_element("id", oidc_provider_client_id) + else: + oidc_client.add_element("id") + if oidc_provider_client_secret is not None: + oidc_client.add_element("secret", oidc_provider_client_secret) + else: + oidc_client.add_element("secret") + + return cmd diff --git a/tests/protocols/gmpnext/entities/integration_configs/__init__.py b/tests/protocols/gmpnext/entities/integration_configs/__init__.py new file mode 100644 index 00000000..99125f8c --- /dev/null +++ b/tests/protocols/gmpnext/entities/integration_configs/__init__.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later diff --git a/tests/protocols/gmpnext/entities/integration_configs/test_get_integration_config.py b/tests/protocols/gmpnext/entities/integration_configs/test_get_integration_config.py new file mode 100644 index 00000000..60242caa --- /dev/null +++ b/tests/protocols/gmpnext/entities/integration_configs/test_get_integration_config.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from gvm.errors import RequiredArgument + + +class GmpGetIntegrationConfigTestMixin: + def test_get_integration_config(self): + self.gmp.get_integration_config("uuid") + + self.connection.send.has_been_called_with( + b'' + ) + self.gmp.get_integration_config(integration_config_id="uuid") + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_integration_config_without_id(self): + with self.assertRaises(RequiredArgument): + self.gmp.get_integration_config(integration_config_id=None) + + with self.assertRaises(RequiredArgument): + self.gmp.get_integration_config("") + + def test_get_integration_config_with_details(self): + self.gmp.get_integration_config( + integration_config_id="uuid", details=True + ) + + self.connection.send.has_been_called_with( + b'' + ) + + self.gmp.get_integration_config( + integration_config_id="uuid", details=False + ) + + self.connection.send.has_been_called_with( + b'' + ) diff --git a/tests/protocols/gmpnext/entities/integration_configs/test_get_integration_configs.py b/tests/protocols/gmpnext/entities/integration_configs/test_get_integration_configs.py new file mode 100644 index 00000000..30b49468 --- /dev/null +++ b/tests/protocols/gmpnext/entities/integration_configs/test_get_integration_configs.py @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + + +class GmpGetIntegrationConfigsTestMixin: + def test_get_integration_configs(self): + self.gmp.get_integration_configs() + + self.connection.send.has_been_called_with(b"") + + def test_get_integration_configs_with_filter_string(self): + self.gmp.get_integration_configs(filter_string="foo=bar") + + self.connection.send.has_been_called_with( + b'' + ) + + def test_get_integration_configs_with_filter_id(self): + self.gmp.get_integration_configs(filter_id="f1") + + self.connection.send.has_been_called_with( + b'' + ) diff --git a/tests/protocols/gmpnext/entities/integration_configs/test_modify_integration_config.py b/tests/protocols/gmpnext/entities/integration_configs/test_modify_integration_config.py new file mode 100644 index 00000000..91ea7e6e --- /dev/null +++ b/tests/protocols/gmpnext/entities/integration_configs/test_modify_integration_config.py @@ -0,0 +1,170 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from gvm.errors import RequiredArgument + + +class GmpModifyIntegrationConfigTestMixin: + def test_modify_integration_config(self): + self.gmp.modify_integration_config(integration_config_id="ic1") + + self.connection.send.has_been_called_with( + b'' + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + ) + + def test_modify_integration_config_missing_integration_config_id(self): + with self.assertRaises(RequiredArgument): + self.gmp.modify_integration_config(integration_config_id=None) + + with self.assertRaises(RequiredArgument): + self.gmp.modify_integration_config(integration_config_id="") + + def test_modify_integration_config_with_service_url(self): + self.gmp.modify_integration_config( + integration_config_id="ic1", + service_url="https://service.example.com", + ) + + self.connection.send.has_been_called_with( + b'' + b"" + b"https://service.example.com" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + ) + + def test_modify_integration_config_with_service_cacert(self): + self.gmp.modify_integration_config( + integration_config_id="ic1", + service_cacert="-----BEGIN CERTIFICATE-----foo-----END CERTIFICATE-----", + ) + + self.connection.send.has_been_called_with( + b'' + b"" + b"" + b"-----BEGIN CERTIFICATE-----foo-----END CERTIFICATE-----" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + ) + + def test_modify_integration_config_with_oidc_provider_url(self): + self.gmp.modify_integration_config( + integration_config_id="ic1", + oidc_provider_url="https://oidc.example.com", + ) + + self.connection.send.has_been_called_with( + b'' + b"" + b"" + b"" + b"" + b"" + b"https://oidc.example.com" + b"" + b"" + b"" + b"" + b"" + b"" + ) + + def test_modify_integration_config_with_oidc_provider_client_id(self): + self.gmp.modify_integration_config( + integration_config_id="ic1", + oidc_provider_client_id="client-id-1", + ) + + self.connection.send.has_been_called_with( + b'' + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"client-id-1" + b"" + b"" + b"" + b"" + ) + + def test_modify_integration_config_with_oidc_provider_client_secret(self): + self.gmp.modify_integration_config( + integration_config_id="ic1", + oidc_provider_client_secret="secret-1", + ) + + self.connection.send.has_been_called_with( + b'' + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"" + b"secret-1" + b"" + b"" + b"" + ) + + def test_modify_integration_config_with_all_fields(self): + self.gmp.modify_integration_config( + integration_config_id="ic1", + service_url="https://service.example.com", + service_cacert="cacert-data", + oidc_provider_url="https://oidc.example.com", + oidc_provider_client_id="client-id-1", + oidc_provider_client_secret="secret-1", + ) + + self.connection.send.has_been_called_with( + b'' + b"" + b"https://service.example.com" + b"cacert-data" + b"" + b"" + b"https://oidc.example.com" + b"" + b"client-id-1" + b"secret-1" + b"" + b"" + b"" + ) diff --git a/tests/protocols/gmpnext/entities/test_integration_configs.py b/tests/protocols/gmpnext/entities/test_integration_configs.py new file mode 100644 index 00000000..4e21ea79 --- /dev/null +++ b/tests/protocols/gmpnext/entities/test_integration_configs.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2026 Greenbone AG +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from ...gmpnext import GMPTestCase +from .integration_configs.test_get_integration_config import ( + GmpGetIntegrationConfigTestMixin, +) +from .integration_configs.test_get_integration_configs import ( + GmpGetIntegrationConfigsTestMixin, +) +from .integration_configs.test_modify_integration_config import ( + GmpModifyIntegrationConfigTestMixin, +) + + +class GmpGetIntegrationConfigTestCase( + GmpGetIntegrationConfigTestMixin, GMPTestCase +): + pass + + +class GmpGetIntegrationConfigsTestCase( + GmpGetIntegrationConfigsTestMixin, GMPTestCase +): + pass + + +class GmpModifyIntegrationConfigTestCase( + GmpModifyIntegrationConfigTestMixin, GMPTestCase +): + pass