From b31480bf25fdc7eb726d3641ccba9a0cdc44323e Mon Sep 17 00:00:00 2001 From: hzhang Date: Sun, 17 May 2026 15:06:17 +0100 Subject: [PATCH] feat: add 'agent' API key role (content CRUD + backup) - ALLOWED_API_KEY_ROLES (+ apikey_cli ALLOWED_ROLES) gain 'agent'. - 'agent' added to require_auth on markdown/patch/path create/update/ delete/move and backup get/load. apikey mint, /backup/convert, logs, config, webhook and permission/template settings stay admin-only. Co-Authored-By: Claude Opus 4.7 (1M context) --- api/apikey/__init__.py | 2 +- api/backup.py | 4 ++-- api/markdown.py | 10 +++++----- api/patch.py | 6 +++--- api/path.py | 12 ++++++------ apikey_cli.py | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/api/apikey/__init__.py b/api/apikey/__init__.py index 688ae91..fdae571 100644 --- a/api/apikey/__init__.py +++ b/api/apikey/__init__.py @@ -9,7 +9,7 @@ api_key_bp = Blueprint('apikey', __name__, url_prefix='/api/apikey') # An API key must never be able to request a role broader than what the # product defines, regardless of what the request body asks for. -ALLOWED_API_KEY_ROLES = {'admin', 'creator', 'user'} +ALLOWED_API_KEY_ROLES = {'admin', 'creator', 'user', 'agent'} # Validity window applied on create and on every renewal. KEY_TTL = timedelta(days=15) diff --git a/api/backup.py b/api/backup.py index ccaac4a..7165584 100644 --- a/api/backup.py +++ b/api/backup.py @@ -346,7 +346,7 @@ def convert_backup_endpoint(): backup_lock = threading.Lock() @backup_bp.route('/', methods=['GET']) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) def get_backup(): """ Create a backup of the application's data. @@ -558,7 +558,7 @@ def traverse(path_id, paths): @backup_bp.route('/load', methods=['POST']) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) def load_backup(): """ Restore data from a backup file. diff --git a/api/markdown.py b/api/markdown.py index d9a7a16..0b10559 100644 --- a/api/markdown.py +++ b/api/markdown.py @@ -194,7 +194,7 @@ def get_markdown(markdown_id): return jsonify(markdown.to_dict()), 200 @markdown_bp.route('/', methods=['POST']) -@require_auth(roles=['admin', 'creator']) +@require_auth(roles=['admin', 'creator', 'agent']) @limiter.limit(api.get_rate_limit) def create_markdown(): """ @@ -250,7 +250,7 @@ def create_markdown(): return jsonify({"error": f"create failed - {errno}"}), 500 @markdown_bp.route('/', methods=['PUT', 'PATCH']) -@require_auth(roles=['admin', 'creator']) +@require_auth(roles=['admin', 'creator', 'agent']) @limiter.limit(api.get_rate_limit) def update_markdown(markdown_id): """ @@ -315,7 +315,7 @@ def update_markdown(markdown_id): return jsonify(markdown.to_dict()), 200 @markdown_bp.route('/', methods=['DELETE']) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) @limiter.limit(api.get_rate_limit) def delete_markdown(markdown_id): """ @@ -391,7 +391,7 @@ def delete_markdown(markdown_id): @markdown_bp.route('/move_forward/', methods=['PATCH']) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) @limiter.limit(api.get_rate_limit) def move_forward(markdown_id): """ @@ -428,7 +428,7 @@ def move_forward(markdown_id): @markdown_bp.route('/move_backward/', methods=['PATCH']) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) @limiter.limit(api.get_rate_limit) def move_backward(markdown_id): """ diff --git a/api/patch.py b/api/patch.py index 569add2..0650fbf 100644 --- a/api/patch.py +++ b/api/patch.py @@ -51,7 +51,7 @@ def get_patches(markdown_id): @patch_bp.route('/', methods=['POST']) -@require_auth(roles=['admin', 'creator']) +@require_auth(roles=['admin', 'creator', 'agent']) @limiter.limit(api.get_rate_limit) def create_patch(): """Create a patch card. Body: markdown_id, content, title?, order?""" @@ -89,7 +89,7 @@ def create_patch(): @patch_bp.route('/', methods=['PUT', 'PATCH']) -@require_auth(roles=['admin', 'creator']) +@require_auth(roles=['admin', 'creator', 'agent']) @limiter.limit(api.get_rate_limit) def update_patch(patch_id): """Update a patch card (title/content/order).""" @@ -118,7 +118,7 @@ def update_patch(patch_id): @patch_bp.route('/', methods=['DELETE']) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) @limiter.limit(api.get_rate_limit) def delete_patch(patch_id): """Delete a patch card.""" diff --git a/api/path.py b/api/path.py index f76b3e5..7bcb79c 100644 --- a/api/path.py +++ b/api/path.py @@ -82,7 +82,7 @@ def get_path_by_parent(parent_id): @path_bp.route('/', methods=['POST']) @limiter.limit(api.get_rate_limit) -@require_auth(roles=['admin', 'creator']) +@require_auth(roles=['admin', 'creator', 'agent']) def create_path(): """ Create a new path. @@ -119,7 +119,7 @@ def create_path(): @path_bp.route('/', methods=['PUT']) @limiter.limit(api.get_rate_limit) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) def update_path(path_id): """ Update a path. @@ -158,7 +158,7 @@ def update_path(path_id): @path_bp.route('/', methods=['PATCH']) @limiter.limit(api.get_rate_limit) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) def patch_path(path_id): """ Partially update a path. @@ -205,7 +205,7 @@ def patch_path(path_id): @path_bp.route('/', methods=['DELETE']) @limiter.limit(api.get_rate_limit) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) def delete_path(path_id): """ Delete a path. @@ -240,7 +240,7 @@ def delete_path(path_id): @path_bp.route('/move_forward/', methods=['PATCH']) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) @limiter.limit(api.get_rate_limit) def move_forward(path_id): """ @@ -277,7 +277,7 @@ def move_forward(path_id): @path_bp.route('/move_backward/', methods=['PATCH']) -@require_auth(roles=['admin']) +@require_auth(roles=['admin', 'agent']) @limiter.limit(api.get_rate_limit) def move_backward(path_id): """ diff --git a/apikey_cli.py b/apikey_cli.py index 619cb6d..ea8cafd 100644 --- a/apikey_cli.py +++ b/apikey_cli.py @@ -24,7 +24,7 @@ from db import get_db from db.models.APIKey import APIKey # Keep in sync with api.apikey.ALLOWED_API_KEY_ROLES -ALLOWED_ROLES = {"admin", "creator", "user"} +ALLOWED_ROLES = {"admin", "creator", "user", "agent"} KEY_TTL_DEFAULT_DAYS = 15