pyinfra/scripts/bw2secrets

148 lines
3.7 KiB
Text
Raw Normal View History

2024-06-08 00:53:33 +02:00
#!/usr/bin/env python3
import argparse
import getpass
import shutil
import subprocess as sp
from pathlib import Path
2024-07-23 17:13:11 +02:00
from typing import Literal
2024-06-08 00:53:33 +02:00
import jinja2
bitwarden_session = None
2024-07-23 17:13:11 +02:00
TemplateEnvType = Literal["password", "username"]
def fetch_secret(bw_path: Path, secret_id: str, object_type: str = "password") -> str:
global bitwarden_session
res = sp.run(
[bw_path, "get", object_type, secret_id, "--session", bitwarden_session],
capture_output=True,
text=True,
)
res.check_returncode()
return res.stdout
class TemplateEnv:
bw_path: Path
env_type: TemplateEnvType
cached_items: dict[str, str]
def __init__(self, _type: TemplateEnvType, bw_path: Path):
self.env_type = _type
self.bw_path = bw_path
self.cached_items = dict()
def __getitem__(self, item):
print(f"{self.env_type} {self.cached_items}")
if cached_item := self.cached_items.get(item):
return cached_item
self.cached_items[item] = fetch_secret(
self.bw_path,
item,
self.env_type,
)
return self.cached_items[item]
2024-06-08 00:53:33 +02:00
def _add_args(parser: argparse.ArgumentParser):
parser.add_argument(
"search_paths",
help="start directory to walk files to find secret references",
default=".",
nargs="*",
)
parser.add_argument(
"--bw-path", "-b",
help="custom path for bitwarden cli executable",
default=shutil.which("bw"),
)
def init_bw_session(bw_path: Path):
global bitwarden_session
if (pw_file := Path("./.bw2secrets")).exists():
bitwarden_password = pw_file.read_text().strip()
else:
print("Please, provide your bitwarden master password")
bitwarden_password = getpass.getpass("Master password: ")
res = sp.run(
[bw_path, "unlock", bitwarden_password, "--raw"], capture_output=True,
text=True,
)
res.check_returncode()
bitwarden_session = res.stdout
2024-06-08 13:17:04 +02:00
def sync_bw_session(bw_path: Path):
global bitwarden_session
res = sp.run(
[bw_path, "sync", "--session", bitwarden_session], capture_output=True,
text=True,
)
res.check_returncode()
2024-06-08 00:53:33 +02:00
def find_templates(base_dirs: set[Path]) -> set[Path]:
env_templates: set[Path] = set()
for path in base_dirs:
2024-07-23 17:13:11 +02:00
for env_template in path.glob("**/*.template"):
2024-06-08 00:53:33 +02:00
env_templates.add(env_template)
return env_templates
2024-07-23 17:13:11 +02:00
# def secret_filter(bw_path: Path, secret_id: str) -> str:
# return fetch_secret(bw_path, secret_id)
2024-06-08 00:53:33 +02:00
def compile_file(file_path: Path, bw_path: Path):
jinja_env = jinja2.Environment(
loader=jinja2.FileSystemLoader(file_path.parent),
)
2024-07-23 17:13:11 +02:00
username = TemplateEnv("username", bw_path)
password = TemplateEnv("password", bw_path)
2024-06-08 00:53:33 +02:00
template = jinja_env.get_template(file_path.name)
2024-07-23 17:13:11 +02:00
rendered_template = template.render(
dict(
username=username,
password=password,
),
)
2024-06-08 00:53:33 +02:00
file_path.with_name(
file_path.name.replace(
".template", "",
),
).write_text(rendered_template)
def main() -> int:
parser = argparse.ArgumentParser("bw2secrets")
_add_args(parser)
args = parser.parse_args()
if not (bw_path := args.bw_path):
print("Bitwarden CLI `bw` executable not found in PATH")
return 1
search_paths: set[Path | None] = set()
for path in args.search_paths:
search_path = Path(path)
search_paths.add(search_path)
template_files = find_templates(search_paths)
init_bw_session(bw_path)
2024-06-12 10:55:52 +02:00
sync_bw_session(bw_path)
2024-06-08 00:53:33 +02:00
for file in template_files:
compile_file(file, bw_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())