Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F11785402
D3697.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
26 KB
Referenced Files
None
Subscribers
None
D3697.diff
View Options
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -22,6 +22,10 @@
allows run the MariaDB or MySQL query and format
the result as expected, like as a MediaWiki table
+* **[secretsmith](tools/secretsmith/README.md)**:
+ wrapper around the hvac library to get secrets from Vault or OpenBao,
+ allow to authenticate with token or AppRole
+
### Contribute
This repository is intended to behave as a monorepo for reporting.
diff --git a/tools/nasqueron-reports/conf/reports.yaml b/tools/nasqueron-reports/conf/reports.yaml
--- a/tools/nasqueron-reports/conf/reports.yaml
+++ b/tools/nasqueron-reports/conf/reports.yaml
@@ -46,3 +46,12 @@
date: Date
userName: Author
repository: Repository
+
+# -------------------------------------------------------------
+# Secretsmith configuration
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+vault:
+ server:
+ url: https://172.27.27.7:8200
+ verify: /usr/local/share/certs/nasqueron-vault-ca.crt
diff --git a/tools/nasqueron-reports/requirements.txt b/tools/nasqueron-reports/requirements.txt
--- a/tools/nasqueron-reports/requirements.txt
+++ b/tools/nasqueron-reports/requirements.txt
@@ -1,4 +1,4 @@
-hvac~=2.3.0
mysql-connector-python~=9.4.0
PyYAML~=6.0.2
+secretsmith~=0.1.0
sqlparse~=0.5.3
diff --git a/tools/nasqueron-reports/setup.cfg b/tools/nasqueron-reports/setup.cfg
--- a/tools/nasqueron-reports/setup.cfg
+++ b/tools/nasqueron-reports/setup.cfg
@@ -27,8 +27,8 @@
python_requires = >=3.6
install_requires =
PyYAML>=6.0,<7.0
- hvac>=2.3,<3.0
mysql-connector-python>=9.4,<10.0
+ secretsmith>=0.1.0,<1.0
sqlparse>=0.5,<0.6
[options.packages.find]
diff --git a/tools/nasqueron-reports/src/nasqueron_reports/config.py b/tools/nasqueron-reports/src/nasqueron_reports/config.py
--- a/tools/nasqueron-reports/src/nasqueron_reports/config.py
+++ b/tools/nasqueron-reports/src/nasqueron_reports/config.py
@@ -101,7 +101,7 @@
report_config["service_options"]["credentials"] = credentials
-def parse_report_config(report_name):
+def parse_report_config(report_name, extra_config=None):
config = get_config()
try:
@@ -109,6 +109,9 @@
except KeyError:
raise NasqueronReportConfigError(f"Report not found: {report_name}")
+ if extra_config:
+ config.update(extra_config)
+
inject_service_config(config, report_config)
return report_config
diff --git a/tools/nasqueron-reports/src/nasqueron_reports/credentials/credentials.py b/tools/nasqueron-reports/src/nasqueron_reports/credentials/credentials.py
--- a/tools/nasqueron-reports/src/nasqueron_reports/credentials/credentials.py
+++ b/tools/nasqueron-reports/src/nasqueron_reports/credentials/credentials.py
@@ -18,12 +18,13 @@
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-def resolve_credentials(config):
- if config["driver"] == "vault":
- return vault.fetch_credentials(config["secret"])
+def resolve_credentials(full_config, credentials_config):
+ if credentials_config["driver"] == "vault":
+ vault_config = full_config.get("vault", {})
+ return vault.fetch_credentials(vault_config, credentials_config["secret"])
- if config["driver"] == "env":
- variables = config.get("variables", {})
+ if credentials_config["driver"] == "env":
+ variables = credentials_config.get("variables", {})
return read_environment(variables)
raise NasqueronReportConfigError("Credentials driver parameter is missing")
diff --git a/tools/nasqueron-reports/src/nasqueron_reports/credentials/vault.py b/tools/nasqueron-reports/src/nasqueron_reports/credentials/vault.py
--- a/tools/nasqueron-reports/src/nasqueron_reports/credentials/vault.py
+++ b/tools/nasqueron-reports/src/nasqueron_reports/credentials/vault.py
@@ -7,25 +7,13 @@
# -------------------------------------------------------------
-import hvac
+from secretsmith.vault.client import from_config as client_from_config
+from secretsmith.vault.secrets import read_secret
+from secretsmith.vault.utils import split_path
-VAULT_CA_CERTIFICATE = "/usr/local/share/certs/nasqueron-vault-ca.crt"
+def fetch_credentials(vault_config, full_secret_path):
+ vault_client = client_from_config(vault_config)
-
-def fetch_credentials(secret_path):
- vault_client = hvac.Client(
- verify=VAULT_CA_CERTIFICATE,
- )
-
- tokens = secret_path.split("/")
- secret_mount = tokens[0]
- secret_path = "/".join(tokens[1:])
-
- secret = vault_client.secrets.kv.read_secret_version(
- mount_point=secret_mount,
- path=secret_path,
- raise_on_deleted_version=True,
- )
-
- return secret["data"]["data"]
+ mount_point, secret_path = split_path(full_secret_path)
+ return read_secret(vault_client, mount_point, secret_path)
diff --git a/tools/rhyne-wyse/conf/rhyne-wyse.yaml b/tools/rhyne-wyse/conf/rhyne-wyse.yaml
--- a/tools/rhyne-wyse/conf/rhyne-wyse.yaml
+++ b/tools/rhyne-wyse/conf/rhyne-wyse.yaml
@@ -1,7 +1,10 @@
wiki:
credentials:
driver: vault
- secret: agora
+ vault_credentials: /usr/local/etc/secrets/rhyne-wyse.yaml
+
+ mount_point: apps
+ secret_path: rhyne-wyse/agora
reports:
- report: devcentral-token-language-models
diff --git a/tools/rhyne-wyse/requirements.txt b/tools/rhyne-wyse/requirements.txt
--- a/tools/rhyne-wyse/requirements.txt
+++ b/tools/rhyne-wyse/requirements.txt
@@ -1,4 +1,4 @@
-hvac~=2.3.0
nasqueron-reports~=0.1.0
pywikibot~=10.4.0
PyYAML~=6.0.2
+secretsmith~=0.1.0
diff --git a/tools/rhyne-wyse/setup.cfg b/tools/rhyne-wyse/setup.cfg
--- a/tools/rhyne-wyse/setup.cfg
+++ b/tools/rhyne-wyse/setup.cfg
@@ -26,9 +26,9 @@
python_requires = >=3.6
install_requires =
PyYAML>=6.0,<7.0
- hvac>=2.3,<3.0
nasqueron-reports>=0.1.0,<1.0
pywikibot>=10.4.0,<11.0
+ secretsmith>=0.1.0,<1.0
[options.packages.find]
where = src
diff --git a/tools/rhyne-wyse/src/rhyne_wyse/client.py b/tools/rhyne-wyse/src/rhyne_wyse/client.py
--- a/tools/rhyne-wyse/src/rhyne_wyse/client.py
+++ b/tools/rhyne-wyse/src/rhyne_wyse/client.py
@@ -27,8 +27,7 @@
raise ValueError("Missing config key: wiki.credentials.driver")
if driver == "vault":
- client = vault.connect_to_vault()
- return vault.read_app_secret(client, credentials_config["secret"])
+ return vault.read_app_secret(credentials_config)
raise ValueError(f"Unknown credentials driver: {driver}")
diff --git a/tools/rhyne-wyse/src/rhyne_wyse/credentials/vault.py b/tools/rhyne-wyse/src/rhyne_wyse/credentials/vault.py
--- a/tools/rhyne-wyse/src/rhyne_wyse/credentials/vault.py
+++ b/tools/rhyne-wyse/src/rhyne_wyse/credentials/vault.py
@@ -11,27 +11,17 @@
from typing import Dict
-import hvac
+import secretsmith
+from secretsmith.vault.secrets import read_secret
-VAULT_CA_CERTIFICATE = "/usr/local/share/certs/nasqueron-vault-ca.crt"
+def read_app_secret(config: Dict[str, str]) -> Dict[str, str]:
+ config_path = config.get("vault_credentials", None)
+ try:
+ vault_client = secretsmith.login(config_path)
+ except PermissionError:
+ # Allow running the bot under a user account too
+ vault_client = secretsmith.login()
-def connect_to_vault():
- return hvac.Client(
- verify=VAULT_CA_CERTIFICATE,
- )
-
-
-def read_secret(
- vault_client, mount_point: str, prefix: str, key: str
-) -> Dict[str, str]:
- secret = vault_client.secrets.kv.read_secret_version(
- mount_point=mount_point,
- path=prefix + "/" + key,
- )
- return secret["data"]["data"]
-
-
-def read_app_secret(vault_client, key: str) -> Dict[str, str]:
- return read_secret(vault_client, "apps", "rhyne-wyse", key)
+ return read_secret(vault_client, config["mount_point"], config["secret_path"])
diff --git a/tools/rhyne-wyse/src/rhyne_wyse/tasks/reports.py b/tools/rhyne-wyse/src/rhyne_wyse/tasks/reports.py
--- a/tools/rhyne-wyse/src/rhyne_wyse/tasks/reports.py
+++ b/tools/rhyne-wyse/src/rhyne_wyse/tasks/reports.py
@@ -9,6 +9,7 @@
from typing import Dict
import requests
+import yaml
from nasqueron_reports.actions.reports import generate_report
from nasqueron_reports.config import parse_report_config
@@ -24,8 +25,7 @@
def prepare_report(report_options: Dict) -> Report:
if report_options["tool"] == "nasqueron-reports":
- report_config = parse_report_config(report_options["report"])
- return generate_report(report_config)
+ return generate_nasqueron_report(report_options)
elif report_options["tool"] == "fetch":
return fetch_report(report_options)
@@ -57,6 +57,35 @@
return to_update
+# -------------------------------------------------------------
+# Call Nasqueron Reports to generate a report
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def parse_nasqueron_report_config(report_options):
+ tool_options = report_options.get("tool_options", {})
+ vault_credentials = tool_options.get("vault_credentials", None)
+
+ if vault_credentials is not None:
+ try:
+ with open(vault_credentials) as fd:
+ return {
+ "vault": yaml.safe_load(fd),
+ }
+ except PermissionError:
+ # Allow running the bot under a user account too
+ pass
+
+ return {}
+
+
+def generate_nasqueron_report(report_options):
+ extra_config = parse_nasqueron_report_config(report_options)
+ report_config = parse_report_config(report_options["report"], extra_config)
+
+ return generate_report(report_config)
+
+
# -------------------------------------------------------------
# Fetch an already generated report from a specific URL
#
diff --git a/tools/secretsmith/README.md b/tools/secretsmith/README.md
new file mode 100644
--- /dev/null
+++ b/tools/secretsmith/README.md
@@ -0,0 +1,120 @@
+## secretsmith
+
+The secretsmith Python package allows **connecting to Vault or OpenBao**,
+with support for several authentication methods, including using a token
+or AppRole.
+
+It also provides a simple wrapper to **query secrets from a kv2 store**.
+
+This is a high-level wrapper around [hvac](https://python-hvac.org/).
+
+At Nasqueron, we use this package to avoid writing boilerplate code in each
+application that needs to interact with Vault or OpenBao to:
+ - read a configuration file to determine login parameters
+ - query a simple password from kv2 store from a path
+
+When more and more applications need to interact with Vault or OpenBao,
+and use the same authentication methods, the same patterns to query secrets,
+to maintain this wrapper high-level library becomes useful.
+
+### Login
+
+Secretsmith uses the `hvac` library to connect to Vault or OpenBao.
+
+If nothing is specified, it will try to connect to Vault using the environment
+variables `VAULT_ADDR` and `VAULT_TOKEN`, or reading a token file at the
+default path. Especially convenient during the development workflow.
+
+When it's ready to be deployed, write a configuration file explaining how to
+connect to Vault or OpenBao.
+
+#### How to use in code?
+
+Call secretsmith.login() with the path to the configuration file:
+
+```python
+import secretsmith
+
+VAULT_CONFIG_PATH = '/path/to/config.yaml'
+
+vault_client = secretsmith.login(config_path=VAULT_CONFIG_PATH)
+```
+
+Then, you can use the client as a hvac library Vault client.
+
+We provide helper methods for common tasks, but you can also directly use hvac.
+
+#### Configuration file
+
+Secretsmith uses a YAML configuration file to determine the login parameters:
+
+```
+vault:
+ server:
+ url: https://127.0.0.1:8200
+ auth:
+ token: hvs.000000000000000000000000
+```
+
+When using AppRole, the configuration file will look like:
+
+```
+vault:
+ server:
+ url: https://127.0.0.1:8200
+ verify: /path/to/ca.pem
+ auth:
+ method: approle
+ role_id: e5a7b66e-5d08-da9c-7075-71984634b882
+ secret_id: 841771dc-11c9-bbc7-bcac-6a3945a69cd9
+```
+
+The format is based on the Vault execution module for SaltStack.
+
+The following parameters are supported:
+ - `server` - a block to specify the Vault or OpenBao server parameters
+ - `url` - the URL
+ - `verify` - the path to a CA certificate to verify the server's certificate
+ - `namespace` - the namespace to use (by default, will follow environment)
+ - `auth` - a block to specify the authentication method and parameters
+ - `method` - what authentication backend to use, by default 'token'
+
+Additional parameters are supported in the `auth` block depending
+on the authentication method.
+
+When the method is `token`:
+ - `token` - the token to use
+ - `token_file` - alternatively, the path to a file containing the token
+
+When the method is `approle`:
+ - `role_id` - the AppRole role ID (required)
+ - `secret_id` - the AppRole secret ID (optional)
+
+### Querying secrets
+
+For kv2, we also provide helper methods for more common use cases.
+
+If you store a password in the password field of the 'secret/app/db' path:
+
+```python
+import secretsmith
+from secretsmith.vault import secrets
+
+vault_client = secretsmith.login()
+password = secrets.get_password(vault_client, "secret", "app/db")
+```
+
+To get the full k/v store at the 'secret/app/db' path:
+
+```python
+secret = secrets.read_secret(vault_client, "secret", "app/db")
+```
+
+If you also store custom metadata, you can use:
+
+```python
+secret, metadata = secrets.read_secret_with_custom_metadata(vault_client, "secret", "app/db")
+```
+
+In all those examples, you need to replace "secret" by your kv2 mount point.
+The "secret" mount point is the default one if you didn't configure Vault.
diff --git a/tools/secretsmith/pyproject.toml b/tools/secretsmith/pyproject.toml
new file mode 100644
--- /dev/null
+++ b/tools/secretsmith/pyproject.toml
@@ -0,0 +1,14 @@
+# -------------------------------------------------------------
+# secretsmith
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+[build-system]
+requires = [
+ "setuptools>=42",
+ "wheel"
+]
+
+build-backend = "setuptools.build_meta"
diff --git a/tools/rhyne-wyse/setup.cfg b/tools/secretsmith/setup.cfg
copy from tools/rhyne-wyse/setup.cfg
copy to tools/secretsmith/setup.cfg
--- a/tools/rhyne-wyse/setup.cfg
+++ b/tools/secretsmith/setup.cfg
@@ -1,34 +1,31 @@
[metadata]
-name = rhyne-wyse
+name = secretsmith
version = 0.1.0
author = Sébastien Santoro
author_email = dereckson@espace-win.org
-description = Automated agent to publish reports
+description = Connect to Vault with a configuration file
long_description = file: README.md
long_description_content_type = text/markdown
license = BSD-2-Clause
url = https://devcentral.nasqueron.org/source/reports/
project_urls =
Bug Tracker = https://devcentral.nasqueron.org/
+ Documentation = https://docs.nasqueron.org/secretsmith/
+ Source Code = https://devcentral.nasqueron.org/source/reports/browse/main/tools/secretsmith/
classifiers =
Programming Language :: Python :: 3
Operating System :: OS Independent
- Environment :: Console
Intended Audience :: Developers
- Topic :: Software Development
+ Topic :: Security
[options]
package_dir =
= src
packages = find:
-scripts =
- bin/update-agora-reports
python_requires = >=3.6
install_requires =
PyYAML>=6.0,<7.0
hvac>=2.3,<3.0
- nasqueron-reports>=0.1.0,<1.0
- pywikibot>=10.4.0,<11.0
[options.packages.find]
where = src
diff --git a/tools/secretsmith/src/secretsmith/__init__.py b/tools/secretsmith/src/secretsmith/__init__.py
new file mode 100644
--- /dev/null
+++ b/tools/secretsmith/src/secretsmith/__init__.py
@@ -0,0 +1,9 @@
+# -------------------------------------------------------------
+# Secretsmith
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+
+from .vault.client import login
diff --git a/tools/secretsmith/src/secretsmith/vault/client.py b/tools/secretsmith/src/secretsmith/vault/client.py
new file mode 100644
--- /dev/null
+++ b/tools/secretsmith/src/secretsmith/vault/client.py
@@ -0,0 +1,81 @@
+# -------------------------------------------------------------
+# Secretsmith :: Vault :: client
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import os
+from typing import Dict
+
+from hvac import Client
+
+from secretsmith.vault.config import load_config
+
+
+# -------------------------------------------------------------
+# General login
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def login(config_path: str | None = None) -> Client:
+ if config_path is None:
+ config = {}
+ else:
+ config = load_config(config_path).get("vault", {})
+
+ return from_config(config)
+
+
+def from_config(config: Dict) -> Client:
+ config_server = config.get("server", {})
+ url = config_server.get("url", None)
+ verify = config_server.get("verify", None)
+ namespace = resolve_namespace(config_server)
+
+ config_auth = config.get("auth", {})
+ auth_method = config_auth.get("method", "token")
+ if auth_method == "token":
+ token = resolve_token(config_auth)
+ else:
+ token = None
+
+ client = Client(url=url, token=token, verify=verify, namespace=namespace)
+
+ if auth_method == "approle":
+ login_with_approle(client, config_auth)
+ elif auth_method != "token":
+ raise ValueError(f"Unknown auth method: {auth_method}")
+
+ return client
+
+
+def resolve_token(config_auth):
+ if "tokenfile" in config_auth:
+ with open(config_auth["tokenfile"]) as fd:
+ return fd.read().strip()
+
+ return config_auth.get("token", None)
+
+
+def resolve_namespace(config: dict) -> str | None:
+ try:
+ return config["namespace"]
+ except KeyError:
+ return os.environ.get("VAULT_NAMESPACE", None)
+
+
+# -------------------------------------------------------------
+# Additional authentication backends
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def login_with_approle(client: Client, config_auth: dict):
+ if "role_id" not in config_auth:
+ raise ValueError("Missing role_id in auth configuration")
+
+ client.auth.approle.login(
+ role_id=config_auth["role_id"],
+ secret_id=config_auth.get("secret_id", None),
+ )
diff --git a/tools/secretsmith/src/secretsmith/vault/config.py b/tools/secretsmith/src/secretsmith/vault/config.py
new file mode 100644
--- /dev/null
+++ b/tools/secretsmith/src/secretsmith/vault/config.py
@@ -0,0 +1,14 @@
+# -------------------------------------------------------------
+# Secretsmith :: Vault :: configuration
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import yaml
+
+
+def load_config(path: str) -> dict:
+ with open(path) as fd:
+ return yaml.safe_load(fd)
diff --git a/tools/secretsmith/src/secretsmith/vault/secrets.py b/tools/secretsmith/src/secretsmith/vault/secrets.py
new file mode 100644
--- /dev/null
+++ b/tools/secretsmith/src/secretsmith/vault/secrets.py
@@ -0,0 +1,79 @@
+# -------------------------------------------------------------
+# Secretsmith :: Vault :: KV secrets engine - version 2
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+from typing import Any, Dict, Tuple
+
+from hvac import Client
+
+
+# -------------------------------------------------------------
+# Fetch secret from kv engine
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def read_secret(client: Client, mount_point: str, secret_path: str) -> Dict[str, str]:
+ secret = client.secrets.kv.read_secret_version(
+ mount_point=mount_point,
+ path=secret_path,
+ raise_on_deleted_version=True,
+ )
+ return secret["data"]["data"]
+
+
+def read_secret_with_metadata(
+ client: Client, mount_point: str, secret_path: str
+) -> Tuple[dict[str, str], dict[str, Any]]:
+ """
+ Read a secret and return the data and the metadata dictionaries.
+ """
+ secret = client.secrets.kv.read_secret_version(
+ mount_point=mount_point,
+ path=secret_path,
+ raise_on_deleted_version=True,
+ )
+
+ return secret["data"]["data"], secret["data"]["metadata"]
+
+
+def read_secret_with_custom_metadata(
+ client: Client, mount_point: str, secret_path: str
+) -> Tuple[dict[str, str], dict[str, Any]]:
+ """
+ Read a secret and return the data and the metadata dictionaries.
+
+ The custom metadata keys are directly merged into the metadata dictionary.
+ """
+ data, metadata = read_secret_with_metadata(client, mount_point, secret_path)
+
+ if "custom_metadata" in metadata:
+ metadata.update(metadata["custom_metadata"])
+ del metadata["custom_metadata"]
+
+ return data, metadata
+
+
+# -------------------------------------------------------------
+# Helpers to select common fields
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def get_username(client: Client, mount_point: str, secret_path: str) -> str:
+ return get_field(client, mount_point, secret_path, "username")
+
+
+def get_password(client: Client, mount_point: str, secret_path: str) -> str:
+ return get_field(client, mount_point, secret_path, "password")
+
+
+def get_field(client: Client, mount_point: str, secret_path: str, field: str) -> str:
+ secret = read_secret(client, mount_point, secret_path)
+
+ try:
+ return secret[field]
+ except KeyError:
+ raise ValueError(f"Missing {field} field in {mount_point}/{secret_path}")
diff --git a/tools/secretsmith/src/secretsmith/vault/utils.py b/tools/secretsmith/src/secretsmith/vault/utils.py
new file mode 100644
--- /dev/null
+++ b/tools/secretsmith/src/secretsmith/vault/utils.py
@@ -0,0 +1,22 @@
+# -------------------------------------------------------------
+# Secretsmith :: Vault :: Utilities
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+from typing import Tuple
+
+
+def split_path(full_path: str) -> Tuple[str, str]:
+ """
+ Split a full path into mount point and secret path,
+ assuming the first part of the full path is the mount point.
+ """
+ tokens = full_path.split("/")
+
+ mount_point = tokens[0]
+ secret_path = "/".join(tokens[1:])
+
+ return mount_point, secret_path
diff --git a/tools/secretsmith/tests/vault/test_secrets.py b/tools/secretsmith/tests/vault/test_secrets.py
new file mode 100644
--- /dev/null
+++ b/tools/secretsmith/tests/vault/test_secrets.py
@@ -0,0 +1,98 @@
+# -------------------------------------------------------------
+# Secretsmith :: Vault :: KV secrets engine - version 2
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+import unittest
+from unittest.mock import MagicMock
+
+from secretsmith.vault.secrets import *
+
+
+class TestReadSecret(unittest.TestCase):
+ def setUp(self):
+ self.mock_client = MagicMock()
+
+ secret_mock = MagicMock(return_value=self.mock_kv2_secret())
+ self.mock_client.secrets.kv.read_secret_version = secret_mock
+
+ @staticmethod
+ def mock_kv2_secret():
+ return {
+ "data": {
+ "data": {
+ "username": "someuser",
+ "password": "somepass",
+ },
+ "metadata": {
+ "created_time": "2021-01-01T00:00:00.000000Z",
+ "deletion_time": "",
+ "destroyed": False,
+ "version": 1,
+ "custom_metadata": {"owner": "someone"},
+ },
+ }
+ }
+
+ def test_read_secret(self):
+ result = read_secret(self.mock_client, "test_mount", "test_path")
+
+ expected = {"username": "someuser", "password": "somepass"}
+ self.assertEqual(expected, result)
+
+ def test_read_secret_empty_data(self):
+ self.mock_client.secrets.kv.read_secret_version.return_value = {
+ "data": {"data": {}}
+ }
+
+ result = read_secret(self.mock_client, "test_mount", "empty_data_path")
+
+ self.assertEqual({}, result)
+
+ def test_read_secret_with_metadata_(self):
+ result_data, result_metadata = read_secret_with_metadata(
+ self.mock_client, "test_mount", "test_path"
+ )
+ expected_data = {"username": "someuser", "password": "somepass"}
+ expected_metadata = {
+ "created_time": "2021-01-01T00:00:00.000000Z",
+ "deletion_time": "",
+ "destroyed": False,
+ "version": 1,
+ "custom_metadata": {"owner": "someone"},
+ }
+ self.assertEqual(expected_data, result_data)
+ self.assertEqual(expected_metadata, result_metadata)
+
+ def test_read_secret_with_custom_metadata(self):
+ result_data, result_metadata = read_secret_with_custom_metadata(
+ self.mock_client, "test_mount", "test_path"
+ )
+ expected_data = {"username": "someuser", "password": "somepass"}
+ expected_metadata = {
+ "created_time": "2021-01-01T00:00:00.000000Z",
+ "deletion_time": "",
+ "destroyed": False,
+ "version": 1,
+ "owner": "someone",
+ }
+ self.assertEqual(expected_data, result_data)
+ self.assertEqual(expected_metadata, result_metadata)
+
+ def test_get_username(self):
+ result = get_username(self.mock_client, "test_mount", "test_path")
+ self.assertEqual("someuser", result)
+
+ def test_get_password(self):
+ result = get_password(self.mock_client, "test_mount", "test_path")
+ self.assertEqual("somepass", result)
+
+ def test_get_field(self):
+ result = get_field(self.mock_client, "test_mount", "test_path", "username")
+ self.assertEqual("someuser", result)
+
+
+if __name__ == "__main__":
+ unittest.main()
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Sep 22, 22:43 (16 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3005251
Default Alt Text
D3697.diff (26 KB)
Attached To
Mode
D3697: Configure Vault connection with secretsmith
Attached
Detach File
Event Timeline
Log In to Comment