feat: add MCP research access and browser key management
This commit is contained in:
+50
-6
@@ -4,11 +4,11 @@ import argparse
|
||||
import asyncio
|
||||
import getpass
|
||||
|
||||
from sqlalchemy import delete
|
||||
from sqlalchemy import delete, update
|
||||
|
||||
from .config import Settings
|
||||
from .db import create_database
|
||||
from .models import Admin, LoginSession
|
||||
from .models import Admin, LoginSession, MCPToken, now
|
||||
from .security import password_hasher
|
||||
|
||||
|
||||
@@ -21,13 +21,57 @@ async def reset_password():
|
||||
admin = await db.get(Admin, 1)
|
||||
admin.password_hash = password_hasher.hash(password)
|
||||
await db.execute(delete(LoginSession))
|
||||
await db.execute(update(MCPToken).where(MCPToken.revoked_at.is_(None)).values(revoked_at=now()))
|
||||
await db.commit()
|
||||
await engine.dispose()
|
||||
print("Admin password updated; all system sessions revoked.")
|
||||
print("Admin password updated; all system sessions and MCP tokens revoked.")
|
||||
|
||||
|
||||
async def token_command(args):
|
||||
import json
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from .mcp_api.auth import create_token
|
||||
from .models import MCPToken, now
|
||||
from .research.serialization import encode_snapshot
|
||||
|
||||
engine, sessions = create_database(Settings().database_url)
|
||||
try:
|
||||
async with sessions.begin() as db:
|
||||
if args.command == "mcp-token-create":
|
||||
row, secret = await create_token(db, args.name, args.scope, args.days)
|
||||
result = {"id": row.id, "name": row.name, "scopes": row.scopes,
|
||||
"expires_at": row.expires_at, "token": secret}
|
||||
elif args.command == "mcp-token-revoke":
|
||||
row = await db.get(MCPToken, args.token_id)
|
||||
if not row:
|
||||
raise ValueError("令牌不存在")
|
||||
row.revoked_at = row.revoked_at or now()
|
||||
result = {"id": row.id, "revoked": True}
|
||||
else:
|
||||
rows = list(await db.scalars(select(MCPToken).order_by(MCPToken.created_at.desc())))
|
||||
result = [{k: getattr(row, k) for k in
|
||||
("id", "name", "scopes", "created_at", "expires_at", "revoked_at")} for row in rows]
|
||||
# Reveal only after the transaction has committed successfully.
|
||||
print(json.dumps(encode_snapshot(result), ensure_ascii=False, indent=2))
|
||||
finally:
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("command", choices=["reset-password"])
|
||||
parser.parse_args()
|
||||
asyncio.run(reset_password())
|
||||
commands = parser.add_subparsers(dest="command", required=True)
|
||||
commands.add_parser("reset-password")
|
||||
create = commands.add_parser("mcp-token-create")
|
||||
create.add_argument("--name", required=True)
|
||||
create.add_argument("--scope", action="append", default=None)
|
||||
create.add_argument("--days", type=int, default=90)
|
||||
commands.add_parser("mcp-token-list")
|
||||
revoke = commands.add_parser("mcp-token-revoke")
|
||||
revoke.add_argument("token_id")
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
asyncio.run(reset_password() if args.command == "reset-password" else token_command(args))
|
||||
except ValueError as exc:
|
||||
parser.error(str(exc))
|
||||
|
||||
Reference in New Issue
Block a user