Remove the reset subcommand that was broken and unnecessary.

This commit is contained in:
2026-02-05 21:53:30 +00:00
parent 6ad3aa7d8c
commit 8fc03ade04
2 changed files with 5 additions and 141 deletions
+5 -35
View File
@@ -13,18 +13,14 @@ from paskia import globals as _globals
from paskia.bootstrap import bootstrap_if_needed from paskia.bootstrap import bootstrap_if_needed
from paskia.config import PaskiaConfig from paskia.config import PaskiaConfig
from paskia.db.background import flush from paskia.db.background import flush
from paskia.fastapi import reset as reset_cmd
from paskia.util import startupbox from paskia.util import startupbox
from paskia.util.hostutil import normalize_origin from paskia.util.hostutil import normalize_origin
DEFAULT_PORT = 4401 DEFAULT_PORT = 4401
EPILOG = """\ EPILOG = """\
Examples: Example:
paskia # localhost:4401 paskia --rp-id example.com --rp-name "Example Corporation" --auth-host auth.example.com
paskia -l :8080 # All interfaces, port 8080
paskia -l /tmp/paskia.sock # Unix socket
paskia reset [user] # Generate passkey reset link
""" """
@@ -63,10 +59,7 @@ def add_common_options(p: argparse.ArgumentParser) -> None:
) )
p.add_argument( p.add_argument(
"--auth-host", "--auth-host",
help=( help=("Dedicated authentication site (optionally with scheme/port)"),
"Dedicated host (optionally with scheme/port) to serve the auth UI at the root,"
" e.g. auth.example.com or https://auth.example.com"
),
) )
@@ -81,17 +74,6 @@ def main():
epilog=EPILOG, epilog=EPILOG,
) )
# Subcommand for reset
parser.add_argument(
"command",
nargs="?",
help="Command: 'reset' for credential reset, or omit to run server",
)
parser.add_argument(
"reset_query",
nargs="?",
help="For 'reset' command: user UUID or substring of display name",
)
parser.add_argument( parser.add_argument(
"-l", "-l",
"--listen", "--listen",
@@ -105,16 +87,8 @@ def main():
args = parser.parse_args() args = parser.parse_args()
# Detect "reset" subcommand # Parse endpoint using fastapi_vue.hostutil
is_reset = args.command == "reset" endpoints = parse_endpoint(args.listen, DEFAULT_PORT)
if is_reset:
endpoints = []
else:
if args.command is not None:
raise SystemExit(f"Unknown command: {args.command}")
# Parse endpoint using fastapi_vue.hostutil
endpoints = parse_endpoint(args.listen, DEFAULT_PORT)
# Extract host/port/uds from first endpoint for config display and site_url # Extract host/port/uds from first endpoint for config display and site_url
ep = endpoints[0] if endpoints else {} ep = endpoints[0] if endpoints else {}
@@ -218,10 +192,6 @@ def main():
await bootstrap_if_needed() await bootstrap_if_needed()
await flush() await flush()
if is_reset:
exit_code = reset_cmd.run(args.reset_query)
raise SystemExit(exit_code)
if len(endpoints) > 1: if len(endpoints) > 1:
async with asyncio.TaskGroup() as tg: async with asyncio.TaskGroup() as tg:
for ep in endpoints: for ep in endpoints:
-106
View File
@@ -1,106 +0,0 @@
"""CLI support for creating user credential reset links.
Usage (via main CLI):
paskia reset [query]
If query is omitted, the master admin (first Administration role user in
an organization granting auth:admin) is targeted. Otherwise query is
matched as either an exact UUID or a case-insensitive substring of the
display name. If multiple users match, they are listed and the command
aborts. A new one-time reset link is always created.
"""
import asyncio
from uuid import UUID
from paskia import authsession as _authsession
from paskia import db
from paskia.util import hostutil
async def _resolve_targets(query: str | None):
if query:
# Try UUID
targets: list[tuple] = []
try:
q_uuid = UUID(query)
p = next(
(p for p in db.data().permissions.values() if p.scope == "auth:admin"),
None,
)
if p:
for org_uuid in p.orgs:
users = db.get_organization_users(org_uuid)
for u, role_name in users:
if u.uuid == q_uuid:
return [(u, role_name)]
# UUID not found among admin orgs -> fall back to substring search (rare case)
except ValueError:
pass
# Substring search
needle = query.lower()
p = next(
(p for p in db.data().permissions.values() if p.scope == "auth:admin"), None
)
if p:
for org_uuid in p.orgs:
users = db.get_organization_users(org_uuid)
for u, role_name in users:
if needle in (u.display_name or "").lower():
targets.append((u, role_name))
# De-duplicate
seen = set()
deduped = []
for u, role_name in targets:
if u.uuid not in seen:
seen.add(u.uuid)
deduped.append((u, role_name))
return deduped
# No query -> master admin
p = next(
(p for p in db.data().permissions.values() if p.scope == "auth:admin"), None
)
if not p or not p.orgs:
return []
first_org_uuid = next(iter(p.orgs))
users = db.get_organization_users(first_org_uuid)
admin_users = [pair for pair in users if pair[1] == "Administration"]
return admin_users[:1]
async def _create_reset(user, role_name: str):
expiry = _authsession.reset_expires()
token = db.create_reset_token(
user_uuid=user.uuid,
expiry=expiry,
token_type="manual reset",
)
return hostutil.reset_link_url(token), token
async def _main(query: str | None) -> int:
try:
candidates = await _resolve_targets(query)
if not candidates:
print("No matching users found")
return 1
if len(candidates) > 1:
print("Multiple matches. Refine your query:")
for u, role_name in candidates:
print(f" - {u.display_name} ({u.uuid}) role={role_name}")
return 2
user, role_name = candidates[0]
link, token = await _create_reset(user, role_name)
print(f"Reset link for {user.display_name} ({user.uuid}):\n{link}\n")
return 0
except Exception as e: # pragma: no cover
print("Failed to create reset link:", e)
return 1
def run(query: str | None) -> int:
"""Synchronous wrapper for CLI entrypoint."""
return asyncio.run(_main(query))
__all__ = ["run"]