diff --git a/gvm/protocols/gmp/_gmpnext.py b/gvm/protocols/gmp/_gmpnext.py
index fae99d99..3ec405eb 100644
--- a/gvm/protocols/gmp/_gmpnext.py
+++ b/gvm/protocols/gmp/_gmpnext.py
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
-from typing import Any, Mapping, Optional, Sequence
+from typing import Any, Mapping, Optional, Sequence, Union
from gvm.protocols.gmp.requests import EntityID
@@ -13,6 +13,9 @@
AgentGroups,
AgentInstallers,
Agents,
+ Credentials,
+ CredentialStoreCredentialType,
+ CredentialStores,
OCIImageTargets,
Tasks,
)
@@ -297,6 +300,174 @@ def clone_agent_group(
AgentGroups.clone_agent_group(agent_group_id)
)
+ def create_credential_store_credential(
+ self,
+ name: str,
+ credential_type: Union[CredentialStoreCredentialType, str],
+ *,
+ comment: Optional[str] = None,
+ credential_store_id: Optional[EntityID] = None,
+ vault_id: Optional[str] = None,
+ host_identifier: Optional[str] = None,
+ ) -> T:
+ """Create a new credential store type credential
+
+ Args:
+ name: Name of the credential
+ credential_type: Type of the credential
+ comment: Optional comment for the credential object
+ credential_store_id: Optional credential store id to fetch the credential from
+ vault_id: Vault id used to fetch the credential from credential store
+ host_identifier: Host identifier used to fetch the credential from credential store
+ """
+ return self._send_request_and_transform_response(
+ Credentials.create_credential_store_credential(
+ name=name,
+ credential_type=credential_type,
+ comment=comment,
+ credential_store_id=credential_store_id,
+ vault_id=vault_id,
+ host_identifier=host_identifier,
+ )
+ )
+
+ def modify_credential_store_credential(
+ self,
+ credential_id: EntityID,
+ *,
+ name: Optional[str] = None,
+ comment: Optional[str] = None,
+ credential_store_id: Optional[EntityID] = None,
+ vault_id: Optional[str] = None,
+ host_identifier: Optional[str] = None,
+ ) -> T:
+ """Modify an existing credential stored in a credential store
+
+ Args:
+ credential_id: UUID of the credential to modify
+ name: Name of the credential
+ comment: Optional comment for the credential object
+ credential_store_id: Optional credential store id to fetch the credential from
+ vault_id: Vault id used to fetch the credential from credential store
+ host_identifier: Host identifier used to fetch the credential from credential store
+ """
+ return self._send_request_and_transform_response(
+ Credentials.modify_credential_store_credential(
+ credential_id=credential_id,
+ name=name,
+ comment=comment,
+ credential_store_id=credential_store_id,
+ vault_id=vault_id,
+ host_identifier=host_identifier,
+ )
+ )
+
+ def get_credential_store(
+ self,
+ credential_store_id: EntityID,
+ *,
+ details: Optional[bool] = None,
+ ) -> T:
+ """Request a credential store
+
+ Args:
+ credential_store_id: ID of credential store to fetch
+ details: True to request all details
+ """
+ return self._send_request_and_transform_response(
+ CredentialStores.get_credential_store(
+ credential_store_id=credential_store_id,
+ details=details,
+ )
+ )
+
+ def get_credential_stores(
+ self,
+ *,
+ filter_string: Optional[str] = None,
+ filter_id: Optional[EntityID] = None,
+ details: Optional[bool] = None,
+ ) -> T:
+ """Request a list of credential stores
+
+ Args:
+ filter_string: Filter term to use for the query
+ filter_id: UUID of an existing filter to use for the query
+ details: True to request all details
+ """
+ return self._send_request_and_transform_response(
+ CredentialStores.get_credential_stores(
+ filter_string=filter_string,
+ filter_id=filter_id,
+ details=details,
+ )
+ )
+
+ def modify_credential_store(
+ self,
+ credential_store_id: EntityID,
+ *,
+ active: Optional[bool] = None,
+ host: Optional[str] = None,
+ port: Optional[int] = None,
+ path: Optional[str] = None,
+ app_id: Optional[str] = None,
+ client_cert: Optional[str] = None,
+ client_key: Optional[str] = None,
+ client_pkcs12_file: Optional[str] = None,
+ passphrase: Optional[str] = None,
+ server_ca_cert: Optional[str] = None,
+ comment: Optional[str] = None,
+ ) -> T:
+ """Modify an existing credential store
+
+ Args:
+ credential_store_id: ID of credential store to fetch
+ active: Whether the credential store is active
+ host: The host to use for reaching the credential store
+ port: The port to use for reaching the credential store
+ path: The URI path the credential store is using
+ app_id: Depends on the credential store used. Usually called the same in the credential store
+ client_cert: The client certificate to use for authorization, as a plain string
+ client_key: The client key to use for authorization, as a plain string
+ client_pkcs12_file: The pkcs12 file contents to use for authorization, as a plain string
+ (alternative to using client_cert and client_key)
+ passphrase: The passphrase to use to decrypt client_pkcs12_file or client_key file
+ server_ca_cert: The server certificate, so the credential store can be trusted
+ comment: An optional comment to store alongside the credential store
+ """
+ return self._send_request_and_transform_response(
+ CredentialStores.modify_credential_store(
+ credential_store_id=credential_store_id,
+ active=active,
+ host=host,
+ port=port,
+ path=path,
+ app_id=app_id,
+ client_cert=client_cert,
+ client_key=client_key,
+ client_pkcs12_file=client_pkcs12_file,
+ passphrase=passphrase,
+ server_ca_cert=server_ca_cert,
+ comment=comment,
+ )
+ )
+
+ def verify_credential_store(
+ self,
+ credential_store_id: EntityID,
+ ) -> T:
+ """Verify that the connection to an existing credential store works
+
+ Args:
+ credential_store_id: The uuid of the credential store to verify
+ """
+ return self._send_request_and_transform_response(
+ CredentialStores.verify_credential_store(
+ credential_store_id=credential_store_id,
+ )
+ )
+
def create_oci_image_target(
self,
name: str,
diff --git a/gvm/protocols/gmp/requests/next/__init__.py b/gvm/protocols/gmp/requests/next/__init__.py
index 109199a8..b6541fcf 100644
--- a/gvm/protocols/gmp/requests/next/__init__.py
+++ b/gvm/protocols/gmp/requests/next/__init__.py
@@ -5,6 +5,11 @@
from gvm.protocols.gmp.requests.next._agent_groups import AgentGroups
from gvm.protocols.gmp.requests.next._agent_installers import AgentInstallers
from gvm.protocols.gmp.requests.next._agents import Agents
+from gvm.protocols.gmp.requests.next._credential_stores import CredentialStores
+from gvm.protocols.gmp.requests.next._credentials import (
+ Credentials,
+ CredentialStoreCredentialType,
+)
from gvm.protocols.gmp.requests.next._oci_image_targets import OCIImageTargets
from gvm.protocols.gmp.requests.next._tasks import Tasks
@@ -24,7 +29,6 @@
CertBundAdvisories,
Cpes,
CredentialFormat,
- Credentials,
CredentialType,
Cves,
DfnCertAdvisories,
@@ -98,6 +102,8 @@
"Credentials",
"CredentialFormat",
"CredentialType",
+ "CredentialStoreCredentialType",
+ "CredentialStores",
"Cves",
"DfnCertAdvisories",
"EntityID",
diff --git a/gvm/protocols/gmp/requests/next/_credential_stores.py b/gvm/protocols/gmp/requests/next/_credential_stores.py
new file mode 100644
index 00000000..67ae0db6
--- /dev/null
+++ b/gvm/protocols/gmp/requests/next/_credential_stores.py
@@ -0,0 +1,171 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from base64 import b64encode
+from typing import Optional
+
+from gvm.errors import RequiredArgument
+from gvm.protocols.core import Request
+from gvm.utils import to_bool
+from gvm.xml import XmlCommand
+
+from .._entity_id import EntityID
+
+
+class CredentialStores:
+ @classmethod
+ def get_credential_store(
+ cls,
+ credential_store_id: EntityID,
+ *,
+ details: Optional[bool] = None,
+ ) -> Request:
+ """Request a credential store
+
+ Args:
+ credential_store_id: ID of credential store to fetch
+ details: True to request all details
+ """
+
+ if not credential_store_id:
+ raise RequiredArgument(
+ function=cls.get_credential_store.__name__,
+ argument="credential_store_id",
+ )
+
+ cmd = XmlCommand("get_credential_stores")
+ cmd.add_element("credential_store_id", str(credential_store_id))
+
+ if details is not None:
+ cmd.set_attribute("details", to_bool(details))
+
+ return cmd
+
+ @classmethod
+ def get_credential_stores(
+ cls,
+ *,
+ filter_string: Optional[str] = None,
+ filter_id: Optional[EntityID] = None,
+ details: Optional[bool] = None,
+ ) -> Request:
+ """Request a list of credential stores
+
+ Args:
+ filter_string: Filter term to use for the query
+ filter_id: UUID of an existing filter to use for the query
+ details: True to request all details
+ """
+
+ cmd = XmlCommand("get_credential_stores")
+ cmd.add_filter(filter_string, filter_id)
+
+ if details is not None:
+ cmd.set_attribute("details", to_bool(details))
+
+ return cmd
+
+ @classmethod
+ def modify_credential_store(
+ cls,
+ credential_store_id: EntityID,
+ *,
+ active: Optional[bool] = None,
+ host: Optional[str] = None,
+ port: Optional[int] = None,
+ path: Optional[str] = None,
+ app_id: Optional[str] = None,
+ client_cert: Optional[str] = None,
+ client_key: Optional[str] = None,
+ client_pkcs12_file: Optional[str] = None,
+ passphrase: Optional[str] = None,
+ server_ca_cert: Optional[str] = None,
+ comment: Optional[str] = None,
+ ) -> Request:
+ """Modify a credential store
+
+ Args:
+ credential_store_id: ID of credential store to fetch
+ active: Whether the credential store is active
+ host: The host to use for reaching the credential store
+ port: The port to use for reaching the credential store
+ path: The URI path the credential store is using
+ app_id: Depends on the credential store used. Usually called the same in the credential store
+ client_cert: The client certificate to use for authorization, as a plain string
+ client_key: The client key to use for authorization, as a plain string
+ client_pkcs12_file: The pkcs12 file contents to use for authorization, as a plain string
+ (alternative to using client_cert and client_key)
+ passphrase: The passphrase to use to decrypt client_pkcs12_file or client_key file
+ server_ca_cert: The server certificate, so the credential store can be trusted
+ comment: An optional comment to store alongside the credential store
+ """
+
+ if not credential_store_id:
+ raise RequiredArgument(
+ function=cls.verify_credential_store.__name__,
+ argument="credential_store_id",
+ )
+
+ cmd = XmlCommand("modify_credential_store")
+ cmd.set_attribute("credential_store_id", str(credential_store_id))
+
+ if active is not None:
+ cmd.add_element("active", to_bool(active))
+ if host:
+ cmd.add_element("host", host)
+ if port:
+ cmd.add_element("port", str(port))
+ if path:
+ cmd.add_element("path", path)
+ if comment:
+ cmd.add_element("comment", comment)
+
+ preferences = cmd.add_element("preferences")
+
+ if app_id:
+ preferences.add_element("app_id", app_id)
+ if client_cert:
+ preferences.add_element(
+ "client_cert",
+ b64encode(client_cert.encode("ascii")).decode("ascii"),
+ )
+ if client_key:
+ preferences.add_element(
+ "client_key",
+ b64encode(client_key.encode("ascii")).decode("ascii"),
+ )
+ if client_pkcs12_file:
+ preferences.add_element(
+ "client_pkcs12_file",
+ b64encode(client_pkcs12_file.encode("ascii")).decode("ascii"),
+ )
+ if passphrase:
+ preferences.add_element("passphrase", passphrase)
+ if server_ca_cert:
+ preferences.add_element(
+ "server_ca_cert",
+ b64encode(server_ca_cert.encode("ascii")).decode("ascii"),
+ )
+
+ return cmd
+
+ @classmethod
+ def verify_credential_store(
+ cls,
+ credential_store_id: EntityID,
+ ) -> Request:
+ """Verify that the connection to a credential store works
+
+ Args:
+ credential_store_id: The uuid of the credential store to verify
+ """
+ if not credential_store_id:
+ raise RequiredArgument(
+ function=cls.verify_credential_store.__name__,
+ argument="credential_store_id",
+ )
+
+ cmd = XmlCommand("verify_credential_store")
+ cmd.set_attribute("credential_store_id", str(credential_store_id))
+ return cmd
diff --git a/gvm/protocols/gmp/requests/next/_credentials.py b/gvm/protocols/gmp/requests/next/_credentials.py
new file mode 100644
index 00000000..a8e9d454
--- /dev/null
+++ b/gvm/protocols/gmp/requests/next/_credentials.py
@@ -0,0 +1,179 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from typing import Optional, Union
+
+from gvm._enum import Enum
+from gvm.errors import InvalidArgument, RequiredArgument
+from gvm.protocols.core import Request
+from gvm.xml import XmlCommand
+
+from .._entity_id import EntityID
+from ..v224._credentials import (
+ Credentials as CredentialsV224,
+)
+
+
+class CredentialStoreCredentialType(Enum):
+ """Enum for credential store credential types"""
+
+ CLIENT_CERTIFICATE = "cs_cc"
+ SNMP = "cs_snmp"
+ USERNAME_PASSWORD = "cs_up"
+ USERNAME_SSH_KEY = "cs_usk"
+ SMIME_CERTIFICATE = "cs_smime"
+ PGP_ENCRYPTION_KEY = "cs_pgp"
+ PASSWORD_ONLY = "cs_pw"
+
+
+class Credentials(CredentialsV224):
+ @classmethod
+ def create_credential_store_credential(
+ cls,
+ name: str,
+ credential_type: Union[CredentialStoreCredentialType, str],
+ *,
+ comment: Optional[str] = None,
+ credential_store_id: Optional[EntityID] = None,
+ vault_id: Optional[str] = None,
+ host_identifier: Optional[str] = None,
+ ) -> Request:
+ """Create a new credential that is fetched from a credential store
+
+ Create a new credential e.g. to be used in the method of an alert.
+
+ Currently the following credential types are supported:
+
+ - Username + Password
+ - Username + SSH-Key
+ - Client Certificates
+ - SNMPv1 or SNMPv2c protocol
+ - S/MIME Certificate
+ - OpenPGP Key
+ - Password only
+
+ Arguments:
+ name: Name of the new credential
+ credential_type: The credential type.
+ comment: Comment for the credential
+ credential_store_id: Optional id of the credential store to use
+ (gvmd will pick default one if none is provided)
+ vault_id: Vault-ID used to access the secret in credential store
+ host_identifier: Host-Identifier used to access the secret in credential store
+
+ Examples:
+ Creating a Password-Only credential stored in a Credential Store
+
+ .. code-block:: python
+
+ request = Credentials.create_credential(
+ name='Credential-Store Password-Only Credential',
+ credential_type=CredentialType.CREDENTIAL_STOREPASSWORD_ONLY,
+ vault_id='a5f84dd4-da18-447c-a9fb-b77b5df49076',
+ host_identifier='/My/Secret',
+ )
+ """
+ if not name:
+ raise RequiredArgument(
+ function=cls.create_credential.__name__, argument="name"
+ )
+
+ if not credential_type:
+ raise RequiredArgument(
+ function=cls.create_credential.__name__,
+ argument="credential_type",
+ )
+
+ if not isinstance(credential_type, CredentialStoreCredentialType):
+ credential_type = CredentialStoreCredentialType(credential_type)
+
+ cmd = XmlCommand("create_credential")
+ cmd.add_element("name", name)
+
+ cmd.add_element("type", credential_type.value)
+
+ if comment:
+ cmd.add_element("comment", comment)
+
+ if (
+ credential_type != CredentialStoreCredentialType.CLIENT_CERTIFICATE
+ and credential_type != CredentialStoreCredentialType.SNMP
+ and credential_type
+ != CredentialStoreCredentialType.USERNAME_PASSWORD
+ and credential_type
+ != CredentialStoreCredentialType.USERNAME_SSH_KEY
+ and credential_type
+ != CredentialStoreCredentialType.SMIME_CERTIFICATE
+ and credential_type
+ != CredentialStoreCredentialType.PGP_ENCRYPTION_KEY
+ and credential_type != CredentialStoreCredentialType.PASSWORD_ONLY
+ ):
+ raise InvalidArgument(
+ function=cls.create_credential.__name__,
+ argument="credential_type",
+ )
+
+ if not vault_id:
+ raise RequiredArgument(
+ function=cls.create_credential.__name__,
+ argument="vault_id",
+ )
+ if not host_identifier:
+ raise RequiredArgument(
+ function=cls.create_credential.__name__,
+ argument="host_identifier",
+ )
+
+ if credential_store_id:
+ cmd.add_element("credential_store_id", str(credential_store_id))
+
+ cmd.add_element("vault_id", vault_id)
+ cmd.add_element("host_identifier", host_identifier)
+
+ return cmd
+
+ @classmethod
+ def modify_credential_store_credential(
+ cls,
+ credential_id: EntityID,
+ *,
+ name: Optional[str] = None,
+ comment: Optional[str] = None,
+ credential_store_id: Optional[EntityID] = None,
+ vault_id: Optional[str] = None,
+ host_identifier: Optional[str] = None,
+ ) -> Request:
+ """Modifies an existing credential.
+
+ Arguments:
+ credential_id: UUID of the credential
+ name: Name of the credential
+ comment: Comment for the credential
+ credential_store_id: Optional id of the credential store to use
+ (gvmd will pick default one if none is provided)
+ vault_id: Vault-ID used to access the secret in credential store
+ host_identifier: Host-Identifier used to access the secret in credential store
+ """
+ if not credential_id:
+ raise RequiredArgument(
+ function=cls.modify_credential.__name__,
+ argument="credential_id",
+ )
+
+ cmd = XmlCommand("modify_credential")
+ cmd.set_attribute("credential_id", str(credential_id))
+
+ if name:
+ cmd.add_element("name", name)
+ if comment:
+ cmd.add_element("comment", comment)
+
+ if credential_store_id:
+ cmd.add_element("credential_store_id", str(credential_store_id))
+ if vault_id:
+ cmd.add_element("vault_id", vault_id)
+ if host_identifier:
+ cmd.add_element("host_identifier", host_identifier)
+
+ return cmd
diff --git a/tests/protocols/gmpnext/entities/credential_stores/__init__.py b/tests/protocols/gmpnext/entities/credential_stores/__init__.py
new file mode 100644
index 00000000..ca5fe222
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/credential_stores/__init__.py
@@ -0,0 +1,13 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from .test_get_credential_stores import GmpGetCredentialStoresTestMixin
+from .test_modify_credential_stores import GmpModifyCredentialStoreTestMixin
+from .test_verify_credential_stores import GmpVerifyCredentialStoreTestMixin
+
+__all__ = (
+ "GmpGetCredentialStoresTestMixin",
+ "GmpModifyCredentialStoreTestMixin",
+ "GmpVerifyCredentialStoreTestMixin",
+)
diff --git a/tests/protocols/gmpnext/entities/credential_stores/test_get_credential_stores.py b/tests/protocols/gmpnext/entities/credential_stores/test_get_credential_stores.py
new file mode 100644
index 00000000..6d460585
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/credential_stores/test_get_credential_stores.py
@@ -0,0 +1,73 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from gvm.errors import RequiredArgument
+
+
+class GmpGetCredentialStoresTestMixin:
+ def test_get_credential_store(self):
+ self.gmp.get_credential_store(credential_store_id="cs1")
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"cs1"
+ b""
+ )
+
+ def test_get_credential_store_with_details(self):
+ self.gmp.get_credential_store(credential_store_id="cs1", details=True)
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"cs1"
+ b""
+ )
+
+ self.gmp.get_credential_store(credential_store_id="cs1", details=False)
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"cs1"
+ b""
+ )
+
+ def test_get_credential_store_without_id(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.get_credential_store(credential_store_id=None)
+
+ with self.assertRaises(RequiredArgument):
+ self.gmp.get_credential_store(credential_store_id="")
+
+ def test_get_credential_stores(self):
+ self.gmp.get_credential_stores()
+
+ self.connection.send.has_been_called_with(b"")
+
+ def test_get_credential_stores_with_filter_string(self):
+ self.gmp.get_credential_stores(filter_string="foo=bar")
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_get_credential_stores_with_filter_id(self):
+ self.gmp.get_credential_stores(filter_id="f1")
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_get_credential_stores_with_details(self):
+ self.gmp.get_credential_stores(details=True)
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ self.gmp.get_credential_stores(details=False)
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
diff --git a/tests/protocols/gmpnext/entities/credential_stores/test_modify_credential_stores.py b/tests/protocols/gmpnext/entities/credential_stores/test_modify_credential_stores.py
new file mode 100644
index 00000000..0210b505
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/credential_stores/test_modify_credential_stores.py
@@ -0,0 +1,219 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from gvm.errors import RequiredArgument
+
+
+class GmpModifyCredentialStoreTestMixin:
+ def test_modify_credential_store(self):
+ self.gmp.modify_credential_store(credential_store_id="cs1")
+
+ self.connection.send.has_been_called_with(
+ b''
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_without_id(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.modify_credential_store(credential_store_id=None)
+
+ with self.assertRaises(RequiredArgument):
+ self.gmp.modify_credential_store(credential_store_id="")
+
+ def test_modify_credential_store_with_active(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ active=True,
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"1"
+ b""
+ b""
+ )
+
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ active=False,
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"0"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_host(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ host="foo",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"foo"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_port(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ port=1234,
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"1234"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_path(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ path="/foo/bar",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"/foo/bar"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_comment(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ comment="ahoi",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"ahoi"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_app_id(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ app_id="foo",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b""
+ b"foo"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_client_cert(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ client_cert="foo",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b""
+ b"Zm9v"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_client_key(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ client_key="foo",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b""
+ b"Zm9v"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_client_pkcs12_file(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ client_pkcs12_file="foo",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b""
+ b"Zm9v"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_passphrase(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ passphrase="foo",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b""
+ b"foo"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_server_ca_cert(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ server_ca_cert="foo",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b""
+ b"Zm9v"
+ b""
+ b""
+ )
+
+ def test_modify_credential_store_with_all(self):
+ self.gmp.modify_credential_store(
+ credential_store_id="cs1",
+ active=False,
+ host="localhost",
+ port="80",
+ path="/api",
+ comment="why was 6 afraid of 7? because 7 8 9",
+ app_id="appId",
+ client_cert="clientCert",
+ client_key="clientKey",
+ client_pkcs12_file="clientPkcs12File",
+ passphrase="secret",
+ server_ca_cert="serverCaCert",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"0"
+ b"localhost"
+ b"80"
+ b"/api"
+ b"why was 6 afraid of 7? because 7 8 9"
+ b""
+ b"appId"
+ b"Y2xpZW50Q2VydA=="
+ b"Y2xpZW50S2V5"
+ b"Y2xpZW50UGtjczEyRmlsZQ=="
+ b"secret"
+ b"c2VydmVyQ2FDZXJ0"
+ b""
+ b""
+ )
diff --git a/tests/protocols/gmpnext/entities/credential_stores/test_verify_credential_stores.py b/tests/protocols/gmpnext/entities/credential_stores/test_verify_credential_stores.py
new file mode 100644
index 00000000..d1c3d298
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/credential_stores/test_verify_credential_stores.py
@@ -0,0 +1,22 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from gvm.errors import RequiredArgument
+
+
+class GmpVerifyCredentialStoreTestMixin:
+ def test_verify_credential_store(self):
+ self.gmp.verify_credential_store(credential_store_id="cs1")
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_verify_credential_store_without_id(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.verify_credential_store(credential_store_id=None)
+
+ with self.assertRaises(RequiredArgument):
+ self.gmp.verify_credential_store(credential_store_id="")
diff --git a/tests/protocols/gmpnext/entities/credentials/__init__.py b/tests/protocols/gmpnext/entities/credentials/__init__.py
new file mode 100644
index 00000000..4a49c76e
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/credentials/__init__.py
@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from .test_create_credential_store_credential import (
+ GmpCreateCredentialStoreCredentialTestMixin,
+)
+from .test_modify_credential_store_credential import (
+ GmpModifyCredentialStoreCredentialTestMixin,
+)
+
+__all__ = (
+ "GmpCreateCredentialStoreCredentialTestMixin",
+ "GmpModifyCredentialStoreCredentialTestMixin",
+)
diff --git a/tests/protocols/gmpnext/entities/credentials/test_create_credential_store_credential.py b/tests/protocols/gmpnext/entities/credentials/test_create_credential_store_credential.py
new file mode 100644
index 00000000..0a17c452
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/credentials/test_create_credential_store_credential.py
@@ -0,0 +1,201 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from gvm.errors import InvalidArgument, RequiredArgument
+from gvm.protocols.gmp.requests.next import CredentialStoreCredentialType
+
+
+class GmpCreateCredentialStoreCredentialTestMixin:
+ def test_create_cs_up_credential_missing_name(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.create_credential_store_credential(
+ name="",
+ credential_type=CredentialStoreCredentialType.USERNAME_PASSWORD,
+ vault_id="foo",
+ host_identifier="bar",
+ )
+
+ with self.assertRaises(RequiredArgument):
+ self.gmp.create_credential_store_credential(
+ name=None,
+ credential_type=CredentialStoreCredentialType.USERNAME_PASSWORD,
+ vault_id="foo",
+ host_identifier="bar",
+ )
+
+ def test_create_cs_up_credential(self):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.USERNAME_PASSWORD,
+ comment="bar",
+ vault_id="123",
+ host_identifier="456",
+ )
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"foo"
+ b"cs_up"
+ b"bar"
+ b"123"
+ b"456"
+ b""
+ )
+
+ def test_create_cs_up_credential_with_credential_store_id(self):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.USERNAME_PASSWORD,
+ comment="bar",
+ credential_store_id="abc",
+ vault_id="123",
+ host_identifier="456",
+ )
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"foo"
+ b"cs_up"
+ b"bar"
+ b"abc"
+ b"123"
+ b"456"
+ b""
+ )
+
+ def test_create_cs_up_credential_with_missing_vault_id(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.USERNAME_PASSWORD,
+ comment="bar",
+ host_identifier="456",
+ )
+
+ def test_create_cs_up_credential_with_missing_host_identifier(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.USERNAME_PASSWORD,
+ comment="bar",
+ vault_id="123",
+ )
+
+ def test_create_cs_cc_credential(self):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.CLIENT_CERTIFICATE,
+ vault_id="123",
+ host_identifier="456",
+ )
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"foo"
+ b"cs_cc"
+ b"123"
+ b"456"
+ b""
+ )
+
+ def test_create_cs_usk_credential(self):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.USERNAME_SSH_KEY,
+ vault_id="123",
+ host_identifier="456",
+ )
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"foo"
+ b"cs_usk"
+ b"123"
+ b"456"
+ b""
+ )
+
+ def test_create_cs_snmp_credential(self):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.SNMP,
+ vault_id="123",
+ host_identifier="456",
+ )
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"foo"
+ b"cs_snmp"
+ b"123"
+ b"456"
+ b""
+ )
+
+ def test_create_cs_smime_credential(self):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.SMIME_CERTIFICATE,
+ vault_id="123",
+ host_identifier="456",
+ )
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"foo"
+ b"cs_smime"
+ b"123"
+ b"456"
+ b""
+ )
+
+ def test_create_cs_pgp_credential(self):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.PGP_ENCRYPTION_KEY,
+ vault_id="123",
+ host_identifier="456",
+ )
+
+ self.connection.send.has_been_called_with(
+ b""
+ b"foo"
+ b"cs_pgp"
+ b"123"
+ b"456"
+ b""
+ )
+
+ def test_create_cs_credential_invalid_credential_type(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.create_credential_store_credential(
+ name="foo", credential_type=None
+ )
+
+ with self.assertRaises(RequiredArgument):
+ self.gmp.create_credential_store_credential(
+ name="foo", credential_type=""
+ )
+
+ with self.assertRaises(InvalidArgument):
+ self.gmp.create_credential_store_credential(
+ name="foo", credential_type="bar"
+ )
+
+ def test_create_cs_pw_credential(self):
+ self.gmp.create_credential_store_credential(
+ name="foo",
+ credential_type=CredentialStoreCredentialType.PASSWORD_ONLY,
+ vault_id="123",
+ host_identifier="456",
+ )
+ self.connection.send.has_been_called_with(
+ b""
+ b"foo"
+ b"cs_pw"
+ b"123"
+ b"456"
+ b""
+ )
diff --git a/tests/protocols/gmpnext/entities/credentials/test_modify_credential_store_credential.py b/tests/protocols/gmpnext/entities/credentials/test_modify_credential_store_credential.py
new file mode 100644
index 00000000..b8dbc860
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/credentials/test_modify_credential_store_credential.py
@@ -0,0 +1,100 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from gvm.errors import RequiredArgument
+
+
+class GmpModifyCredentialStoreCredentialTestMixin:
+ def test_modify_cs_credential(self):
+ self.gmp.modify_credential_store_credential(credential_id="c1")
+
+ self.connection.send.has_been_called_with(
+ b''
+ )
+
+ def test_modify_cs_credential_missing_credential_id(self):
+ with self.assertRaises(RequiredArgument):
+ self.gmp.modify_credential_store_credential(None)
+
+ with self.assertRaises(RequiredArgument):
+ self.gmp.modify_credential_store_credential("")
+
+ with self.assertRaises(RequiredArgument):
+ self.gmp.modify_credential_store_credential(credential_id="")
+
+ def test_modify_cs_credential_with_name(self):
+ self.gmp.modify_credential_store_credential(
+ credential_id="c1", name="foo"
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"foo"
+ b""
+ )
+
+ def test_modify_cs_credential_with_comment(self):
+ self.gmp.modify_credential_store_credential(
+ credential_id="c1", comment="foo"
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"foo"
+ b""
+ )
+
+ def test_modify_cs_credential_with_credential_store_id(self):
+ self.gmp.modify_credential_store_credential(
+ credential_id="c1", credential_store_id="foo"
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"foo"
+ b""
+ )
+
+ def test_modify_cs_credential_with_vault_id(self):
+ self.gmp.modify_credential_store_credential(
+ credential_id="c1", vault_id="foo"
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"foo"
+ b""
+ )
+
+ def test_modify_cs_credential_with_host_identifier(self):
+ self.gmp.modify_credential_store_credential(
+ credential_id="c1", host_identifier="foo"
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"foo"
+ b""
+ )
+
+ def test_modify_cs_credential_with_all(self):
+ self.gmp.modify_credential_store_credential(
+ credential_id="c1",
+ name="foo_name",
+ comment="foo_comment",
+ credential_store_id="foo_csid",
+ vault_id="foo_vid",
+ host_identifier="foo_hid",
+ )
+
+ self.connection.send.has_been_called_with(
+ b''
+ b"foo_name"
+ b"foo_comment"
+ b"foo_csid"
+ b"foo_vid"
+ b"foo_hid"
+ b""
+ )
diff --git a/tests/protocols/gmpnext/entities/test_credential_stores.py b/tests/protocols/gmpnext/entities/test_credential_stores.py
new file mode 100644
index 00000000..1c3628e6
--- /dev/null
+++ b/tests/protocols/gmpnext/entities/test_credential_stores.py
@@ -0,0 +1,27 @@
+# SPDX-FileCopyrightText: 2025 Greenbone AG
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from ...gmpnext import GMPTestCase
+from ..entities.credential_stores import (
+ GmpGetCredentialStoresTestMixin,
+ GmpModifyCredentialStoreTestMixin,
+ GmpVerifyCredentialStoreTestMixin,
+)
+
+
+class GMPGetCredentialStoresTest(GmpGetCredentialStoresTestMixin, GMPTestCase):
+ pass
+
+
+class GMPModifyCredentialStoreTest(
+ GmpModifyCredentialStoreTestMixin, GMPTestCase
+):
+ pass
+
+
+class GMPVerifyCredentialStoreTest(
+ GmpVerifyCredentialStoreTestMixin, GMPTestCase
+):
+ pass
diff --git a/tests/protocols/gmpnext/entities/test_credentials.py b/tests/protocols/gmpnext/entities/test_credentials.py
index 5843ed24..6fd67390 100644
--- a/tests/protocols/gmpnext/entities/test_credentials.py
+++ b/tests/protocols/gmpnext/entities/test_credentials.py
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#
+from ...gmpnext import GMPTestCase
from ...gmpv224.entities.credentials import (
GmpCloneCredentialTestMixin,
GmpCreateCredentialTestMixin,
@@ -11,14 +12,21 @@
GmpGetCredentialTestMixin,
GmpModifyCredentialTestMixin,
)
-from ...gmpv227 import GMPTestCase
+from ..entities.credentials import (
+ GmpCreateCredentialStoreCredentialTestMixin,
+ GmpModifyCredentialStoreCredentialTestMixin,
+)
class GMPCloneCredentialTestCase(GmpCloneCredentialTestMixin, GMPTestCase):
pass
-class GMPCreateCredentialTestCase(GmpCreateCredentialTestMixin, GMPTestCase):
+class GMPCreateCredentialTestCase(
+ GmpCreateCredentialTestMixin,
+ GmpCreateCredentialStoreCredentialTestMixin,
+ GMPTestCase,
+):
pass
@@ -34,5 +42,9 @@ class GMPGetCredentialsTestCase(GmpGetCredentialsTestMixin, GMPTestCase):
pass
-class GMPModifyCredentialTestCase(GmpModifyCredentialTestMixin, GMPTestCase):
+class GMPModifyCredentialTestCase(
+ GmpModifyCredentialTestMixin,
+ GmpModifyCredentialStoreCredentialTestMixin,
+ GMPTestCase,
+):
pass