feat: add credential encryption key config and improve service token CLI display
- Add CREDENTIAL_ENCRYPTION_KEY environment variable with Fernet key generation guide - Improve service token issue command to display raw token once with warning - Refactor token list display function for better UX (new print_service_token_list) - Keep backward compatibility with print_service_token_table alias
This commit is contained in:
@@ -14,6 +14,10 @@ PORTFOLIOHUB_SESSION_TOKEN_TTL_HOURS=12
|
||||
PORTFOLIOHUB_CLIENT_TOKEN_DEFAULT_TTL_DAYS=365
|
||||
PORTFOLIOHUB_BOT_TOKEN_DEFAULT_TTL_DAYS=365
|
||||
|
||||
# Credential encryption key (Fernet, required when DB_ENABLED=true)
|
||||
# Generate: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
||||
CREDENTIAL_ENCRYPTION_KEY=
|
||||
|
||||
# TimescaleDB / PostgreSQL connection settings
|
||||
TIMESCALEDB_HOST=localhost
|
||||
TIMESCALEDB_PORT=5432
|
||||
|
||||
@@ -81,10 +81,15 @@ def issue_service_token(
|
||||
runtime_id=issued.auth_token.runtime_id,
|
||||
token=issued.token,
|
||||
)
|
||||
render_service_token_result(ctx.obj.console, ctx.obj.output_format, response)
|
||||
|
||||
if ctx.obj.output_format != "json":
|
||||
ctx.obj.console.print(f"masked_token: {mask_token_text(issued.token)}")
|
||||
# 발급 시 1회에 한해 원문 token 출력 (이후 마스킹된 값만 표시됨)
|
||||
if ctx.obj.output_format == "json":
|
||||
from app.cli.core.utils import render_output
|
||||
render_output(ctx.obj.console, "json", response.model_dump(mode="json"))
|
||||
else:
|
||||
ctx.obj.console.print("[bold yellow]⚠ 토큰 원문은 지금 1회만 표시됩니다. 안전한 곳에 저장하세요.[/bold yellow]")
|
||||
ctx.obj.console.print(f"[bold green]token:[/bold green] {issued.token}")
|
||||
render_service_token_result(ctx.obj.console, ctx.obj.output_format, response)
|
||||
|
||||
|
||||
@token_app.command("revoke")
|
||||
|
||||
+26
-20
@@ -163,26 +163,32 @@ def print_user_detail(console: Console, user: UserResponse) -> None:
|
||||
)
|
||||
|
||||
|
||||
def print_service_token_table(console: Console, tokens: list[AuthTokenResponse]) -> None:
|
||||
table = Table(title="Service Tokens", expand=True)
|
||||
table.add_column("Name", style="cyan", no_wrap=True)
|
||||
table.add_column("Permission", no_wrap=True)
|
||||
table.add_column("User ID", no_wrap=True)
|
||||
table.add_column("Prefix", no_wrap=True)
|
||||
table.add_column("Expires At", no_wrap=True)
|
||||
table.add_column("Revoked At", no_wrap=True)
|
||||
table.add_column("Created At", no_wrap=True)
|
||||
for token in tokens:
|
||||
table.add_row(
|
||||
token.name,
|
||||
token.permission,
|
||||
token.user_id,
|
||||
token.token_prefix,
|
||||
token.expires_at.isoformat(timespec="seconds") if token.expires_at else "never",
|
||||
token.revoked_at.isoformat(timespec="seconds") if token.revoked_at else "-",
|
||||
token.created_at.isoformat(timespec="seconds") if token.created_at else "N/A",
|
||||
)
|
||||
console.print(table)
|
||||
def print_service_token_list(console: Console, tokens: list[AuthTokenResponse]) -> None:
|
||||
for i, token in enumerate(tokens, 1):
|
||||
status = "[red]revoked[/red]" if token.revoked_at else "[green]active[/green]"
|
||||
expires = token.expires_at.isoformat(timespec="seconds") if token.expires_at else "never"
|
||||
last_used = token.last_used_at.isoformat(timespec="seconds") if token.last_used_at else "-"
|
||||
revoked_at = token.revoked_at.isoformat(timespec="seconds") if token.revoked_at else "-"
|
||||
created_at = token.created_at.isoformat(timespec="seconds") if token.created_at else "N/A"
|
||||
lines = [
|
||||
f"[bold cyan]{token.name}[/bold cyan] ({status})",
|
||||
f" token : {token.token_prefix}",
|
||||
f" id : {token.id}",
|
||||
f" user_id : {token.user_id}",
|
||||
f" perm : {token.permission}",
|
||||
f" expires : {expires}",
|
||||
f" last_used: {last_used}",
|
||||
f" created : {created_at}",
|
||||
]
|
||||
if token.revoked_at:
|
||||
lines.append(f" revoked : {revoked_at}")
|
||||
console.print("\n".join(lines))
|
||||
if i < len(tokens):
|
||||
console.print("[dim]─[/dim]" * 40)
|
||||
|
||||
|
||||
# Keep old name as alias for backward compatibility
|
||||
print_service_token_table = print_service_token_list
|
||||
|
||||
|
||||
def print_comment_table(console: Console, comments: list[CommentResponse]) -> None:
|
||||
|
||||
Reference in New Issue
Block a user