dworshak 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dworshak-0.1.2/LICENSE +7 -0
- dworshak-0.1.2/PKG-INFO +51 -0
- dworshak-0.1.2/README.md +39 -0
- dworshak-0.1.2/pyproject.toml +26 -0
- dworshak-0.1.2/setup.cfg +4 -0
- dworshak-0.1.2/src/dworshak/__init__.py +0 -0
- dworshak-0.1.2/src/dworshak/cli.py +110 -0
- dworshak-0.1.2/src/dworshak/core/__init__.py +0 -0
- dworshak-0.1.2/src/dworshak/core/bootstrap.py +101 -0
- dworshak-0.1.2/src/dworshak/core/security.py +40 -0
- dworshak-0.1.2/src/dworshak/core/vault.py +57 -0
- dworshak-0.1.2/src/dworshak/paths.py +9 -0
- dworshak-0.1.2/src/dworshak/services.py +7 -0
- dworshak-0.1.2/src/dworshak.egg-info/PKG-INFO +51 -0
- dworshak-0.1.2/src/dworshak.egg-info/SOURCES.txt +17 -0
- dworshak-0.1.2/src/dworshak.egg-info/dependency_links.txt +1 -0
- dworshak-0.1.2/src/dworshak.egg-info/entry_points.txt +2 -0
- dworshak-0.1.2/src/dworshak.egg-info/requires.txt +3 -0
- dworshak-0.1.2/src/dworshak.egg-info/top_level.txt +1 -0
dworshak-0.1.2/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 George Clayton Bennett <https://github.com/City-of-Memphis-Wastewater/dworshak-access/>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
dworshak-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dworshak
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Manage local, encrypted credentials. The **dworshak* CLI leverages openssl, sqlite3, and cryptography.
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: cryptography>=46.0.3
|
|
9
|
+
Requires-Dist: typer>=0.21.0
|
|
10
|
+
Requires-Dist: rich>=13.0.0
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# Project Dworshak 🌊
|
|
14
|
+
|
|
15
|
+
**Dworshak** is the security bones behind API orchestration of infrastructure data between legacy SOAP services (EDS) and modern REST APIs (RJN).
|
|
16
|
+
|
|
17
|
+
## 🏗 The Ultimate Vision
|
|
18
|
+
To become a stable credential management tool for scripting the flow of Emerson Ovation data and related APIs, supporting multiple projects in and beyond at the Maxson Wastewater Treatment Plant.
|
|
19
|
+
* **The Wider Goal:** A system where data is fetched, validated, and mirrored locally so that decision-support tools (Dashboards, Alarms) never have to "wait" on a slow external API.
|
|
20
|
+
* **The Method:** "Do one boring thing well." Use OpenSSL to manage a local `~/.dowrshak/ directory which includes a `.key` file, a `vault.db` encrypted credential file, and a `config.json` file for controlling defaults.
|
|
21
|
+
|
|
22
|
+
## ⚖️ User Stories
|
|
23
|
+
Dworshak supports two complementary roles within the infrastructure data ecosystem:
|
|
24
|
+
1. Infrastructure Integrator (Primary User)
|
|
25
|
+
> I need a secure, predictable tool that orchestrates the movement of data between upstream and downstream systems — pulling from legacy SOAP endpoints, transforming or validating as needed, and pushing clean, trusted data to the services that depend on it.
|
|
26
|
+
> Dworshak should behave like a controlled “data dam,” ensuring one‑directional flow, consistent execution across platforms, and strict protection of credentials.
|
|
27
|
+
2. Data Analyst (Secondary User)
|
|
28
|
+
> I need a reliable, set-and-forget tool that synchronizes remote API data into a local, high-performance SQLite mirror, so that dashboards, reports, and decision-support tools never have to wait on slow or unreliable external services. Equipped with the Dworshak CLI and its companion toolset, I can build visualizations and reports without worrying about credential leaks, API timeouts, or platform-specific (Windows vs. Termux) bugs.
|
|
29
|
+
|
|
30
|
+
## 🚀 The MVP (Current State)
|
|
31
|
+
- **Secure Vault:** Fernet-encrypted SQLite storage for API credentials.
|
|
32
|
+
- **Root of Trust:** A local `.key` file architecture that works identically on Windows and Termux.
|
|
33
|
+
- **CLI Entry:** A `typer`-based interface for setup and credential management.
|
|
34
|
+
|
|
35
|
+
## ⚠️ Risks & Guardrails
|
|
36
|
+
To prevent "going off the rails" or drowning in scope creep:
|
|
37
|
+
**The Anti-Daemon Bias:** Stay script-based. Using `task-scheduler` or `cron` is more robust than maintaining a long-running daemon process that can leak memory or crash silently.
|
|
38
|
+
|
|
39
|
+
### Quick Start
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Install the CLI
|
|
43
|
+
pipx install dworshak
|
|
44
|
+
|
|
45
|
+
# Bootstrap the security layer
|
|
46
|
+
dworshak setup
|
|
47
|
+
|
|
48
|
+
# Register your first API
|
|
49
|
+
dworshak register --service rjn_api --item username
|
|
50
|
+
|
|
51
|
+
```
|
dworshak-0.1.2/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Project Dworshak 🌊
|
|
2
|
+
|
|
3
|
+
**Dworshak** is the security bones behind API orchestration of infrastructure data between legacy SOAP services (EDS) and modern REST APIs (RJN).
|
|
4
|
+
|
|
5
|
+
## 🏗 The Ultimate Vision
|
|
6
|
+
To become a stable credential management tool for scripting the flow of Emerson Ovation data and related APIs, supporting multiple projects in and beyond at the Maxson Wastewater Treatment Plant.
|
|
7
|
+
* **The Wider Goal:** A system where data is fetched, validated, and mirrored locally so that decision-support tools (Dashboards, Alarms) never have to "wait" on a slow external API.
|
|
8
|
+
* **The Method:** "Do one boring thing well." Use OpenSSL to manage a local `~/.dowrshak/ directory which includes a `.key` file, a `vault.db` encrypted credential file, and a `config.json` file for controlling defaults.
|
|
9
|
+
|
|
10
|
+
## ⚖️ User Stories
|
|
11
|
+
Dworshak supports two complementary roles within the infrastructure data ecosystem:
|
|
12
|
+
1. Infrastructure Integrator (Primary User)
|
|
13
|
+
> I need a secure, predictable tool that orchestrates the movement of data between upstream and downstream systems — pulling from legacy SOAP endpoints, transforming or validating as needed, and pushing clean, trusted data to the services that depend on it.
|
|
14
|
+
> Dworshak should behave like a controlled “data dam,” ensuring one‑directional flow, consistent execution across platforms, and strict protection of credentials.
|
|
15
|
+
2. Data Analyst (Secondary User)
|
|
16
|
+
> I need a reliable, set-and-forget tool that synchronizes remote API data into a local, high-performance SQLite mirror, so that dashboards, reports, and decision-support tools never have to wait on slow or unreliable external services. Equipped with the Dworshak CLI and its companion toolset, I can build visualizations and reports without worrying about credential leaks, API timeouts, or platform-specific (Windows vs. Termux) bugs.
|
|
17
|
+
|
|
18
|
+
## 🚀 The MVP (Current State)
|
|
19
|
+
- **Secure Vault:** Fernet-encrypted SQLite storage for API credentials.
|
|
20
|
+
- **Root of Trust:** A local `.key` file architecture that works identically on Windows and Termux.
|
|
21
|
+
- **CLI Entry:** A `typer`-based interface for setup and credential management.
|
|
22
|
+
|
|
23
|
+
## ⚠️ Risks & Guardrails
|
|
24
|
+
To prevent "going off the rails" or drowning in scope creep:
|
|
25
|
+
**The Anti-Daemon Bias:** Stay script-based. Using `task-scheduler` or `cron` is more robust than maintaining a long-running daemon process that can leak memory or crash silently.
|
|
26
|
+
|
|
27
|
+
### Quick Start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Install the CLI
|
|
31
|
+
pipx install dworshak
|
|
32
|
+
|
|
33
|
+
# Bootstrap the security layer
|
|
34
|
+
dworshak setup
|
|
35
|
+
|
|
36
|
+
# Register your first API
|
|
37
|
+
dworshak register --service rjn_api --item username
|
|
38
|
+
|
|
39
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "dworshak"
|
|
3
|
+
version = "0.1.2"
|
|
4
|
+
description = "Manage local, encrypted credentials. The **dworshak* CLI leverages openssl, sqlite3, and cryptography."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"cryptography>=46.0.3", # No marker needed if Rust is present
|
|
9
|
+
"typer>=0.21.0",
|
|
10
|
+
#"typer>=0.12.0",
|
|
11
|
+
"rich>=13.0.0",
|
|
12
|
+
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
dworshak = "dworshak.cli:app"
|
|
17
|
+
|
|
18
|
+
[tool.uv]
|
|
19
|
+
# This tells uv that if it can't find a wheel for aarch64,
|
|
20
|
+
# it is allowed to build from source using your local Rust/Clang.
|
|
21
|
+
package = true
|
|
22
|
+
resolution = "highest"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["setuptools>=64", "wheel"]
|
|
26
|
+
build-backend = "setuptools.build_meta"
|
dworshak-0.1.2/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sqlite3
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from cryptography.fernet import Fernet
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from dworshak.paths import APP_DIR,KEY_FILE,DB_FILE,CONFIG_FILE
|
|
15
|
+
from dworshak.services import KNOWN_SERVICES
|
|
16
|
+
from dworshak.core.bootstrap import initialize_environment
|
|
17
|
+
from dworshak.core.security import get_fernet
|
|
18
|
+
from dworshak.core.vault import (
|
|
19
|
+
credential_exists,
|
|
20
|
+
store_credential,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Force Rich to always enable colors, even when running from a .pyz bundle
|
|
24
|
+
os.environ["FORCE_COLOR"] = "1"
|
|
25
|
+
# Optional but helpful for full terminal feature detection
|
|
26
|
+
os.environ["TERM"] = "xterm-256color"
|
|
27
|
+
|
|
28
|
+
app = typer.Typer(
|
|
29
|
+
name = "dworshak",
|
|
30
|
+
help="Secure API Orchestration for Infrastructure.",
|
|
31
|
+
add_completion=False,
|
|
32
|
+
invoke_without_command = True,
|
|
33
|
+
no_args_is_help = True,
|
|
34
|
+
context_settings={"ignore_unknown_options": True,
|
|
35
|
+
"allow_extra_args": True,
|
|
36
|
+
"help_option_names": ["-h", "--help"]},
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
console = Console()
|
|
41
|
+
|
|
42
|
+
# --- CLI COMMANDS ---
|
|
43
|
+
|
|
44
|
+
@app.command()
|
|
45
|
+
def setup():
|
|
46
|
+
"""Bootstrap the Dworshak environment and generate security keys."""
|
|
47
|
+
#initialize_system()
|
|
48
|
+
initialize_environment()
|
|
49
|
+
console.print(Panel.fit(
|
|
50
|
+
"Dworshak System Initialized\n[bold green]Security Layer Active[/bold green]",
|
|
51
|
+
title="Success"
|
|
52
|
+
))
|
|
53
|
+
|
|
54
|
+
# def register(service: str = typer.Option("rjn_api", prompt=True)):
|
|
55
|
+
@app.command()
|
|
56
|
+
def register(
|
|
57
|
+
service: str = typer.Option(
|
|
58
|
+
"rjn_api",
|
|
59
|
+
prompt="Service Name",
|
|
60
|
+
show_default=True,
|
|
61
|
+
click_type=click.Choice(KNOWN_SERVICES),),
|
|
62
|
+
item: str = typer.Option(..., prompt="Credential Item (e.g., primary)"),
|
|
63
|
+
username: str = typer.Option(..., prompt="Username"),
|
|
64
|
+
password: str = typer.Option(..., prompt="Password", hide_input=True)
|
|
65
|
+
):
|
|
66
|
+
|
|
67
|
+
"""Encrypt and store a new credential in the vault."""
|
|
68
|
+
# Check for existing credential
|
|
69
|
+
if credential_exists(service, item):
|
|
70
|
+
console.print(
|
|
71
|
+
f"[yellow]A credential for {service}/{item} already exists.[/yellow]"
|
|
72
|
+
)
|
|
73
|
+
overwrite = typer.confirm("Overwrite?", default=False)
|
|
74
|
+
if not overwrite:
|
|
75
|
+
console.print("[green]Operation cancelled.[/green]")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
# Encrypt the payload
|
|
79
|
+
fernet = get_fernet()
|
|
80
|
+
payload = json.dumps({"u": username, "p": password}).encode()
|
|
81
|
+
encrypted_blob = fernet.encrypt(payload)
|
|
82
|
+
|
|
83
|
+
store_credential(service, item, encrypted_blob)
|
|
84
|
+
console.print(
|
|
85
|
+
f"[green]✔ Credential for [bold]{service}/{item}[/bold] stored securely.[/green]"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@app.command()
|
|
89
|
+
def list_services():
|
|
90
|
+
"""List all services currently stored in the vault (names only)."""
|
|
91
|
+
if not DB_FILE.exists():
|
|
92
|
+
console.print("[red]Vault not initialized.[/red]")
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
conn = sqlite3.connect(DB_FILE)
|
|
96
|
+
cursor = conn.execute("SELECT service, item FROM credentials")
|
|
97
|
+
rows = cursor.fetchall()
|
|
98
|
+
conn.close()
|
|
99
|
+
|
|
100
|
+
table = Table(title="Secure Vault Services")
|
|
101
|
+
table.add_column("Service", style="cyan")
|
|
102
|
+
table.add_column("Item", style="magenta")
|
|
103
|
+
|
|
104
|
+
for row in rows:
|
|
105
|
+
table.add_row(row[0], row[1])
|
|
106
|
+
|
|
107
|
+
console.print(table)
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
app()
|
|
File without changes
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bootstrap routines for establishing the Dworshak runtime environment.
|
|
3
|
+
|
|
4
|
+
This module is responsible for:
|
|
5
|
+
- Creating the application directory structure
|
|
6
|
+
- Generating and securing the master encryption key
|
|
7
|
+
- Initializing the SQLite credential vault
|
|
8
|
+
- Writing the initial configuration file
|
|
9
|
+
|
|
10
|
+
These operations are intended to run once during setup, but remain
|
|
11
|
+
idempotent to support repeated execution without side effects.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import json
|
|
16
|
+
import sqlite3
|
|
17
|
+
from cryptography.fernet import Fernet
|
|
18
|
+
from rich.console import Console
|
|
19
|
+
|
|
20
|
+
from dworshak.paths import APP_DIR, KEY_FILE, DB_FILE, CONFIG_FILE
|
|
21
|
+
|
|
22
|
+
console = Console()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def initialize_environment() -> None:
|
|
26
|
+
"""
|
|
27
|
+
Establishes the Dworshak environment on the local system.
|
|
28
|
+
|
|
29
|
+
This includes:
|
|
30
|
+
- Ensuring the application directory exists
|
|
31
|
+
- Creating the master key if absent
|
|
32
|
+
- Initializing the credential vault
|
|
33
|
+
- Creating the configuration file with stable defaults
|
|
34
|
+
|
|
35
|
+
All operations are safe to repeat and will not overwrite existing data.
|
|
36
|
+
"""
|
|
37
|
+
APP_DIR.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
|
|
39
|
+
_ensure_master_key()
|
|
40
|
+
_ensure_vault()
|
|
41
|
+
_ensure_config()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _ensure_master_key() -> None:
|
|
45
|
+
"""
|
|
46
|
+
Generates the master Fernet key if it does not already exist.
|
|
47
|
+
|
|
48
|
+
The key is stored with restrictive permissions to limit access
|
|
49
|
+
to the current system account.
|
|
50
|
+
"""
|
|
51
|
+
if KEY_FILE.exists():
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
console.print("[yellow]Initializing Root of Trust...[/yellow]")
|
|
55
|
+
|
|
56
|
+
key = Fernet.generate_key()
|
|
57
|
+
KEY_FILE.write_bytes(key)
|
|
58
|
+
|
|
59
|
+
# Restrict permissions: read/write for owner only
|
|
60
|
+
os.chmod(KEY_FILE, 0o600)
|
|
61
|
+
|
|
62
|
+
console.print(f"[green]✔ Master key generated at {KEY_FILE}[/green]")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _ensure_vault() -> None:
|
|
66
|
+
"""
|
|
67
|
+
Creates the SQLite credential vault if it does not exist.
|
|
68
|
+
|
|
69
|
+
The schema is intentionally minimal and stable to support long-term
|
|
70
|
+
compatibility across Dworshak versions.
|
|
71
|
+
"""
|
|
72
|
+
conn = sqlite3.connect(DB_FILE)
|
|
73
|
+
conn.execute(
|
|
74
|
+
"""
|
|
75
|
+
CREATE TABLE IF NOT EXISTS credentials (
|
|
76
|
+
service TEXT NOT NULL,
|
|
77
|
+
item TEXT NOT NULL,
|
|
78
|
+
encrypted_blob BLOB NOT NULL,
|
|
79
|
+
PRIMARY KEY (service, item)
|
|
80
|
+
)
|
|
81
|
+
"""
|
|
82
|
+
)
|
|
83
|
+
conn.close()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _ensure_config() -> None:
|
|
87
|
+
"""
|
|
88
|
+
Writes the initial configuration file if absent.
|
|
89
|
+
|
|
90
|
+
The configuration file stores user preferences only.
|
|
91
|
+
Service definitions are maintained in code to ensure stability.
|
|
92
|
+
"""
|
|
93
|
+
if CONFIG_FILE.exists():
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
default_config = {
|
|
97
|
+
"default_service": "rjn_api"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
CONFIG_FILE.write_text(json.dumps(default_config, indent=2))
|
|
101
|
+
console.print(f"[green]✔ Config file created at {CONFIG_FILE}[/green]")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Security utilities for Dworshak.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
- Loading of the master Fernet key
|
|
6
|
+
- Construction of Fernet instances for encryption and decryption
|
|
7
|
+
|
|
8
|
+
Environment overrides are supported to enable automated or headless
|
|
9
|
+
execution environments such as CI pipelines or scheduled tasks.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
from cryptography.fernet import Fernet
|
|
14
|
+
|
|
15
|
+
from dworshak.paths import KEY_FILE
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_fernet() -> Fernet:
|
|
19
|
+
"""
|
|
20
|
+
Returns a Fernet instance using the master key.
|
|
21
|
+
|
|
22
|
+
The key is loaded from:
|
|
23
|
+
1. The DWORSHAK_MASTER_KEY environment variable, if present
|
|
24
|
+
2. The .key file in the application directory
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
FileNotFoundError: if no key is available from either source.
|
|
28
|
+
"""
|
|
29
|
+
key_override = os.getenv("DWORSHAK_MASTER_KEY")
|
|
30
|
+
|
|
31
|
+
if key_override:
|
|
32
|
+
return Fernet(key_override.encode())
|
|
33
|
+
|
|
34
|
+
if not KEY_FILE.exists():
|
|
35
|
+
raise FileNotFoundError(
|
|
36
|
+
"Master key not found. Run 'dworshak setup' to initialize the environment."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
key_bytes = KEY_FILE.read_bytes()
|
|
40
|
+
return Fernet(key_bytes)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Credential vault CRUD operations for Dworshak.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
- Existence checks
|
|
6
|
+
- Insert and update operations
|
|
7
|
+
- Retrieval of encrypted blobs
|
|
8
|
+
- Listing of stored credentials
|
|
9
|
+
|
|
10
|
+
All encryption and decryption is handled by core.security.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import sqlite3
|
|
14
|
+
from typing import Optional, Tuple
|
|
15
|
+
|
|
16
|
+
from dworshak.paths import DB_FILE
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def credential_exists(service: str, item: str) -> bool:
|
|
20
|
+
conn = sqlite3.connect(DB_FILE)
|
|
21
|
+
cursor = conn.execute(
|
|
22
|
+
"SELECT 1 FROM credentials WHERE service = ? AND item = ?",
|
|
23
|
+
(service, item)
|
|
24
|
+
)
|
|
25
|
+
exists = cursor.fetchone() is not None
|
|
26
|
+
conn.close()
|
|
27
|
+
return exists
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def store_credential(service: str, item: str, encrypted_blob: bytes) -> None:
|
|
31
|
+
conn = sqlite3.connect(DB_FILE)
|
|
32
|
+
conn.execute(
|
|
33
|
+
"INSERT OR REPLACE INTO credentials (service, item, encrypted_blob)"
|
|
34
|
+
" VALUES (?, ?, ?)",
|
|
35
|
+
(service, item, encrypted_blob)
|
|
36
|
+
)
|
|
37
|
+
conn.commit()
|
|
38
|
+
conn.close()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_encrypted_credential(service: str, item: str) -> Optional[bytes]:
|
|
42
|
+
conn = sqlite3.connect(DB_FILE)
|
|
43
|
+
cursor = conn.execute(
|
|
44
|
+
"SELECT encrypted_blob FROM credentials WHERE service = ? AND item = ?",
|
|
45
|
+
(service, item)
|
|
46
|
+
)
|
|
47
|
+
row = cursor.fetchone()
|
|
48
|
+
conn.close()
|
|
49
|
+
return row[0] if row else None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def list_credentials() -> list[tuple[str, str]]:
|
|
53
|
+
conn = sqlite3.connect(DB_FILE)
|
|
54
|
+
cursor = conn.execute("SELECT service, item FROM credentials")
|
|
55
|
+
rows = cursor.fetchall()
|
|
56
|
+
conn.close()
|
|
57
|
+
return rows
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
# --- CONFIGURATION & PATHS ---
|
|
4
|
+
# Standardizing on a hidden home directory for cross-platform utility (Termux/Windows)
|
|
5
|
+
|
|
6
|
+
APP_DIR = Path.home() / ".dworshak"
|
|
7
|
+
KEY_FILE = APP_DIR / ".key"
|
|
8
|
+
DB_FILE = APP_DIR / "vault.db"
|
|
9
|
+
CONFIG_FILE = APP_DIR / "config.json"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dworshak
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Manage local, encrypted credentials. The **dworshak* CLI leverages openssl, sqlite3, and cryptography.
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: cryptography>=46.0.3
|
|
9
|
+
Requires-Dist: typer>=0.21.0
|
|
10
|
+
Requires-Dist: rich>=13.0.0
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# Project Dworshak 🌊
|
|
14
|
+
|
|
15
|
+
**Dworshak** is the security bones behind API orchestration of infrastructure data between legacy SOAP services (EDS) and modern REST APIs (RJN).
|
|
16
|
+
|
|
17
|
+
## 🏗 The Ultimate Vision
|
|
18
|
+
To become a stable credential management tool for scripting the flow of Emerson Ovation data and related APIs, supporting multiple projects in and beyond at the Maxson Wastewater Treatment Plant.
|
|
19
|
+
* **The Wider Goal:** A system where data is fetched, validated, and mirrored locally so that decision-support tools (Dashboards, Alarms) never have to "wait" on a slow external API.
|
|
20
|
+
* **The Method:** "Do one boring thing well." Use OpenSSL to manage a local `~/.dowrshak/ directory which includes a `.key` file, a `vault.db` encrypted credential file, and a `config.json` file for controlling defaults.
|
|
21
|
+
|
|
22
|
+
## ⚖️ User Stories
|
|
23
|
+
Dworshak supports two complementary roles within the infrastructure data ecosystem:
|
|
24
|
+
1. Infrastructure Integrator (Primary User)
|
|
25
|
+
> I need a secure, predictable tool that orchestrates the movement of data between upstream and downstream systems — pulling from legacy SOAP endpoints, transforming or validating as needed, and pushing clean, trusted data to the services that depend on it.
|
|
26
|
+
> Dworshak should behave like a controlled “data dam,” ensuring one‑directional flow, consistent execution across platforms, and strict protection of credentials.
|
|
27
|
+
2. Data Analyst (Secondary User)
|
|
28
|
+
> I need a reliable, set-and-forget tool that synchronizes remote API data into a local, high-performance SQLite mirror, so that dashboards, reports, and decision-support tools never have to wait on slow or unreliable external services. Equipped with the Dworshak CLI and its companion toolset, I can build visualizations and reports without worrying about credential leaks, API timeouts, or platform-specific (Windows vs. Termux) bugs.
|
|
29
|
+
|
|
30
|
+
## 🚀 The MVP (Current State)
|
|
31
|
+
- **Secure Vault:** Fernet-encrypted SQLite storage for API credentials.
|
|
32
|
+
- **Root of Trust:** A local `.key` file architecture that works identically on Windows and Termux.
|
|
33
|
+
- **CLI Entry:** A `typer`-based interface for setup and credential management.
|
|
34
|
+
|
|
35
|
+
## ⚠️ Risks & Guardrails
|
|
36
|
+
To prevent "going off the rails" or drowning in scope creep:
|
|
37
|
+
**The Anti-Daemon Bias:** Stay script-based. Using `task-scheduler` or `cron` is more robust than maintaining a long-running daemon process that can leak memory or crash silently.
|
|
38
|
+
|
|
39
|
+
### Quick Start
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Install the CLI
|
|
43
|
+
pipx install dworshak
|
|
44
|
+
|
|
45
|
+
# Bootstrap the security layer
|
|
46
|
+
dworshak setup
|
|
47
|
+
|
|
48
|
+
# Register your first API
|
|
49
|
+
dworshak register --service rjn_api --item username
|
|
50
|
+
|
|
51
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/dworshak/__init__.py
|
|
5
|
+
src/dworshak/cli.py
|
|
6
|
+
src/dworshak/paths.py
|
|
7
|
+
src/dworshak/services.py
|
|
8
|
+
src/dworshak.egg-info/PKG-INFO
|
|
9
|
+
src/dworshak.egg-info/SOURCES.txt
|
|
10
|
+
src/dworshak.egg-info/dependency_links.txt
|
|
11
|
+
src/dworshak.egg-info/entry_points.txt
|
|
12
|
+
src/dworshak.egg-info/requires.txt
|
|
13
|
+
src/dworshak.egg-info/top_level.txt
|
|
14
|
+
src/dworshak/core/__init__.py
|
|
15
|
+
src/dworshak/core/bootstrap.py
|
|
16
|
+
src/dworshak/core/security.py
|
|
17
|
+
src/dworshak/core/vault.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dworshak
|