diff --git a/README.md b/README.md
index 8e414bb..dc754e5 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@ This app supports the following popular secrets backends:
| [AWS Systems Manager Parameter Store](https://aws.amazon.com/secrets-manager/) | [Other: Key/value pairs](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) | [AWS credentials](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html) (see Usage section below) |
| [HashiCorp Vault](https://www.vaultproject.io) | [K/V Version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2)
[K/V Version 1](https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v1) | [Token](https://www.vaultproject.io/docs/auth/token)
[AppRole](https://www.vaultproject.io/docs/auth/approle)
[AWS](https://www.vaultproject.io/docs/auth/aws)
[Kubernetes](https://www.vaultproject.io/docs/auth/kubernetes) |
| [Delinea/Thycotic Secret Server](https://delinea.com/products/secret-server) | [Secret Server Cloud](https://github.com/DelineaXPM/python-tss-sdk#secret-server-cloud)
[Secret Server (on-prem)](https://github.com/DelineaXPM/python-tss-sdk#initializing-secretserver)| [Access Token Authorization](https://github.com/DelineaXPM/python-tss-sdk#access-token-authorization)
[Domain Authorization](https://github.com/DelineaXPM/python-tss-sdk#domain-authorization)
[Password Authorization](https://github.com/DelineaXPM/python-tss-sdk#password-authorization)
|
+| [CyberARK AIM](https://www.cyberark.com/) | [Password](https://www.cyberark.com/what-is/secrets-management/) | Token
Username/Password |
## Screenshots
@@ -85,6 +86,12 @@ The Delinea/Thycotic Secret Server provider requires the `python-tss-sdk` librar
pip install nautobot-secrets-providers[thycotic]
```
+### CyberARK AIM
+
+```no-highlight
+pip install nautobot-secrets-providers[cyberark]
+```
+
### Enabling Secrets Providers
To ensure Nautobot Secrets Providers is automatically re-installed during future upgrades, create a file named `local_requirements.txt` (if not already existing) in the Nautobot root directory (alongside `requirements.txt`) and list the `secrets` package:
@@ -228,6 +235,34 @@ PLUGINS_CONFIG = {
- `token` - (optional) Required for 'Access Token Authorization'.
- `username` - (optional) Required for 'Secret Server Cloud', 'Password Authorization', 'Domain Authorization'.
+### CyberARK AIM
+
+The CyberARK AIM Secret Provider includes only one provider:
+
+- **`CyberARK AIM`**
+
+ This provider allows to get a password from CyberARK Password Vault using API.
+ The user configured to access to CyberARK needs to be able to see the Vault where the account is stored to see it, if not it will return an error.
+
+#### Configuration
+
+```python
+PLUGINS_CONFIG = {
+ "nautobot_secrets_providers": {
+ "cyberark": {
+ "url": os.getenv("NAUTOBOT_CYBERARK_URL", None),
+ "token": os.getenv("NAUTOBOT_CYBERARK_TOKEN", None),
+ "username": os.getenv("NAUTOBOT_CYBERARK_USERNAME", None),
+ "password": os.getenv("NAUTOBOT_CYBERARK_PASSWORD", None),
+ }
+ }
+}
+```
+- `url` - (required) The URL to access to the CyberARK instance. _e.g. 'https://cyberark.example.local' (Without the slash '/' char at the end)
+- `token` - (optional) Either token or username/password need to be set to access the CyberARK API.
+- `username` - (optional) Either token or username/password need to be set to access the CyberARK API.
+- `password` - (optional) Either token or username/password need to be set to access the CyberARK API.
+
## Contributing
Pull requests are welcomed and automatically built and tested against multiple version of Python and multiple version of Nautobot through GitHub Actions.
diff --git a/nautobot_secrets_providers/providers/__init__.py b/nautobot_secrets_providers/providers/__init__.py
index 3a0baf9..ea44b6d 100644
--- a/nautobot_secrets_providers/providers/__init__.py
+++ b/nautobot_secrets_providers/providers/__init__.py
@@ -3,10 +3,12 @@
from .aws import AWSSecretsManagerSecretsProvider, AWSSystemsManagerParameterStore
from .hashicorp import HashiCorpVaultSecretsProvider
from .delinea import ThycoticSecretServerSecretsProviderId, ThycoticSecretServerSecretsProviderPath
+from .cyberark import CyberARKSecretsProvider
__all__ = ( # type: ignore
AWSSecretsManagerSecretsProvider, # pylint: disable=invalid-all-object
AWSSystemsManagerParameterStore, # pylint: disable=invalid-all-object
+ CyberARKSecretsProvider, # pylint: disable=invalid-all-object
HashiCorpVaultSecretsProvider, # pylint: disable=invalid-all-object
ThycoticSecretServerSecretsProviderId, # pylint: disable=invalid-all-object
ThycoticSecretServerSecretsProviderPath, # pylint: disable=invalid-all-object
diff --git a/nautobot_secrets_providers/providers/cyberark.py b/nautobot_secrets_providers/providers/cyberark.py
new file mode 100644
index 0000000..6acf36b
--- /dev/null
+++ b/nautobot_secrets_providers/providers/cyberark.py
@@ -0,0 +1,104 @@
+import socket
+from email.policy import default
+
+from django import forms
+from django.conf import settings
+
+try:
+ import requests
+except ImportError:
+ requests = None
+
+from nautobot.extras.secrets import SecretsProvider, exceptions
+from nautobot.core.forms import BootstrapMixin
+
+__all__ = ("CyberARKSecretsProvider")
+
+class CyberARKSecretsProvider(SecretsProvider):
+ """
+ A secrets provider for CyberARK AIM.
+ """
+
+ slug = "cyberark"
+ name = "CyberARK"
+ is_available = requests is None
+
+ class ParametersForm(BootstrapMixin, forms.Form):
+ """
+ Required parameters for CyberARK.
+ """
+
+ object = forms.CharField(
+ required = False,
+ help_text = "Object to requestin CyberARK (not required if username is given)"
+ )
+ username = forms.CharField(
+ required = True if object.empty_value else False,
+ help_text = "Username to request in CyberARK (not required if objects is given)"
+ )
+ object.required = True if username.empty_value else False
+
+ @classmethod
+ def get_value_for_secret(cls, secret, obj=None, **kwargs):
+ """
+ Return the value stored under the
+
+ Args:
+ secret (_type_): _description_
+ obj (_type_, optional): _description_. Defaults to None.
+ """
+
+ plugin_settings = settings.PLUGINS_CONFIG['nautobot_secret_providers']
+ if 'cyberark' not in plugin_settings:
+ raise exceptions.SecretProviderError(secret, cls, "CyberARK is not configured")
+
+ cyberark_settings = plugin_settings["cyberark"]
+
+ if "url" not in cyberark_settings:
+ raise exceptions.SecretProviderError(secret, cls, "CyberARK url is not set in PLUGINS_CONFIG['nautobot_secret_providers']['cyberark']")
+
+ parameters = secret.rendered_parameters(obj=obj)
+ try:
+ secret_username = parameters["username"]
+ secret_object = parameters["object"]
+ except KeyError as err:
+ msg = f"The secret parameter could not be retrieved for field {err}"
+ raise exceptions.SecretParametersError(secret, cls, msg) from err
+
+ try:
+ if "token" in cyberark_settings:
+ token = cyberark_settings["token"]
+ else:
+ cyberark_settings["username"]
+ cyberark_settings["password"]
+ cyberark_settings["auth_method"]
+ except KeyError as err:
+ raise exceptions.SecretProviderError(secret, cls, f"Can't retrieve field {err} from PLUGINS_CONFIG['nautobot_secret_providers']['cyberark']")
+
+ if "token" not in cyberark_settings:
+ response = requests.post(
+ f"{cyberark_settings['url']}/PasswordVault/API/auth/{cyberark_settings['auth_method']}/Logon",
+ verify = cyberark_settings['verifySsl'], # If needs to disable cert check only for CyberARK
+ data = {
+ "username": cyberark_settings["username"],
+ "password": cyberark_settings["password"]
+ }
+ )
+ if response.status_code != 200:
+ raise exceptions.SecretProviderError(secret, cls, f"Can't get token: {response.status_code} {response.text}")
+
+ token = response.text.replace('"', '')
+
+ response = requests.get(
+ f"{cyberark_settings['url']}/PasswordVault/api/Accounts/?search={secret_username}", # This call can be very long to answer depending of the CyberARK instance setup
+ verify = cyberark_settings['verifySsl'],
+ headers = {"Authorization": token},
+ )
+
+ if response.status_code != 200:
+ raise exceptions.SecretProviderError(secret, cls, f"Can't get value: {response.status_code} {response.text}")
+ return requests.post(
+ f"{cyberark_settings['url']}/PasswordVault/api/Accounts/{response.json()['value'][0]['id']}/Password/Retrieve",
+ verify = cyberark_settings["verifySsl"],
+ headers = { 'Authorization': token },
+ ).text.replace('"', '')
\ No newline at end of file
diff --git a/nautobot_secrets_providers/templates/nautobot_secrets_providers/home.html b/nautobot_secrets_providers/templates/nautobot_secrets_providers/home.html
index 10eafd3..31af8a2 100644
--- a/nautobot_secrets_providers/templates/nautobot_secrets_providers/home.html
+++ b/nautobot_secrets_providers/templates/nautobot_secrets_providers/home.html
@@ -46,6 +46,11 @@