Skip to content

Commit dc533f3

Browse files
committed
add support for hashicorp vault
1 parent 1e93b47 commit dc533f3

4 files changed

Lines changed: 367 additions & 257 deletions

File tree

.vscode/settings.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
"notif",
1212
"werein"
1313
],
14-
1514
"editor.formatOnSave": true,
1615
"editor.codeActionsOnSave": {
1716
"source.fixAll.ruff": "explicit",
@@ -46,5 +45,10 @@
4645
"**/.venv/**": true,
4746
"**/.mypy_cache/**": true,
4847
"**/.ruff_cache/**": true
49-
}
50-
}
48+
},
49+
"python.testing.pytestArgs": [
50+
"tests"
51+
],
52+
"python.testing.unittestEnabled": false,
53+
"python.testing.pytestEnabled": true
54+
}

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dependencies = [
2525
"requests>=2.32.3",
2626
"tabulate>=0.9.0",
2727
"deprecation>=2.1.0",
28+
"hvac>=2.3.0",
2829
]
2930

3031
[project.urls]

src/mistapi/__api_session.py

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from getpass import getpass
1818
from pathlib import Path
1919

20+
import hvac
2021
import requests
2122
from dotenv import load_dotenv
2223
from requests import Response, Session
@@ -55,6 +56,10 @@ def __init__(
5556
apitoken: str | None = None,
5657
host: str | None = None,
5758
env_file: str | None = None,
59+
vault_url: str | None = None,
60+
vault_token: str | None = None,
61+
vault_mount_point: str | None = None,
62+
vault_path: str | None = None,
5863
console_log_level: int = 20,
5964
logging_log_level: int = 10,
6065
show_cli_notif: bool = True,
@@ -75,6 +80,14 @@ def __init__(
7580
Mist Cloud to reach (e.g. "api.mist.com"). Can de defined later
7681
env_file : str
7782
path to the env file to load. See README.md for allowed variables
83+
vault_url : str
84+
URL of the HashiCorp Vault instance
85+
vault_mount_point : str
86+
Mount point for the secrets engine
87+
vault_path : str
88+
Path to the secret in Vault
89+
vault_token : str
90+
Token for authenticating with Vault
7891
console_log_level : int, default: 20
7992
Log level for the console output. Values are:
8093
50 -> CRITICAL
@@ -113,10 +126,15 @@ def __init__(
113126
self._logging_log_level = logging_log_level
114127
self._show_cli_notif = show_cli_notif
115128
self._proxies = {"https": https_proxy}
129+
self.vault_url = vault_url
130+
self.vault_path = vault_path
131+
self.vault_mount_point = vault_mount_point
132+
self.vault_token = vault_token
116133

117134
console._set_log_level(console_log_level, logging_log_level)
118-
119135
self._load_env(env_file)
136+
if self.vault_path:
137+
self._load_vault()
120138
# Filter out None values before updating proxies
121139
filtered_proxies = {k: v for k, v in self._proxies.items() if v is not None}
122140
self._session.proxies.update(filtered_proxies)
@@ -173,6 +191,44 @@ def __str__(self) -> str:
173191

174192
####################################
175193
# LOAD FUNCTIONS
194+
def _load_vault(
195+
self,
196+
) -> None:
197+
"""
198+
Load Vault settings from env file
199+
"""
200+
logger.info("apisession:_load_vault: Loading Vault settings")
201+
client = hvac.Client(url=self.vault_url, token=self.vault_token, verify=False)
202+
if not client.is_authenticated():
203+
logger.error("apisession:_load_vault: Vault authentication failed")
204+
console.error("Vault authentication failed")
205+
return
206+
logger.debug(
207+
"apisession:_load_vault: Vault authentication successful. Retrieving secret"
208+
)
209+
try:
210+
read_response = client.secrets.kv.v2.read_secret(
211+
path=self.vault_path, mount_point=self.vault_mount_point
212+
)
213+
logger.info("apisession:_load_vault: Secret retrieved successfully")
214+
215+
mist_host = read_response["data"]["data"].get("MIST_HOST", None)
216+
logger.info("apisession:_load_vault: MIST_HOST=%s", mist_host)
217+
if mist_host:
218+
self.set_cloud(mist_host)
219+
220+
mist_apitoken = read_response["data"]["data"].get("MIST_APITOKEN", None)
221+
if mist_apitoken:
222+
self.set_api_token(mist_apitoken)
223+
except (KeyError, TypeError, AttributeError):
224+
logger.error("apisession:_load_vault: Failed to retrieve secret")
225+
console.error("Failed to retrieve secret")
226+
finally:
227+
del self.vault_url
228+
del self.vault_path
229+
del self.vault_mount_point
230+
del self.vault_token
231+
176232
def _load_env(self, env_file=None) -> None:
177233
"""
178234
Load Mist API settings from env file
@@ -192,21 +248,17 @@ def _load_env(self, env_file=None) -> None:
192248
# logger.debug(f"apisession:_load_env:loading settings from env file")
193249
# load_dotenv()
194250

195-
mist_host = os.getenv("MIST_HOST")
196-
if mist_host:
197-
self.set_cloud(mist_host)
251+
if os.getenv("MIST_HOST"):
252+
self.set_cloud(os.getenv("MIST_HOST", ""))
198253

199-
mist_apitoken = os.getenv("MIST_APITOKEN")
200-
if mist_apitoken:
201-
self.set_api_token(mist_apitoken)
254+
if os.getenv("MIST_APITOKEN"):
255+
self.set_api_token(os.getenv("MIST_APITOKEN", ""))
202256

203-
mist_user = os.getenv("MIST_USER")
204-
if mist_user:
205-
self.set_email(mist_user)
257+
if os.getenv("MIST_USER"):
258+
self.set_email(os.getenv("MIST_USER"))
206259

207-
mist_password = os.getenv("MIST_PASSWORD")
208-
if mist_password:
209-
self.set_password(mist_password)
260+
if os.getenv("MIST_PASSWORD"):
261+
self.set_password(os.getenv("MIST_PASSWORD"))
210262

211263
console_log_level_env = os.getenv("CONSOLE_LOG_LEVEL")
212264
if console_log_level_env:
@@ -222,6 +274,18 @@ def _load_env(self, env_file=None) -> None:
222274
except ValueError:
223275
self._logging_log_level = 10 # Default fallback
224276

277+
if os.getenv("MIST_VAULT_URL") and not self.vault_url:
278+
self.vault_url = os.getenv("MIST_VAULT_URL")
279+
280+
if os.getenv("MIST_VAULT_PATH") and not self.vault_path:
281+
self.vault_path = os.getenv("MIST_VAULT_PATH")
282+
283+
if os.getenv("MIST_VAULT_MOUNT_POINT") and not self.vault_mount_point:
284+
self.vault_mount_point = os.getenv("MIST_VAULT_MOUNT_POINT")
285+
286+
if os.getenv("MIST_VAULT_TOKEN") and not self.vault_token:
287+
self.vault_token = os.getenv("MIST_VAULT_TOKEN")
288+
225289
if os.getenv("HTTPS_PROXY"):
226290
self._proxies["https"] = os.getenv("HTTPS_PROXY")
227291

0 commit comments

Comments
 (0)