2026-09-07 02:23:33 +08:00
|
|
|
|
"""开发联调 JWT 签发 CLI(T-01 · 仅 development;生产由 IdP 签 RS256)。
|
|
|
|
|
|
|
|
|
|
|
|
用法(Git Bash / PowerShell):
|
|
|
|
|
|
python scripts/dev/issue_dev_token.py --sub STAFF-30001 --roles risk_officer
|
|
|
|
|
|
python scripts/dev/issue_dev_token.py --sub CUST-9527 --roles customer \\
|
|
|
|
|
|
--token-type customer --customer-id CUST-9527
|
|
|
|
|
|
|
|
|
|
|
|
签发后经 `Authorization: Bearer <token>` 调用,JWT 通道须带 `X-Agent-Type`
|
|
|
|
|
|
(手册 §4.6)。HS256 密钥取 .env JWT_DEV_SECRET;APP_ENV != development 时
|
|
|
|
|
|
拒绝签发(防止对称密钥流出)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
|
|
import sys
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
|
|
|
|
|
|
|
|
|
|
|
from app.config.settings import settings # noqa: E402
|
|
|
|
|
|
from app.service.auth_service import issue_dev_token # noqa: E402
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> int:
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="issue a development JWT (HS256)")
|
|
|
|
|
|
parser.add_argument("--sub", required=True, help="staff_id 或 customer_id(手册 §3)")
|
|
|
|
|
|
parser.add_argument("--roles", required=True, help="逗号分隔角色,如 risk_officer / customer")
|
|
|
|
|
|
parser.add_argument("--token-type", default="staff", choices=["customer", "staff", "service"])
|
|
|
|
|
|
parser.add_argument("--customer-id", default=None, help="token_type=customer 时必填且须等于 sub")
|
2026-09-07 03:03:12 +08:00
|
|
|
|
parser.add_argument("--exp-minutes", type=int, default=60, help="有效期(分钟),上限 480(手册 §11 员工 ≤8h)")
|
2026-09-07 02:23:33 +08:00
|
|
|
|
parser.add_argument("--tenant-id", default="TENANT-001")
|
2026-09-07 03:03:12 +08:00
|
|
|
|
parser.add_argument("--permissions", default=None, help="逗号分隔细粒度权限,如 risk:alert:write,audit:write")
|
2026-09-07 02:23:33 +08:00
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
if settings.app_env != "development":
|
|
|
|
|
|
print("refused: issue_dev_token only runs with APP_ENV=development", file=sys.stderr)
|
|
|
|
|
|
return 1
|
2026-09-07 03:03:12 +08:00
|
|
|
|
if args.exp_minutes < 1 or args.exp_minutes > 480:
|
|
|
|
|
|
print("refused: --exp-minutes must be in [1, 480]", file=sys.stderr)
|
|
|
|
|
|
return 1
|
2026-09-07 02:23:33 +08:00
|
|
|
|
roles = [r.strip() for r in args.roles.split(",") if r.strip()]
|
2026-09-07 03:03:12 +08:00
|
|
|
|
permissions = (
|
|
|
|
|
|
[p.strip() for p in args.permissions.split(",") if p.strip()] if args.permissions else None
|
|
|
|
|
|
)
|
2026-09-07 02:23:33 +08:00
|
|
|
|
if args.token_type == "customer" and args.customer_id != args.sub:
|
|
|
|
|
|
print("refused: customer token requires --customer-id equal to --sub (手册 §4.2)", file=sys.stderr)
|
|
|
|
|
|
return 1
|
|
|
|
|
|
token = issue_dev_token(
|
|
|
|
|
|
sub=args.sub,
|
|
|
|
|
|
roles=roles,
|
|
|
|
|
|
token_type=args.token_type,
|
|
|
|
|
|
exp_minutes=args.exp_minutes,
|
|
|
|
|
|
tenant_id=args.tenant_id,
|
|
|
|
|
|
customer_id=args.customer_id,
|
2026-09-07 03:03:12 +08:00
|
|
|
|
permissions=permissions,
|
2026-09-07 02:23:33 +08:00
|
|
|
|
)
|
|
|
|
|
|
print(token)
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
raise SystemExit(main())
|