mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
Add vault locking command and API
This commit is contained in:
@@ -72,6 +72,7 @@ Manage the entire vault for a profile.
|
||||
| Export the vault | `vault export` | `seedpass vault export --file backup.json` |
|
||||
| Import a vault | `vault import` | `seedpass vault import --file backup.json` |
|
||||
| Change the master password | `vault change-password` | `seedpass vault change-password` |
|
||||
| Lock the vault | `vault lock` | `seedpass vault lock` |
|
||||
|
||||
### Nostr Commands
|
||||
|
||||
@@ -157,6 +158,7 @@ Code: 123456
|
||||
- **`seedpass vault export`** – Export the entire vault to an encrypted JSON file.
|
||||
- **`seedpass vault import`** – Import a vault from an encrypted JSON file.
|
||||
- **`seedpass vault change-password`** – Change the master password used for encryption.
|
||||
- **`seedpass vault lock`** – Clear sensitive data from memory and require reauthentication.
|
||||
|
||||
### `nostr` Commands
|
||||
|
||||
|
@@ -35,6 +35,7 @@ Keep this token secret. Every request must include it in the `Authorization` hea
|
||||
- `POST /api/v1/checksum/update` – Update the stored script checksum.
|
||||
- `POST /api/v1/change-password` – Change the master password for the active profile.
|
||||
- `POST /api/v1/vault/import` – Import a vault backup from a file or path.
|
||||
- `POST /api/v1/vault/lock` – Lock the vault and clear sensitive data from memory.
|
||||
- `POST /api/v1/shutdown` – Stop the server gracefully.
|
||||
|
||||
**Security Warning:** Accessing `/api/v1/parent-seed` exposes your master seed in plain text. Use it only from a trusted environment.
|
||||
|
@@ -414,6 +414,15 @@ def change_password(authorization: str | None = Header(None)) -> dict[str, str]:
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@app.post("/api/v1/vault/lock")
|
||||
def lock_vault(authorization: str | None = Header(None)) -> dict[str, str]:
|
||||
"""Lock the vault and clear sensitive data from memory."""
|
||||
_check_token(authorization)
|
||||
assert _pm is not None
|
||||
_pm.lock_vault()
|
||||
return {"status": "locked"}
|
||||
|
||||
|
||||
@app.post("/api/v1/shutdown")
|
||||
async def shutdown_server(authorization: str | None = Header(None)) -> dict[str, str]:
|
||||
_check_token(authorization)
|
||||
|
@@ -360,6 +360,14 @@ def vault_change_password(ctx: typer.Context) -> None:
|
||||
pm.change_password()
|
||||
|
||||
|
||||
@vault_app.command("lock")
|
||||
def vault_lock(ctx: typer.Context) -> None:
|
||||
"""Lock the vault and clear sensitive data from memory."""
|
||||
pm = _get_pm(ctx)
|
||||
pm.lock_vault()
|
||||
typer.echo("locked")
|
||||
|
||||
|
||||
@vault_app.command("reveal-parent-seed")
|
||||
def vault_reveal_parent_seed(
|
||||
ctx: typer.Context,
|
||||
|
@@ -220,6 +220,7 @@ def test_shutdown(client, monkeypatch):
|
||||
("post", "/api/v1/entry/1/archive"),
|
||||
("post", "/api/v1/entry/1/unarchive"),
|
||||
("post", "/api/v1/change-password"),
|
||||
("post", "/api/v1/vault/lock"),
|
||||
],
|
||||
)
|
||||
def test_invalid_token_other_endpoints(client, method, path):
|
||||
|
@@ -253,3 +253,25 @@ def test_vault_import_via_upload(client, tmp_path):
|
||||
assert res.status_code == 200
|
||||
assert res.json() == {"status": "ok"}
|
||||
assert isinstance(called.get("path"), Path)
|
||||
|
||||
|
||||
def test_vault_lock_endpoint(client):
|
||||
cl, token = client
|
||||
called = {}
|
||||
|
||||
def lock():
|
||||
called["locked"] = True
|
||||
api._pm.locked = True
|
||||
|
||||
api._pm.lock_vault = lock
|
||||
api._pm.locked = False
|
||||
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
res = cl.post("/api/v1/vault/lock", headers=headers)
|
||||
assert res.status_code == 200
|
||||
assert res.json() == {"status": "locked"}
|
||||
assert called.get("locked") is True
|
||||
assert api._pm.locked is True
|
||||
api._pm.unlock_vault = lambda: setattr(api._pm, "locked", False)
|
||||
api._pm.unlock_vault()
|
||||
assert api._pm.locked is False
|
||||
|
@@ -111,6 +111,23 @@ def test_vault_change_password(monkeypatch):
|
||||
assert called.get("called") is True
|
||||
|
||||
|
||||
def test_vault_lock(monkeypatch):
|
||||
called = {}
|
||||
|
||||
def lock():
|
||||
called["locked"] = True
|
||||
pm.locked = True
|
||||
|
||||
pm = SimpleNamespace(
|
||||
lock_vault=lock, locked=False, select_fingerprint=lambda fp: None
|
||||
)
|
||||
monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
|
||||
result = runner.invoke(app, ["vault", "lock"])
|
||||
assert result.exit_code == 0
|
||||
assert called.get("locked") is True
|
||||
assert pm.locked is True
|
||||
|
||||
|
||||
def test_vault_reveal_parent_seed(monkeypatch, tmp_path):
|
||||
called = {}
|
||||
|
||||
|
Reference in New Issue
Block a user