mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
Merge pull request #439 from PR0M3TH3AN/codex/add-totp-codes-command-and-api-endpoint
Add totp codes display
This commit is contained in:
@@ -50,6 +50,7 @@ SeedPass now uses the `portalocker` library for cross-platform file locking. No
|
|||||||
- **SeedPass 2FA:** Generate TOTP codes with a real-time countdown progress bar.
|
- **SeedPass 2FA:** Generate TOTP codes with a real-time countdown progress bar.
|
||||||
- **2FA Secret Issuance & Import:** Derive new TOTP secrets from your seed or import existing `otpauth://` URIs.
|
- **2FA Secret Issuance & Import:** Derive new TOTP secrets from your seed or import existing `otpauth://` URIs.
|
||||||
- **Export 2FA Codes:** Save all stored TOTP entries to an encrypted JSON file for use with other apps.
|
- **Export 2FA Codes:** Save all stored TOTP entries to an encrypted JSON file for use with other apps.
|
||||||
|
- **Display TOTP Codes:** Show all active 2FA codes with a countdown timer.
|
||||||
- **Optional External Backup Location:** Configure a second directory where backups are automatically copied.
|
- **Optional External Backup Location:** Configure a second directory where backups are automatically copied.
|
||||||
- **Auto‑Lock on Inactivity:** Vault locks after a configurable timeout for additional security.
|
- **Auto‑Lock on Inactivity:** Vault locks after a configurable timeout for additional security.
|
||||||
- **Secret Mode:** Copy retrieved passwords directly to your clipboard and automatically clear it after a delay.
|
- **Secret Mode:** Copy retrieved passwords directly to your clipboard and automatically clear it after a delay.
|
||||||
|
@@ -14,6 +14,12 @@ $ seedpass entry get "email"
|
|||||||
Code: 123456
|
Code: 123456
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To show all stored TOTP codes with their countdown timers, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ seedpass entry totp-codes
|
||||||
|
```
|
||||||
|
|
||||||
## CLI and API Reference
|
## CLI and API Reference
|
||||||
|
|
||||||
See [advanced_cli.md](advanced_cli.md) for a list of command examples. Detailed information about the REST API is available in [api_reference.md](api_reference.md). When starting the API, set `SEEDPASS_CORS_ORIGINS` if you need to allow requests from specific web origins.
|
See [advanced_cli.md](advanced_cli.md) for a list of command examples. Detailed information about the REST API is available in [api_reference.md](api_reference.md). When starting the API, set `SEEDPASS_CORS_ORIGINS` if you need to allow requests from specific web origins.
|
||||||
|
@@ -61,6 +61,7 @@ Manage individual entries within a vault.
|
|||||||
| Archive an entry | `entry archive` | `seedpass entry archive 1` |
|
| Archive an entry | `entry archive` | `seedpass entry archive 1` |
|
||||||
| Unarchive an entry | `entry unarchive` | `seedpass entry unarchive 1` |
|
| Unarchive an entry | `entry unarchive` | `seedpass entry unarchive 1` |
|
||||||
| Export all TOTP secrets | `entry export-totp` | `seedpass entry export-totp --file totp.json` |
|
| Export all TOTP secrets | `entry export-totp` | `seedpass entry export-totp --file totp.json` |
|
||||||
|
| Show all TOTP codes | `entry totp-codes` | `seedpass entry totp-codes` |
|
||||||
|
|
||||||
### Vault Commands
|
### Vault Commands
|
||||||
|
|
||||||
@@ -141,6 +142,7 @@ Run or stop the local HTTP API.
|
|||||||
- **`seedpass entry archive <id>`** – Mark an entry as archived so it is hidden from normal lists.
|
- **`seedpass entry archive <id>`** – Mark an entry as archived so it is hidden from normal lists.
|
||||||
- **`seedpass entry unarchive <id>`** – Restore an archived entry.
|
- **`seedpass entry unarchive <id>`** – Restore an archived entry.
|
||||||
- **`seedpass entry export-totp --file <path>`** – Export all stored TOTP secrets to a JSON file.
|
- **`seedpass entry export-totp --file <path>`** – Export all stored TOTP secrets to a JSON file.
|
||||||
|
- **`seedpass entry totp-codes`** – Display all current TOTP codes with remaining time.
|
||||||
|
|
||||||
Example retrieving a TOTP code:
|
Example retrieving a TOTP code:
|
||||||
|
|
||||||
|
@@ -28,6 +28,7 @@ Keep this token secret. Every request must include it in the `Authorization` hea
|
|||||||
- `DELETE /api/v1/fingerprint/{fp}` – Remove a fingerprint.
|
- `DELETE /api/v1/fingerprint/{fp}` – Remove a fingerprint.
|
||||||
- `POST /api/v1/fingerprint/select` – Switch the active fingerprint.
|
- `POST /api/v1/fingerprint/select` – Switch the active fingerprint.
|
||||||
- `GET /api/v1/totp/export` – Export all TOTP entries as JSON.
|
- `GET /api/v1/totp/export` – Export all TOTP entries as JSON.
|
||||||
|
- `GET /api/v1/totp` – Return current TOTP codes and remaining time.
|
||||||
- `GET /api/v1/nostr/pubkey` – Fetch the Nostr public key for the active seed.
|
- `GET /api/v1/nostr/pubkey` – Fetch the Nostr public key for the active seed.
|
||||||
- `POST /api/v1/checksum/verify` – Verify the checksum of the running script.
|
- `POST /api/v1/checksum/verify` – Verify the checksum of the running script.
|
||||||
- `POST /api/v1/checksum/update` – Update the stored script checksum.
|
- `POST /api/v1/checksum/update` – Update the stored script checksum.
|
||||||
|
@@ -14,6 +14,7 @@ import sys
|
|||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
from password_manager.manager import PasswordManager
|
from password_manager.manager import PasswordManager
|
||||||
|
from password_manager.entry_types import EntryType
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
@@ -314,6 +315,24 @@ def export_totp(authorization: str | None = Header(None)) -> dict:
|
|||||||
return _pm.entry_manager.export_totp_entries(_pm.parent_seed)
|
return _pm.entry_manager.export_totp_entries(_pm.parent_seed)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/v1/totp")
|
||||||
|
def get_totp_codes(authorization: str | None = Header(None)) -> dict:
|
||||||
|
"""Return active TOTP codes with remaining seconds."""
|
||||||
|
_check_token(authorization)
|
||||||
|
assert _pm is not None
|
||||||
|
entries = _pm.entry_manager.list_entries(
|
||||||
|
filter_kind=EntryType.TOTP.value, include_archived=False
|
||||||
|
)
|
||||||
|
codes = []
|
||||||
|
for idx, label, _u, _url, _arch in entries:
|
||||||
|
code = _pm.entry_manager.get_totp_code(idx, _pm.parent_seed)
|
||||||
|
rem = _pm.entry_manager.get_totp_time_remaining(idx)
|
||||||
|
codes.append(
|
||||||
|
{"id": idx, "label": label, "code": code, "seconds_remaining": rem}
|
||||||
|
)
|
||||||
|
return {"codes": codes}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/v1/nostr/pubkey")
|
@app.get("/api/v1/nostr/pubkey")
|
||||||
def get_nostr_pubkey(authorization: str | None = Header(None)) -> Any:
|
def get_nostr_pubkey(authorization: str | None = Header(None)) -> Any:
|
||||||
_check_token(authorization)
|
_check_token(authorization)
|
||||||
|
@@ -315,6 +315,13 @@ def entry_unarchive(ctx: typer.Context, entry_id: int) -> None:
|
|||||||
typer.echo(str(entry_id))
|
typer.echo(str(entry_id))
|
||||||
|
|
||||||
|
|
||||||
|
@entry_app.command("totp-codes")
|
||||||
|
def entry_totp_codes(ctx: typer.Context) -> None:
|
||||||
|
"""Display all current TOTP codes."""
|
||||||
|
pm = _get_pm(ctx)
|
||||||
|
pm.handle_display_totp_codes()
|
||||||
|
|
||||||
|
|
||||||
@entry_app.command("export-totp")
|
@entry_app.command("export-totp")
|
||||||
def entry_export_totp(
|
def entry_export_totp(
|
||||||
ctx: typer.Context, file: str = typer.Option(..., help="Output file")
|
ctx: typer.Context, file: str = typer.Option(..., help="Output file")
|
||||||
|
@@ -122,6 +122,22 @@ def test_totp_export_endpoint(client):
|
|||||||
assert res.json() == {"entries": ["x"]}
|
assert res.json() == {"entries": ["x"]}
|
||||||
|
|
||||||
|
|
||||||
|
def test_totp_codes_endpoint(client):
|
||||||
|
cl, token = client
|
||||||
|
api._pm.entry_manager.list_entries = lambda **kw: [(0, "Email", None, None, False)]
|
||||||
|
api._pm.entry_manager.get_totp_code = lambda i, s: "123456"
|
||||||
|
api._pm.entry_manager.get_totp_time_remaining = lambda i: 30
|
||||||
|
api._pm.parent_seed = "seed"
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
res = cl.get("/api/v1/totp", headers=headers)
|
||||||
|
assert res.status_code == 200
|
||||||
|
assert res.json() == {
|
||||||
|
"codes": [
|
||||||
|
{"id": 0, "label": "Email", "code": "123456", "seconds_remaining": 30}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_fingerprint_endpoints(client):
|
def test_fingerprint_endpoints(client):
|
||||||
cl, token = client
|
cl, token = client
|
||||||
calls = {}
|
calls = {}
|
||||||
|
@@ -373,6 +373,19 @@ def test_entry_export_totp(monkeypatch, tmp_path):
|
|||||||
assert called.get("called") is True
|
assert called.get("called") is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_entry_totp_codes(monkeypatch):
|
||||||
|
called = {}
|
||||||
|
|
||||||
|
pm = SimpleNamespace(
|
||||||
|
handle_display_totp_codes=lambda: called.setdefault("called", True),
|
||||||
|
select_fingerprint=lambda fp: None,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
|
||||||
|
result = runner.invoke(app, ["entry", "totp-codes"])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert called.get("called") is True
|
||||||
|
|
||||||
|
|
||||||
def test_verify_checksum_command(monkeypatch):
|
def test_verify_checksum_command(monkeypatch):
|
||||||
called = {}
|
called = {}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user