diff --git a/README.md b/README.md index bc47eae..e4cba7e 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This app supports the following popular secrets backends: | Secrets Backend | Supported Secret Types | Supported Authentication Methods | | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [1Password](https://1password.com) | [Hosted Password Management](https://1password.com/password-management) | [Service Account Token](https://developer.1password.com/docs/service-accounts/) | | [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) | [Other: Key/value pairs](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html) | [AWS credentials](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html) (see Usage section below) | | [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) | | [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/) | [Key Vault Secrets](https://learn.microsoft.com/en-us/azure/key-vault/secrets/about-secrets) | [Entra ID Service Principal](https://learn.microsoft.com/en-us/python/api/azure-identity/azure.identity.environmentcredential?view=azure-python) | diff --git a/changes/88.added b/changes/88.added new file mode 100644 index 0000000..46e2201 --- /dev/null +++ b/changes/88.added @@ -0,0 +1 @@ +Added 1Password as a Secrets Provider. diff --git a/development/nautobot_config.py b/development/nautobot_config.py index 264c307..daac0bb 100644 --- a/development/nautobot_config.py +++ b/development/nautobot_config.py @@ -163,5 +163,11 @@ }, } }, + "one_password": { + "vaults": {}, + "token": os.getenv( + "OP_SERVICE_ACCOUNT_TOKEN", + ), + }, }, } diff --git a/docs/admin/install.md b/docs/admin/install.md index 1a09788..2d4b7be 100644 --- a/docs/admin/install.md +++ b/docs/admin/install.md @@ -52,6 +52,17 @@ The HashiCorp Vault provider requires the `hvac` library. This can easily be ins pip install nautobot-secrets-providers[hashicorp] ``` +#### 1Password Vault + +The 1Password Vault provider requires the `onepassword-sdk` library. This can easily be installed along with the app using the following command. + +```no-highlight +pip install nautobot-secrets-providers[onepassword] +``` + +!!! note + The 1Password Vault requires a minimum version of Python 3.9. + ### Access Requirements There are no special access requirements to install the app. diff --git a/docs/admin/providers/index.md b/docs/admin/providers/index.md index 0dc5440..8ddd825 100644 --- a/docs/admin/providers/index.md +++ b/docs/admin/providers/index.md @@ -6,3 +6,4 @@ This Nautobot app supports the following secrets providers: - [Azure](./azure_setup.md) - [Delinea/Thycotic](./delinea_setup.md) - [HashiCorp](./hashicorp_setup.md) +- [1Password](onepassword_setup.md) diff --git a/docs/admin/providers/onepassword_setup.md b/docs/admin/providers/onepassword_setup.md new file mode 100644 index 0000000..54362f1 --- /dev/null +++ b/docs/admin/providers/onepassword_setup.md @@ -0,0 +1,34 @@ +# 1Password Vault + +Requires a minimum of Python3.9 + +## Prerequisites + +You must create a Service Account for the 1Password vault/vaults you are trying to access. You can follow the [Getting Started with Service Accounts](https://developer.1password.com/docs/service-accounts/get-started/) to assist with creating the Service Account. + +!!! note + The Service Account token needs to have access to the Vault that it is configured for. Per 1Password policy "You can't grant a service account access to your built-in Personal, Private, or Employee vault, or your default Shared vault." + +## Configuration + +You must provide a mapping in `PLUGINS_CONFIG` within your `nautobot_config.py`, for example: + +```python +PLUGINS_CONFIG = { + "nautobot_secrets_providers": { + "one_password": { + "token": os.environ.get("OP_SERVICE_ACCOUNT_TOKEN"), + "vaults": { + "MyVault": { + "token": os.environ.get("OP_SERVICE_ACCOUNT_TOKEN"), + }, + }, + }, + }, +} +``` + +- `token` - (required) The 1Password Service Account Token to be used globally when it is not specified by a vault. +- `vaults` (required) Each 1Password Vault that is supported by this app will be listed inside this dictionary. + - `` (required) The name of the vault needs to be placed as a key inside the `vaults` dictionary. + - `token` (optional) The 1Password Service Account Token to be used by the above vault, if overriding the global `token`. diff --git a/docs/images/dark/1password-vault-secrets-provider-add.png b/docs/images/dark/1password-vault-secrets-provider-add.png new file mode 100644 index 0000000..e9d700c Binary files /dev/null and b/docs/images/dark/1password-vault-secrets-provider-add.png differ diff --git a/docs/images/dark/secrets-providers-home.png b/docs/images/dark/secrets-providers-home.png new file mode 100644 index 0000000..b6eac8f Binary files /dev/null and b/docs/images/dark/secrets-providers-home.png differ diff --git a/docs/images/light/1password-vault-secrets-provider-add.png b/docs/images/light/1password-vault-secrets-provider-add.png new file mode 100644 index 0000000..5d385c3 Binary files /dev/null and b/docs/images/light/1password-vault-secrets-provider-add.png differ diff --git a/docs/images/light/secrets-providers-home.png b/docs/images/light/secrets-providers-home.png new file mode 100644 index 0000000..05506a8 Binary files /dev/null and b/docs/images/light/secrets-providers-home.png differ diff --git a/docs/user/app_use_cases.md b/docs/user/app_use_cases.md index e24e158..3a54263 100644 --- a/docs/user/app_use_cases.md +++ b/docs/user/app_use_cases.md @@ -12,7 +12,8 @@ This document describes common use-cases and scenarios for this App. --- -![Screenshot of plugin home page](../images/secrets-providers-home.png "App Home page") +![Screenshot of plugin home page](../images/light/secrets-providers-home.png#only-light "App Home page") +![Screenshot of plugin home page](../images/dark/secrets-providers-home.png#only-dark "App Home page") --- @@ -32,4 +33,9 @@ This document describes common use-cases and scenarios for this App. --- -![Screenshot of secret using Azure Key Vault](../images/azure-key-vault-secrets-provider-add.png "Secret using Azure Key Vault") \ No newline at end of file +![Screenshot of secret using Azure Key Vault](../images/azure-key-vault-secrets-provider-add.png "Secret using Azure Key Vault") + +--- + +![Screenshot of secret using 1Password](../images/light/1password-vault-secrets-provider-add.png#only-light "Secret using 1Password") +![Screenshot of secret using 1Password](../images/dark/1password-vault-secrets-provider-add.png#only-dark "Secret using 1Password") diff --git a/mkdocs.yml b/mkdocs.yml index 5fa3ecf..12d416b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -112,6 +112,7 @@ nav: - Install and Configure: "admin/install.md" - Provider Setup: - "admin/providers/index.md" + - 1Password: "admin/providers/onepassword_setup.md" - AWS: "admin/providers/aws_setup.md" - Azure: "admin/providers/azure_setup.md" - Delinea/Thycotic: "admin/providers/delinea_setup.md" diff --git a/nautobot_secrets_providers/providers/__init__.py b/nautobot_secrets_providers/providers/__init__.py index 973b3a8..70c826d 100644 --- a/nautobot_secrets_providers/providers/__init__.py +++ b/nautobot_secrets_providers/providers/__init__.py @@ -4,6 +4,7 @@ from .azure import AzureKeyVaultSecretsProvider from .delinea import DelineaSecretServerSecretsProviderId, DelineaSecretServerSecretsProviderPath from .hashicorp import HashiCorpVaultSecretsProvider +from .one_password import OnePasswordSecretsProvider __all__ = ( "AWSSecretsManagerSecretsProvider", @@ -12,4 +13,5 @@ "DelineaSecretServerSecretsProviderId", "DelineaSecretServerSecretsProviderPath", "HashiCorpVaultSecretsProvider", + "OnePasswordSecretsProvider", ) diff --git a/nautobot_secrets_providers/providers/one_password.py b/nautobot_secrets_providers/providers/one_password.py new file mode 100644 index 0000000..d9c835f --- /dev/null +++ b/nautobot_secrets_providers/providers/one_password.py @@ -0,0 +1,109 @@ +"""1Password Secrets Provider for Nautobot.""" + +from asgiref.sync import async_to_sync +from django import forms +from django.conf import settings +from nautobot.core.forms import BootstrapMixin +from nautobot.extras.secrets import SecretsProvider, exceptions + +try: + from onepassword.client import Client +except ImportError: + Client = None + +from nautobot_secrets_providers import __version__ + +__all__ = ("OnePasswordSecretsProvider",) + + +@async_to_sync +async def get_secret_from_vault(vault, item, field, token, section=None): + """Get a secret from a 1Password vault. + + Args: + vault (str): 1Password Vault where the secret is located. + item (str): 1Password Item where the secret is located. + field (str): 1Password secret field name. + token (str): 1Password Service Account token. + section (str, optional): 1Password Item Section for the secret. Defaults to None. + + Returns: + (str): Value from the secret. + """ + client = await Client.authenticate( + auth=token, integration_name="nautobot-secrets-providers", integration_version=__version__ + ) + reference = f"op://{vault}/{item}/{f'{section}/' if section else ''}{field}" + return await client.secrets.resolve(reference) + + +def vault_choices(): + """Generate Choices for vault form field. + + Build a form option for each key in vaults. + """ + plugin_settings = settings.PLUGINS_CONFIG["nautobot_secrets_providers"] + return [(key, key) for key in plugin_settings["one_password"]["vaults"].keys()] + + +class OnePasswordSecretsProvider(SecretsProvider): + """A secrets provider for 1Password.""" + + slug = "one-password" + name = "1Password Vault" + is_available = Client is not None + + # TBD: Remove after pylint-nautobot bump + # pylint: disable-next=nb-incorrect-base-class + class ParametersForm(BootstrapMixin, forms.Form): + """Required parameters for HashiCorp Vault.""" + + vault = forms.ChoiceField( + required=True, + choices=vault_choices, + help_text="1Password Vault to retrieve the secret from.", + ) + item = forms.CharField( + required=True, + help_text="The item in 1Password.", + ) + section = forms.CharField( + required=False, + help_text="The section where the field is a part of.", + ) + field = forms.CharField( + required=True, + help_text="The field where the secret is located. Defaults to 'password'.", + initial="password", + ) + + @classmethod + def get_token(cls, secret, vault): + """Get the token for a vault.""" + plugin_settings = settings.PLUGINS_CONFIG["nautobot_secrets_providers"] + if "token" in plugin_settings["one_password"]["vaults"][vault]: + return plugin_settings["one_password"]["vaults"][vault]["token"] + try: + return plugin_settings["one_password"]["token"] + except KeyError as exc: + raise exceptions.SecretProviderError(secret, cls, "1Password token is not configured!") from exc + + @classmethod + def get_value_for_secret(cls, secret, obj=None, **kwargs): # pylint: disable=too-many-locals + """Get the value for a secret from 1Password.""" + # This is only required for 1Password therefore not defined in + # `required_settings` for the app config. + plugin_settings = settings.PLUGINS_CONFIG["nautobot_secrets_providers"] + if "one_password" not in plugin_settings: + raise exceptions.SecretProviderError(secret, cls, "1Password is not configured!") + + parameters = secret.rendered_parameters(obj=obj) + vault = parameters["vault"] + + return get_secret_from_vault( + vault=vault, + item=parameters["item"], + field=parameters["field"], + token=cls.get_token(secret, vault=vault), + section=parameters.get("section", None), + ) diff --git a/nautobot_secrets_providers/templates/nautobot_secrets_providers/home.html b/nautobot_secrets_providers/templates/nautobot_secrets_providers/home.html index 8020656..6beb6ed 100644 --- a/nautobot_secrets_providers/templates/nautobot_secrets_providers/home.html +++ b/nautobot_secrets_providers/templates/nautobot_secrets_providers/home.html @@ -21,6 +21,11 @@

{% block title %}Secrets Providers Home{% endblock %}

+ + 1Password + Hosted Password Management + Service Account Token + AWS Secrets Manager Other: Key/value pairs diff --git a/nautobot_secrets_providers/tests/test_providers.py b/nautobot_secrets_providers/tests/test_providers.py index fb380db..c12329b 100644 --- a/nautobot_secrets_providers/tests/test_providers.py +++ b/nautobot_secrets_providers/tests/test_providers.py @@ -17,9 +17,11 @@ AWSSecretsManagerSecretsProvider, AWSSystemsManagerParameterStore, HashiCorpVaultSecretsProvider, + OnePasswordSecretsProvider, ) from nautobot_secrets_providers.providers.choices import HashicorpKVVersionChoices from nautobot_secrets_providers.providers.hashicorp import vault_choices +from nautobot_secrets_providers.providers.one_password import vault_choices as one_password_vault_choices # Use the proper swappable User model User = get_user_model() @@ -709,3 +711,110 @@ def test_retrieve_invalid_version(self): self.provider.get_value_for_secret(self.secret) exc = err.exception self.assertIn("ParameterVersionNotFound", exc.message) + + +class OnePasswordSecretsProviderTestCase(SecretsProviderTestCase): + """Tests for OnePasswordSecretsProvider.""" + + provider = OnePasswordSecretsProvider + + def setUp(self): + super().setUp() + + # The secret we be using. + self.secret = Secret.objects.create( + name="hello-onepassword", + provider=self.provider.slug, + parameters={ + "vault": "example", + "item": "location", + "section": "section", + "field": "value", + }, + ) + self.secret2 = Secret.objects.create( + name="hello-onepassword-2", + provider=self.provider.slug, + parameters={ + "vault": "example_2", + "item": "location", + "field": "value", + }, + ) + + self.plugin_config = { + "nautobot_secrets_providers": { + "one_password": { + "vaults": { + "example": {"token": "nautobot"}, + "example_2": {}, + }, + "token": "another", + } + } + } + + @patch("nautobot_secrets_providers.providers.one_password.get_secret_from_vault", return_value="world") + def test_retrieve_success(self, get_secret_from_vault): + """Retrieve a secret successfully.""" + with get_secret_from_vault: + with self.settings(PLUGINS_CONFIG=self.plugin_config): + response = self.provider.get_value_for_secret(self.secret) + self.assertEqual("world", response) + response2 = self.provider.get_value_for_secret(self.secret2) + self.assertEqual("world", response2) + + def test_multiple_valid_settings(self): + # Test with a configuration passed in + multiple_plugins_config = { + "nautobot_secrets_providers": { + "one_password": { + "vaults": { + "example": {"token": "nautobot"}, + "example_2": {}, + }, + "token": "another_token", + } + } + } + + invalid_plugins_config = { + "nautobot_secrets_providers": { + "one_password": { + "vaults": { + "example": {}, + }, + } + } + } + + with self.settings(PLUGINS_CONFIG=multiple_plugins_config): + token = self.provider.get_token(self.secret, "example") + self.assertEqual( + token, + settings.PLUGINS_CONFIG["nautobot_secrets_providers"]["one_password"]["vaults"]["example"]["token"], + ) + token = self.provider.get_token(self.secret, "example_2") + self.assertEqual( + token, + settings.PLUGINS_CONFIG["nautobot_secrets_providers"]["one_password"]["token"], + ) + + with self.settings(PLUGINS_CONFIG=invalid_plugins_config): + with self.assertRaises(exceptions.SecretProviderError): + self.provider.get_token(self.secret, "example") + + def test_vault_choices(self): + multiple_plugins_config = { + "nautobot_secrets_providers": { + "one_password": { + "vaults": { + "Example": {"token": "nautobot"}, + "Example 2": {"token": "nautobot"}, + } + } + } + } + with self.settings(PLUGINS_CONFIG=multiple_plugins_config): + choices = one_password_vault_choices() + self.assertEqual(choices, [("Example", "Example"), ("Example 2", "Example 2")]) diff --git a/poetry.lock b/poetry.lock index e3005bb..6378ad1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -25,6 +25,17 @@ files = [ {file = "aniso8601-7.0.0.tar.gz", hash = "sha256:513d2b6637b7853806ae79ffaca6f3e8754bdd547048f5ccc1420aec4b714f1e"}, ] +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = true +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + [[package]] name = "appnope" version = "0.1.4" @@ -2322,6 +2333,39 @@ rsa = ["cryptography (>=3.0.0)"] signals = ["blinker (>=1.4.0)"] signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] +[[package]] +name = "onepassword-sdk" +version = "0.1.2" +description = "The 1Password Python SDK offers programmatic read access to your secrets in 1Password in an interface native to Python." +optional = true +python-versions = ">=3.9" +files = [ + {file = "onepassword_sdk-0.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ecf5cf2dd51d409171e6fac45cc782dcbb2d87fcc030268a65348224bad23e8"}, + {file = "onepassword_sdk-0.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:714c9f9ec17612e1ed96789145e603335c838cfb29cf78924400a47034bf3688"}, + {file = "onepassword_sdk-0.1.2-cp310-cp310-manylinux_2_32_aarch64.whl", hash = "sha256:bcf824a3c874833e9534a9c633c1b2b68e5ce04121e82cbb9808328459ecc3b3"}, + {file = "onepassword_sdk-0.1.2-cp310-cp310-manylinux_2_32_x86_64.whl", hash = "sha256:b1e50d86fc033730f551217d2cb34cab1e486ff4d2bc18918cf36b71955d935c"}, + {file = "onepassword_sdk-0.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:567828bae40ca48bf493118efec990efd898e10ac3ef7a9cbdc3e2f786241180"}, + {file = "onepassword_sdk-0.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:47e7a6534042a69067ff117998693956bc1429d58cfeac3eb1caa5afb99d7a35"}, + {file = "onepassword_sdk-0.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72beaa348d8e3e11adc63765e93569b2ca0201a61034889bb0344f1071a91088"}, + {file = "onepassword_sdk-0.1.2-cp311-cp311-manylinux_2_32_aarch64.whl", hash = "sha256:27516b172bbc0c9156ff1ee3d551703f12c3ae135786e5936acf670d5892cab0"}, + {file = "onepassword_sdk-0.1.2-cp311-cp311-manylinux_2_32_x86_64.whl", hash = "sha256:809f4d41c1779e980412a4c016917d5fde0a6be7ea54743ef38c13b3fddd577b"}, + {file = "onepassword_sdk-0.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:55550e1f9756ba6fbe7dab548e1958ba31b1e4ece228e24b407dbd2013b04248"}, + {file = "onepassword_sdk-0.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6da00e28c74a268597d104c3b77fa6f75dc7e75300273d7d245bd07c037d1dd2"}, + {file = "onepassword_sdk-0.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d28ed9f1f27cb32d418e36ec26b59c4436b2d6627d8ed41a8a84f61c73f28271"}, + {file = "onepassword_sdk-0.1.2-cp312-cp312-manylinux_2_32_aarch64.whl", hash = "sha256:a03517073b581d7a6525976b2ee79b3a16adc5cd39640ae7e596c33d28d23853"}, + {file = "onepassword_sdk-0.1.2-cp312-cp312-manylinux_2_32_x86_64.whl", hash = "sha256:663cc389c34eb88bd6f6d74fff5aa6ea7dd96365998c8a3c86ea3528675cd919"}, + {file = "onepassword_sdk-0.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:33fa9d53f93fdaf5593aeb45a270023f49df42b4790aa498d521e47c4c1c0a80"}, + {file = "onepassword_sdk-0.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4720067bb35a15e1bee6f539295290cd5cf6b44c5704aeec62a254c23c164d19"}, + {file = "onepassword_sdk-0.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcea1bcff89bb4e9d14e87d22e1069f8dfd457790b418f60b146dabcbb34a1f8"}, + {file = "onepassword_sdk-0.1.2-cp39-cp39-manylinux_2_32_aarch64.whl", hash = "sha256:01f73d0a781826c85610fbc5e72f67e650cd252c9416863f30b6da649ec0c38a"}, + {file = "onepassword_sdk-0.1.2-cp39-cp39-manylinux_2_32_x86_64.whl", hash = "sha256:d0391a7d00b426cb0147264fa421436cfe9465b4cbe08468f6e8b23f735db1f9"}, + {file = "onepassword_sdk-0.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:505d5a4e16e59da19578014dbc598d7ecda4fe50f2b61ef45f20719bd0a0ccc0"}, + {file = "onepassword_sdk-0.1.2.tar.gz", hash = "sha256:029ba2e03accc9ffe762414721bc29fd153103afa972d361c1a67d75678d9a5f"}, +] + +[package.dependencies] +pydantic = ">=2.5" + [[package]] name = "packaging" version = "24.1" @@ -2703,6 +2747,127 @@ files = [ {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] +[[package]] +name = "pydantic" +version = "2.9.2" +description = "Data validation using Python type hints" +optional = true +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.23.4" +typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.23.4" +description = "Core functionality for Pydantic validation and serialization" +optional = true +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + [[package]] name = "pygments" version = "2.18.0" @@ -4055,14 +4220,15 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", type = ["pytest-mypy"] [extras] -all = ["azure-identity", "azure-keyvault-secrets", "boto3", "hvac", "python-tss-sdk"] +all = ["azure-identity", "azure-keyvault-secrets", "boto3", "hvac", "onepassword-sdk", "python-tss-sdk"] aws = ["boto3"] azure = ["azure-identity", "azure-keyvault-secrets"] hashicorp = ["hvac"] nautobot = ["nautobot"] +onepassword = ["onepassword-sdk"] thycotic = ["python-tss-sdk"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.13" -content-hash = "bed3d6669dec6ef6e4e89651d8f3033590477d99ba0f3e6fee62602b9c76c651" +content-hash = "04bed3774f58ea3b31f28b315231a276230bbc555daf6f64cb05f7c5b246099c" diff --git a/pyproject.toml b/pyproject.toml index a0722f0..52d5f11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ azure-keyvault-secrets = { version = "^4.8.0", optional = true } boto3 = { version = "^1.19.5", optional = true } hvac = { version = ">=0.11.0, <1.1.0", optional = true } python-tss-sdk = {version = "~1.2.0", optional = true} +onepassword-sdk = {version = "^0.1.2", python = ">=3.9", optional = true} [tool.poetry.group.dev.dependencies] coverage = "*" @@ -72,6 +73,7 @@ all = [ "azure-keyvault-secrets", "boto3", "hvac", + "onepassword-sdk", "python-tss-sdk", ] aws = ["boto3"] @@ -79,6 +81,7 @@ azure = ["azure-identity", "azure-keyvault-secrets"] hashicorp = ["hvac"] nautobot = ["nautobot"] thycotic = ["python-tss-sdk"] +onepassword = ["onepassword-sdk"] [tool.pylint.master] # Include the pylint_django plugin to avoid spurious warnings about Django patterns