fix bug api key managemtn for admin
This commit is contained in:
@@ -11,7 +11,13 @@ from wtforms.validators import Optional
|
||||
from app.core.config import settings
|
||||
from app.db.engine import engine
|
||||
from app.db.models import ApiClient, ApiKey
|
||||
from app.security.api_key import generate_api_key, get_prefix, hash_api_key
|
||||
from app.security.api_key import (
|
||||
decrypt_api_key,
|
||||
encrypt_api_key,
|
||||
generate_api_key,
|
||||
get_prefix,
|
||||
hash_api_key,
|
||||
)
|
||||
|
||||
|
||||
class AdminAuth(AuthenticationBackend):
|
||||
@@ -38,45 +44,31 @@ class ApiClientAdmin(ModelView, model=ApiClient):
|
||||
|
||||
|
||||
class ApiKeyAdmin(ModelView, model=ApiKey):
|
||||
form_excluded_columns = [ApiKey.key_hash, ApiKey.key_prefix, ApiKey.created_at]
|
||||
column_list = [ApiKey.id, ApiKey.client_id, ApiKey.name, ApiKey.key_prefix, ApiKey.is_active, ApiKey.created_at]
|
||||
column_details_list = [ApiKey.id, ApiKey.client_id, ApiKey.name, ApiKey.key_prefix, ApiKey.permissions, ApiKey.is_active, ApiKey.created_at]
|
||||
details_template = "apikey_details.html"
|
||||
|
||||
form_extra_fields = {
|
||||
"permissions": TextAreaField(
|
||||
"Permissions (JSON Array)",
|
||||
validators=[Optional()],
|
||||
description='Example: ["feed.waiting-time:write", "feed.opd-checkpoint:write"]',
|
||||
render_kw={"placeholder": '["feed.waiting-time:write"]', "rows": 3}
|
||||
),
|
||||
form_excluded_columns = [ApiKey.key_hash, ApiKey.key_prefix, ApiKey.created_at, ApiKey.encrypted_key]
|
||||
column_list = [ApiKey.id, ApiKey.client_id, ApiKey.name, ApiKey.key_prefix, ApiKey.encrypted_key, ApiKey.is_active, ApiKey.created_at]
|
||||
column_details_list = [ApiKey.id, ApiKey.client_id, ApiKey.name, ApiKey.key_prefix, ApiKey.encrypted_key, ApiKey.permissions, ApiKey.is_active, ApiKey.created_at]
|
||||
edit_template = "apikey_edit.html"
|
||||
|
||||
column_formatters = {
|
||||
ApiKey.encrypted_key: lambda m, a: (m.encrypted_key[:16] + "...") if m.encrypted_key else "-"
|
||||
}
|
||||
|
||||
form_args = {
|
||||
"permissions": {
|
||||
"label": "Permissions (JSON Array)",
|
||||
"description": 'Example: ["feed.waiting-time:write", "feed.opd-checkpoint:write"]'
|
||||
}
|
||||
}
|
||||
|
||||
async def on_model_change(self, data: dict, model: ApiKey, is_created: bool, request: Request) -> None:
|
||||
import json
|
||||
|
||||
# Handle permissions from textarea (JSON format)
|
||||
permissions_str = data.get("permissions")
|
||||
if permissions_str:
|
||||
try:
|
||||
perms = json.loads(permissions_str)
|
||||
if isinstance(perms, list):
|
||||
model.permissions = perms
|
||||
else:
|
||||
raise ValueError("Permissions must be a JSON array")
|
||||
except (json.JSONDecodeError, ValueError) as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Invalid permissions format: {str(e)}. Use JSON array like: [\"feed.waiting-time:write\"]"
|
||||
)
|
||||
|
||||
# Auto-generate key for new records if not provided
|
||||
if is_created and not model.key_hash:
|
||||
plain_key = generate_api_key()
|
||||
model.key_prefix = get_prefix(plain_key)
|
||||
model.key_hash = hash_api_key(plain_key)
|
||||
model.encrypted_key = encrypt_api_key(plain_key)
|
||||
# Store in session for display after creation
|
||||
request.session[f"new_api_key_{model.client_id}"] = plain_key
|
||||
request.session[f"new_api_key_{model.id or 'new'}"] = plain_key
|
||||
|
||||
|
||||
def mount_admin(app):
|
||||
@@ -128,6 +120,7 @@ def mount_admin(app):
|
||||
name=name,
|
||||
key_prefix=get_prefix(plain_key),
|
||||
key_hash=hash_api_key(plain_key),
|
||||
encrypted_key=encrypt_api_key(plain_key),
|
||||
permissions=perms,
|
||||
is_active=True,
|
||||
)
|
||||
@@ -155,6 +148,7 @@ def mount_admin(app):
|
||||
plain_key = generate_api_key()
|
||||
api_key.key_prefix = get_prefix(plain_key)
|
||||
api_key.key_hash = hash_api_key(plain_key)
|
||||
api_key.encrypted_key = encrypt_api_key(plain_key)
|
||||
|
||||
db.commit()
|
||||
db.refresh(api_key)
|
||||
@@ -170,7 +164,7 @@ def mount_admin(app):
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@app.get("/admin/api-keys/{key_id}/view")
|
||||
@app.post("/admin/api-keys/{key_id}/view")
|
||||
async def _admin_view_api_key(request: Request, key_id: int):
|
||||
"""View API key from session (only works for newly created/regenerated keys)."""
|
||||
if not request.session.get("admin"):
|
||||
@@ -192,10 +186,20 @@ def mount_admin(app):
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
"api_key": plain_key,
|
||||
"key_prefix": api_key.key_prefix,
|
||||
"key_prefix": get_prefix(api_key.key_prefix),
|
||||
"message": "This is the only time you can view this key!"
|
||||
})
|
||||
else:
|
||||
# fallback to encrypted_key if exists
|
||||
if api_key.encrypted_key:
|
||||
decrypted = decrypt_api_key(api_key.encrypted_key)
|
||||
if decrypted:
|
||||
return JSONResponse({
|
||||
"success": True,
|
||||
"api_key": decrypted,
|
||||
"key_prefix": get_prefix(decrypted),
|
||||
"message": "Retrieved from encrypted storage."
|
||||
})
|
||||
return JSONResponse({
|
||||
"success": False,
|
||||
"message": "API key cannot be retrieved. Keys can only be viewed once after creation or regeneration."
|
||||
|
||||
Reference in New Issue
Block a user