Loading capif_backend/backoffice_service/controllers/me_controller.py +8 −27 Changes for capif_backend/backoffice_service/controllers/me_controller.py: 8 added lines, 27 removed lines. Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ from flask import Blueprint, jsonify, request from flask_jwt_extended import jwt_required from ..core.operations import Operations from ..utils.utils import get_capif_token, get_capif_urls, get_capif_uuid from ..utils.utils import get_capif_token, get_capif_urls, capif_user_required from ..utils.vault import read_invoker, list_invokers, VaultError me_routes = Blueprint("me_routes", __name__) Loading @@ -10,15 +10,8 @@ operations = Operations() # create operations object -> operations.py @me_routes.route("/invoker", methods=["POST"]) @jwt_required() def onboard_invoker(): capif_token = get_capif_token() if not capif_token: return jsonify({"error": "This account has no CAPIF credentials."}), 403 user_uuid = get_capif_uuid() if not user_uuid: return jsonify({"error": "Could not determine the CAPIF user id for this account."}), 500 @capif_user_required def onboard_invoker(user_uuid): payload = request.get_json(silent=True) or {} invoker_info = payload.get("apiInvokerInformation") notification_destination = payload.get("notificationDestination") Loading @@ -27,20 +20,14 @@ def onboard_invoker(): ccf_onboarding_url = get_capif_urls()["ccf_onboarding_url"] data, status = operations.onboard_invoker( capif_token, user_uuid, ccf_onboarding_url, invoker_info, notification_destination get_capif_token(), user_uuid, ccf_onboarding_url, invoker_info, notification_destination ) return jsonify(data), status @me_routes.route("/invokers", methods=["GET"]) @jwt_required() def list_my_invokers(): if not get_capif_token(): return jsonify({"error": "This account has no CAPIF credentials."}), 403 user_uuid = get_capif_uuid() if not user_uuid: return jsonify({"error": "Could not determine the CAPIF user id for this account."}), 500 @capif_user_required def list_my_invokers(user_uuid): try: invoker_ids = list_invokers(user_uuid) except VaultError as e: Loading @@ -51,14 +38,8 @@ def list_my_invokers(): @me_routes.route("/invokers/<api_invoker_id>/apis", methods=["GET"]) @jwt_required() def discover_apis(api_invoker_id): if not get_capif_token(): return jsonify({"error": "This account has no CAPIF credentials"}), 403 user_uuid = get_capif_uuid() if not user_uuid: return jsonify({"error": "Could not determine the CAPIF user id for this account"}), 500 @capif_user_required def discover_apis(api_invoker_id, user_uuid): # Authorization by path design: the Vault path is keyed by the uuid from the JWT, # never from the URL. If a user passes another user's apiInvokerId, the lookup runs # under their own uuid, finds nothing, and returns 404. Do not flatten the Vault path Loading capif_backend/backoffice_service/utils/utils.py +16 −0 Changes for capif_backend/backoffice_service/utils/utils.py: 16 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import uuid import jwt from flask import request, jsonify from flask_jwt_extended import verify_jwt_in_request, get_jwt from functools import wraps def is_snake_case(value): """Verifica si una clave está en snake_case (solo letras minúsculas, números y guiones bajos).""" Loading Loading @@ -45,6 +46,21 @@ def get_capif_uuid(): """Return the CAPIF user uuid from the current JWT, or None for superadmin.""" return get_jwt().get("capif_uuid") def capif_user_required(fn): """Decorator for routes guard for user-scoped endpoints. Must be below @jwt_required(), because it read claims from the JWT. """ @wraps(fn) def wrapper(*args, **kwargs): if not get_capif_token(): return jsonify({"error": "This account has no CAPIF credentials."}), 403 user_uuid = get_capif_uuid() if not user_uuid: return jsonify({"error": "Could not determine the CAPIF user id for this account"}), 500 return fn(*args, user_uuid=user_uuid, **kwargs) return wrapper def get_capif_urls(): """CCF endpoints URLs. Static per deployment, so they are stored in environment variables.""" return { Loading Loading
capif_backend/backoffice_service/controllers/me_controller.py +8 −27 Changes for capif_backend/backoffice_service/controllers/me_controller.py: 8 added lines, 27 removed lines. Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ from flask import Blueprint, jsonify, request from flask_jwt_extended import jwt_required from ..core.operations import Operations from ..utils.utils import get_capif_token, get_capif_urls, get_capif_uuid from ..utils.utils import get_capif_token, get_capif_urls, capif_user_required from ..utils.vault import read_invoker, list_invokers, VaultError me_routes = Blueprint("me_routes", __name__) Loading @@ -10,15 +10,8 @@ operations = Operations() # create operations object -> operations.py @me_routes.route("/invoker", methods=["POST"]) @jwt_required() def onboard_invoker(): capif_token = get_capif_token() if not capif_token: return jsonify({"error": "This account has no CAPIF credentials."}), 403 user_uuid = get_capif_uuid() if not user_uuid: return jsonify({"error": "Could not determine the CAPIF user id for this account."}), 500 @capif_user_required def onboard_invoker(user_uuid): payload = request.get_json(silent=True) or {} invoker_info = payload.get("apiInvokerInformation") notification_destination = payload.get("notificationDestination") Loading @@ -27,20 +20,14 @@ def onboard_invoker(): ccf_onboarding_url = get_capif_urls()["ccf_onboarding_url"] data, status = operations.onboard_invoker( capif_token, user_uuid, ccf_onboarding_url, invoker_info, notification_destination get_capif_token(), user_uuid, ccf_onboarding_url, invoker_info, notification_destination ) return jsonify(data), status @me_routes.route("/invokers", methods=["GET"]) @jwt_required() def list_my_invokers(): if not get_capif_token(): return jsonify({"error": "This account has no CAPIF credentials."}), 403 user_uuid = get_capif_uuid() if not user_uuid: return jsonify({"error": "Could not determine the CAPIF user id for this account."}), 500 @capif_user_required def list_my_invokers(user_uuid): try: invoker_ids = list_invokers(user_uuid) except VaultError as e: Loading @@ -51,14 +38,8 @@ def list_my_invokers(): @me_routes.route("/invokers/<api_invoker_id>/apis", methods=["GET"]) @jwt_required() def discover_apis(api_invoker_id): if not get_capif_token(): return jsonify({"error": "This account has no CAPIF credentials"}), 403 user_uuid = get_capif_uuid() if not user_uuid: return jsonify({"error": "Could not determine the CAPIF user id for this account"}), 500 @capif_user_required def discover_apis(api_invoker_id, user_uuid): # Authorization by path design: the Vault path is keyed by the uuid from the JWT, # never from the URL. If a user passes another user's apiInvokerId, the lookup runs # under their own uuid, finds nothing, and returns 404. Do not flatten the Vault path Loading
capif_backend/backoffice_service/utils/utils.py +16 −0 Changes for capif_backend/backoffice_service/utils/utils.py: 16 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import uuid import jwt from flask import request, jsonify from flask_jwt_extended import verify_jwt_in_request, get_jwt from functools import wraps def is_snake_case(value): """Verifica si una clave está en snake_case (solo letras minúsculas, números y guiones bajos).""" Loading Loading @@ -45,6 +46,21 @@ def get_capif_uuid(): """Return the CAPIF user uuid from the current JWT, or None for superadmin.""" return get_jwt().get("capif_uuid") def capif_user_required(fn): """Decorator for routes guard for user-scoped endpoints. Must be below @jwt_required(), because it read claims from the JWT. """ @wraps(fn) def wrapper(*args, **kwargs): if not get_capif_token(): return jsonify({"error": "This account has no CAPIF credentials."}), 403 user_uuid = get_capif_uuid() if not user_uuid: return jsonify({"error": "Could not determine the CAPIF user id for this account"}), 500 return fn(*args, user_uuid=user_uuid, **kwargs) return wrapper def get_capif_urls(): """CCF endpoints URLs. Static per deployment, so they are stored in environment variables.""" return { Loading