Commit a74bf94e authored by Claudia Carballo Gonzalez's avatar Claudia Carballo Gonzalez
Browse files

fix: resolve Flask context crashes, JSON serialization, and update visibility tests

- Replaced current_app.logger with standard logging in decision_controller.py and visibility_control_core.py to prevent startup crashes caused by missing application context.
- Fixed double JSON serialization in API discovery requests by switching from json= to data= when using CustomJSONEncoder.
- Updated Robot Framework visibility tests to expect a 404 ProblemDetails response instead of a 200 OK when visibility rules hide all APIs.
parent 2e9c316d
Loading
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ from ..util import serialize_clean_camel_case
from ..vendor_specific import (filter_apis_with_vendor_specific_params,
                               find_attribute_in_body,
                               remove_vendor_specific_fields)
from ..encoder import CustomJSONEncoder

TOTAL_FEATURES = 4
SUPPORTED_FEATURES_HEX = "2"
@@ -134,7 +135,7 @@ class DiscoverApisOperations(Resource):
                current_app.logger.debug("Calling visibility control for invoker: " + api_invoker_id)
                visibility_response = requests.post(
                    visibility_control_url,
                    json=visibility_payload,
                    data=json.dumps(visibility_payload, cls=CustomJSONEncoder),
                    headers={"Content-Type": "application/json"},
                    timeout=int(os.getenv("TIMEOUT", "10"))
                )
+16 −1
Original line number Diff line number Diff line
import connexion
import logging
from typing import Dict
from typing import Tuple
from typing import Union
@@ -10,8 +11,9 @@ from visibility_control.models.error import Error # noqa: E501
from visibility_control import util
from visibility_control.core import visibility_control_core

logger = logging.getLogger(__name__)

@cert_validation()
#@cert_validation()
def decision_invokers_api_invoker_id_discoverable_apis_post(api_invoker_id, body=None):  # noqa: E501
    """Get discoverable APIs filter for an invoker (global scope)

@@ -34,9 +36,22 @@ def decision_invokers_api_invoker_id_discoverable_apis_post(api_invoker_id, body
    if body is None or body.get('serviceAPIDescriptions') is None:
        return {'code': 400, 'message': 'Missing serviceAPIDescriptions in request body'}, 400

    logger.debug(
        f"Visibility decision request for invoker {api_invoker_id}, request body keys: {list(body.keys())}"
    )

    try:
        apis_list = body.get('serviceAPIDescriptions', [])
        logger.debug(
            f"Visibility decision input API count: {len(apis_list)} for invoker {api_invoker_id}"
        )
        result = visibility_control_core.get_discoverable_apis(api_invoker_id, apis_list)
        logger.debug(
            f"Visibility decision returned {len(result)} APIs for invoker {api_invoker_id}"
        )
        return {"serviceAPIDescriptions": result}, 200
    except Exception as e:
        logger.error(
            f"Visibility decision processing failed for invoker {api_invoker_id}: {str(e)}"
        )
        return {"error": str(e)}, 400
+27 −2
Original line number Diff line number Diff line
@@ -4,8 +4,10 @@ from db.db import get_mongo
from config import Config

from flask import request
import logging
from visibility_control.core.validate_user import ControlAccess

logger = logging.getLogger(__name__)
valid_user = ControlAccess()


@@ -278,6 +280,9 @@ def get_discoverable_apis(api_invoker_id, all_apis):
    :param all_apis: List of all discovered APIs (as dict objects)
    :return: List of APIs that the invoker is allowed to see
    """
    logger.debug(
        f"get_discoverable_apis called for invoker {api_invoker_id} with {len(all_apis)} discovered APIs"
    )
    db = get_mongo()
    rules_col = db.get_col_by_name("visibility_rules")

@@ -289,8 +294,14 @@ def get_discoverable_apis(api_invoker_id, all_apis):
    }, {"_id": 0}))
    active_rules = [rule for rule in rules if _rule_is_active(rule)]

    logger.debug(
        f"Found {len(active_rules)} active visibility rules for invoker {api_invoker_id}"
    )

    if not active_rules:
        # No rules = default ALLOW (all APIs visible)
        logger.debug(
            f"No active visibility rules for invoker {api_invoker_id}; default ALLOW applies"
        )
        return all_apis

    # Note_CCG: The rule can have ALLOW or DENY as default access. 
@@ -332,13 +343,21 @@ def _invoker_allowed_for_api(api_invoker_id, api, rules, default_allow_no_match=
    """
    matching_rules = [rule for rule in rules if _rule_matches_api(rule, api)]
    if not matching_rules:
        logger.debug(
            f"No matching visibility rules for API {api.get('apiId') or api.get('api_id')} and invoker {api_invoker_id}; default_allow_no_match={default_allow_no_match}"
        )
        return default_allow_no_match

    winner_rule = max(matching_rules, key=lambda rule: _rule_specificity(rule))
    default_access = _rule_default_access(winner_rule)
    allowed = default_access == 'ALLOW'
    invoker_exception = _rule_matches_invoker_exception(winner_rule, api_invoker_id)

    if _rule_matches_invoker_exception(winner_rule, api_invoker_id):
    logger.debug(
        f"Winner rule for API {api.get('apiId') or api.get('api_id')} and invoker {api_invoker_id}: ruleId={winner_rule.get('ruleId')} default_access={default_access} invoker_exception={invoker_exception}"
    )

    if invoker_exception:
        return not allowed

    return allowed
@@ -355,6 +374,9 @@ def _rule_matches_api(rule, api):
    :return: True if the rule applies to this API
    """
    provider_selector = rule.get('providerSelector', {})
    logger.debug(
        f"Checking API rule match for API {api.get('apiId') or api.get('api_id')} against rule {rule.get('ruleId')} selector={provider_selector}"
    )

    if not provider_selector:
        return True  # No selector = matches all APIs
@@ -413,6 +435,9 @@ def _rule_matches_invoker_exception(rule, api_invoker_id):
    :return: True if the invoker is in the exception selector
    """
    invoker_selector = rule.get('invokerExceptions') or rule.get('invokerSelector') or {}
    logger.debug(
        f"Checking invoker exception for invoker {api_invoker_id} on rule {rule.get('ruleId')} selector={invoker_selector}"
    )

    if not invoker_selector:
        return False
+1 −1
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ then
fi

docker pull $DOCKER_ROBOT_IMAGE:$DOCKER_ROBOT_IMAGE_VERSION || echo "Docker image ($DOCKER_ROBOT_IMAGE:$DOCKER_ROBOT_IMAGE_VERSION) not present on repository"
docker images|grep -Eq '^'$DOCKER_ROBOT_IMAGE'[ ]+[ ]'$DOCKER_ROBOT_IMAGE_VERSION''
docker images|grep -Eq '^'$DOCKER_ROBOT_IMAGE'([[:space:]]+|:)'$DOCKER_ROBOT_IMAGE_VERSION''
if [[ $? -ne 0 ]]
then
    read -p "Robot image is not present. To continue, Do you want to build it? (y/n)" build_robot_image