mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 23:38:49 +00:00
Add totp codes command and endpoint
This commit is contained in:
@@ -14,6 +14,7 @@ import sys
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from password_manager.manager import PasswordManager
|
||||
from password_manager.entry_types import EntryType
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@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")
|
||||
def get_nostr_pubkey(authorization: str | None = Header(None)) -> Any:
|
||||
_check_token(authorization)
|
||||
|
@@ -315,6 +315,13 @@ def entry_unarchive(ctx: typer.Context, entry_id: int) -> None:
|
||||
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")
|
||||
def entry_export_totp(
|
||||
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"]}
|
||||
|
||||
|
||||
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):
|
||||
cl, token = client
|
||||
calls = {}
|
||||
|
@@ -373,6 +373,19 @@ def test_entry_export_totp(monkeypatch, tmp_path):
|
||||
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):
|
||||
called = {}
|
||||
|
||||
|
Reference in New Issue
Block a user