Remove the reset subcommand that was broken and unnecessary.
This commit is contained in:
@@ -13,18 +13,14 @@ from paskia import globals as _globals
|
||||
from paskia.bootstrap import bootstrap_if_needed
|
||||
from paskia.config import PaskiaConfig
|
||||
from paskia.db.background import flush
|
||||
from paskia.fastapi import reset as reset_cmd
|
||||
from paskia.util import startupbox
|
||||
from paskia.util.hostutil import normalize_origin
|
||||
|
||||
DEFAULT_PORT = 4401
|
||||
|
||||
EPILOG = """\
|
||||
Examples:
|
||||
paskia # localhost:4401
|
||||
paskia -l :8080 # All interfaces, port 8080
|
||||
paskia -l /tmp/paskia.sock # Unix socket
|
||||
paskia reset [user] # Generate passkey reset link
|
||||
Example:
|
||||
paskia --rp-id example.com --rp-name "Example Corporation" --auth-host auth.example.com
|
||||
"""
|
||||
|
||||
|
||||
@@ -63,10 +59,7 @@ def add_common_options(p: argparse.ArgumentParser) -> None:
|
||||
)
|
||||
p.add_argument(
|
||||
"--auth-host",
|
||||
help=(
|
||||
"Dedicated host (optionally with scheme/port) to serve the auth UI at the root,"
|
||||
" e.g. auth.example.com or https://auth.example.com"
|
||||
),
|
||||
help=("Dedicated authentication site (optionally with scheme/port)"),
|
||||
)
|
||||
|
||||
|
||||
@@ -81,17 +74,6 @@ def main():
|
||||
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(
|
||||
"-l",
|
||||
"--listen",
|
||||
@@ -105,14 +87,6 @@ def main():
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Detect "reset" subcommand
|
||||
is_reset = args.command == "reset"
|
||||
|
||||
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)
|
||||
|
||||
@@ -218,10 +192,6 @@ def main():
|
||||
await bootstrap_if_needed()
|
||||
await flush()
|
||||
|
||||
if is_reset:
|
||||
exit_code = reset_cmd.run(args.reset_query)
|
||||
raise SystemExit(exit_code)
|
||||
|
||||
if len(endpoints) > 1:
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
for ep in endpoints:
|
||||
|
||||
@@ -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"]
|
||||
Reference in New Issue
Block a user