Commit 03f83118 authored by Jorge Moratinos's avatar Jorge Moratinos
Browse files

SERVICE_API_UPDATE upgraded

parent 25540b00
Loading
Loading
Loading
Loading
Loading
+17 −24
Original line number Diff line number Diff line
@@ -4,7 +4,6 @@ from ..core import serviceapidescriptions
from ..core.serviceapidescriptions import PublishServiceOperations
from ..core.publisher import Publisher

import json
from flask import Response, request, current_app
from flask_jwt_extended import jwt_required, get_jwt_identity
from flask import current_app
@@ -14,15 +13,12 @@ from cryptography import x509
from cryptography.hazmat.backends import default_backend
from ..core.validate_user import ControlAccess
from functools import wraps
import pymongo
from ..core.redis_event import RedisEvent


service_operations = PublishServiceOperations()
publisher_ops = Publisher()

valid_user = ControlAccess()


def cert_validation():
    def _cert_validation(f):
        @wraps(f)
@@ -32,13 +28,16 @@ def cert_validation():
            cert_tmp = request.headers['X-Ssl-Client-Cert']
            cert_raw = cert_tmp.replace('\t', '')

            cert = x509.load_pem_x509_certificate(str.encode(cert_raw), default_backend())
            cert = x509.load_pem_x509_certificate(
                str.encode(cert_raw), default_backend())

            cn = cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)[0].value.strip()
            cn = cert.subject.get_attributes_for_oid(
                x509.OID_COMMON_NAME)[0].value.strip()

            if cn != "superadmin":
                cert_signature = cert.signature.hex()
                result = valid_user.validate_user_cert(args["apfId"], args["serviceApiId"], cert_signature)
                result = valid_user.validate_user_cert(
                    args["apfId"], args["serviceApiId"], cert_signature)

                if result is not None:
                    return result
@@ -48,6 +47,7 @@ def cert_validation():
        return __cert_validation
    return _cert_validation


def apf_id_service_apis_get(apf_id):  # noqa: E501
    """apf_id_service_apis_get

@@ -83,13 +83,9 @@ def apf_id_service_apis_post(apf_id, body): # noqa: E501

    res = service_operations.add_serviceapidescription(apf_id, body)

    if res.status_code == 201:
        current_app.logger.info("Service published")
        api_id=res.headers['Location'].split('/')[-1]
        RedisEvent("SERVICE_API_AVAILABLE", "apiIds", [api_id] ).send_event()

    return res


@cert_validation()
def apf_id_service_apis_service_api_id_delete(service_api_id, apf_id):  # noqa: E501
    """apf_id_service_apis_service_api_id_delete
@@ -105,15 +101,12 @@ def apf_id_service_apis_service_api_id_delete(service_api_id, apf_id): # noqa:
    """

    current_app.logger.info("Removing service published")
    res = service_operations.delete_serviceapidescription(service_api_id, apf_id)

    if res.status_code == 204:
        current_app.logger.info("Removed service published")
        RedisEvent("SERVICE_API_UNAVAILABLE", "apiIds", [service_api_id] ).send_event()
        publisher_ops.publish_message("internal-messages", f"service-removed:{service_api_id}")
    res = service_operations.delete_serviceapidescription(
        service_api_id, apf_id)

    return res


@cert_validation()
def apf_id_service_apis_service_api_id_get(service_api_id, apf_id):  # noqa: E501
    """apf_id_service_apis_service_api_id_get
@@ -133,6 +126,7 @@ def apf_id_service_apis_service_api_id_get(service_api_id, apf_id): # noqa: E50

    return res


@cert_validation()
def apf_id_service_apis_service_api_id_put(service_api_id, apf_id, body):  # noqa: E501
    """apf_id_service_apis_service_api_id_put
@@ -149,14 +143,13 @@ def apf_id_service_apis_service_api_id_put(service_api_id, apf_id, body): # noq
    :rtype: ServiceAPIDescription
    """

    current_app.logger.info("Updating service api id with id: " + service_api_id)
    current_app.logger.info(
        "Updating service api id with id: " + service_api_id)

    if connexion.request.is_json:
        body = ServiceAPIDescription.from_dict(connexion.request.get_json())  # noqa: E501

    response = service_operations.update_serviceapidescription(service_api_id, apf_id, body)

    if response.status_code == 200:
        publisher_ops.publish_message("events", "SERVICE_API_UPDATE")
    response = service_operations.update_serviceapidescription(
        service_api_id, apf_id, body)

    return response
+48 −22
Original line number Diff line number Diff line
@@ -17,23 +17,30 @@ from ..util import dict_to_camel_case, clean_empty
from .responses import bad_request_error, internal_server_error, forbidden_error, not_found_error, unauthorized_error, make_response
from bson import json_util
from .auth_manager import AuthManager
from .redis_event import RedisEvent
from .publisher import Publisher

publisher_ops = Publisher()


service_api_not_found_message = "Service API not found"


class PublishServiceOperations(Resource):

    def __check_apf(self, apf_id):
        providers_col = self.db.get_col_by_name(self.db.capif_provider_col)

        current_app.logger.debug("Checking apf id")
        provider = providers_col.find_one({"api_prov_funcs.api_prov_func_id": apf_id})
        provider = providers_col.find_one(
            {"api_prov_funcs.api_prov_func_id": apf_id})

        if provider is None:
            current_app.logger.error("Publisher not exist")
            return unauthorized_error(detail="Publisher not existing", cause="Publisher id not found")

        list_apf_ids =  [func["api_prov_func_id"] for func in provider["api_prov_funcs"] if func["api_prov_func_role"] == "APF"]
        list_apf_ids = [func["api_prov_func_id"]
                        for func in provider["api_prov_funcs"] if func["api_prov_func_role"] == "APF"]
        if apf_id not in list_apf_ids:
            current_app.logger.debug("This id not belongs to APF")
            return unauthorized_error(detail="You are not a publisher", cause="This API is only available for publishers")
@@ -57,7 +64,8 @@ class PublishServiceOperations(Resource):
            if result != None:
                return result

            service = mycol.find({"apf_id": apf_id}, {"_id":0, "api_name":1, "api_id":1, "aef_profiles":1, "description":1, "supported_features":1, "shareable_info":1, "service_api_category":1, "api_supp_feats":1, "pub_api_path":1, "ccf_id":1})
            service = mycol.find({"apf_id": apf_id}, {"_id": 0, "api_name": 1, "api_id": 1, "aef_profiles": 1, "description": 1,
                                 "supported_features": 1, "shareable_info": 1, "service_api_category": 1, "api_supp_feats": 1, "pub_api_path": 1, "ccf_id": 1})
            current_app.logger.debug(service)
            if service is None:
                current_app.logger.error("Not found services for this apf id")
@@ -92,9 +100,11 @@ class PublishServiceOperations(Resource):
            if result != None:
                return result

            service = mycol.find_one({"api_name": serviceapidescription.api_name})
            service = mycol.find_one(
                {"api_name": serviceapidescription.api_name})
            if service is not None:
                current_app.logger.error("Service already registered with same api name")
                current_app.logger.error(
                    "Service already registered with same api name")
                return forbidden_error(detail="Already registered service with same api name", cause="Found service with same api name")

            api_id = secrets.token_hex(15)
@@ -110,8 +120,13 @@ class PublishServiceOperations(Resource):

            current_app.logger.debug("Service inserted in database")
            res = make_response(object=serviceapidescription, status=201)
            res.headers['Location'] = "http://localhost:8080/published-apis/v1/" + str(apf_id) + "/service-apis/" + str(api_id)
            res.headers['Location'] = "http://localhost:8080/published-apis/v1/" + \
                str(apf_id) + "/service-apis/" + str(api_id)

            if res.status_code == 201:
                current_app.logger.info("Service published")
                RedisEvent("SERVICE_API_AVAILABLE", "apiIds",
                           [str(api_id)]).send_event()
            return res

        except Exception as e:
@@ -119,26 +134,25 @@ class PublishServiceOperations(Resource):
            current_app.logger.error(exception + "::" + str(e))
            return internal_server_error(detail=exception, cause=str(e))



    def get_one_serviceapi(self, service_api_id, apf_id):

        mycol = self.db.get_col_by_name(self.db.service_api_descriptions)

        try:
            current_app.logger.debug("Geting service api with id: " + service_api_id)
            current_app.logger.debug(
                "Geting service api with id: " + service_api_id)
            result = self.__check_apf(apf_id)

            if result != None:
                return result

            my_query = {'apf_id': apf_id, 'api_id': service_api_id}
            service_api = mycol.find_one(my_query, {"_id":0, "api_name":1, "api_id":1, "aef_profiles":1, "description":1, "supported_features":1, "shareable_info":1, "service_api_category":1, "api_supp_feats":1, "pub_api_path":1, "ccf_id":1})
            service_api = mycol.find_one(my_query, {"_id": 0, "api_name": 1, "api_id": 1, "aef_profiles": 1, "description": 1,
                                         "supported_features": 1, "shareable_info": 1, "service_api_category": 1, "api_supp_feats": 1, "pub_api_path": 1, "ccf_id": 1})
            if service_api is None:
                current_app.logger.error(service_api_not_found_message)
                return not_found_error(detail=service_api_not_found_message, cause="No Service with specific credentials exists")


            my_service_api = dict_to_camel_case(service_api)
            my_service_api = clean_empty(my_service_api)

@@ -157,7 +171,8 @@ class PublishServiceOperations(Resource):

        try:

            current_app.logger.debug("Removing api service with id: " + service_api_id)
            current_app.logger.debug(
                "Removing api service with id: " + service_api_id)
            result = self.__check_apf(apf_id)

            if result != None:
@@ -176,21 +191,28 @@ class PublishServiceOperations(Resource):

            current_app.logger.debug("Removed service from database")
            out = "The service matching api_id " + service_api_id + " was deleted."
            return make_response(out, status=204)
            res = make_response(out, status=204)
            if res.status_code == 204:
                current_app.logger.info("Removed service published")
                RedisEvent("SERVICE_API_UNAVAILABLE", "apiIds",
                           [service_api_id]).send_event()
                publisher_ops.publish_message(
                    "internal-messages", f"service-removed:{service_api_id}")
            return res

        except Exception as e:
            exception = "An exception occurred in delete service"
            current_app.logger.error(exception + "::" + str(e))
            return internal_server_error(detail=exception, cause=str(e))


    def update_serviceapidescription(self, service_api_id, apf_id, service_api_description):

        mycol = self.db.get_col_by_name(self.db.service_api_descriptions)

        try:

            current_app.logger.debug("Updating service api with id: " + service_api_id)
            current_app.logger.debug(
                "Updating service api with id: " + service_api_id)

            result = self.__check_apf(apf_id)

@@ -207,18 +229,22 @@ class PublishServiceOperations(Resource):
            service_api_description = service_api_description.to_dict()
            service_api_description = clean_empty(service_api_description)

            result = mycol.find_one_and_update(serviceapidescription, {"$set":service_api_description}, projection={"_id":0, "api_name":1, "api_id":1, "aef_profiles":1, "description":1, "supported_features":1, "shareable_info":1, "service_api_category":1, "api_supp_feats":1, "pub_api_path":1, "ccf_id":1},return_document=ReturnDocument.AFTER ,upsert=False)
            result = mycol.find_one_and_update(serviceapidescription, {"$set": service_api_description}, projection={"_id": 0, "api_name": 1, "api_id": 1, "aef_profiles": 1, "description": 1,
                                               "supported_features": 1, "shareable_info": 1, "service_api_category": 1, "api_supp_feats": 1, "pub_api_path": 1, "ccf_id": 1}, return_document=ReturnDocument.AFTER, upsert=False)

            result = clean_empty(result)

            current_app.logger.debug("Updated service api")
            service_api_description_updated = dict_to_camel_case(result)

            response = make_response(object=dict_to_camel_case(result), status=200)

            response = make_response(
                object=service_api_description_updated, status=200)
            if response.status_code == 200:
                RedisEvent("SERVICE_API_UPDATE", "serviceAPIDescriptions", [
                           service_api_description_updated]).send_event()
            return response

        except Exception as e:
            exception = "An exception occurred in update service"
            current_app.logger.error(exception + "::" + str(e))
            return internal_server_error(detail=exception, cause=str(e))
+1 −1
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ CAPIF_VAULT_PORT=8200
# CAPIF_VAULT_TOKEN=dev-only-token
CAPIF_VAULT_TOKEN=read-ca-token

MOCK_SERVER_URL=http://10.95.115.22:9090
MOCK_SERVER_URL=http://192.168.0.119:9090


echo "CAPIF_HOSTNAME = $CAPIF_HOSTNAME"
+81 −0
Original line number Diff line number Diff line
@@ -311,3 +311,84 @@ Invoker subscribe to Service API Available and Unavailable events
    Length Should Be    ${notification_events_on_mock_server}    2
    List Should Contain Value    ${notification_events_on_mock_server}    ${notification_event_expected_removed}
    List Should Contain Value    ${notification_events_on_mock_server}    ${notification_event_expected_created}


Invoker subscribe to Service API Update
    [Tags]    capif_api_events-8    mockserver

    # Start Mock server
    Check Mock Server
    Clean Mock Server

    # Register APF
    ${register_user_info_provider}=    Provider Default Registration

    # Publish one api
    ${service_api_description_published}    ${resource_url}    ${request_body}=    Publish Service Api
    ...    ${register_user_info_provider}

    # Register INVOKER
    ${register_user_info_invoker}    ${url}    ${request_body}=    Invoker Default Onboarding

    ${discover_response}=    Get Request Capif
    ...    ${DISCOVER_URL}${register_user_info_invoker['api_invoker_id']}
    ...    server=${CAPIF_HTTPS_URL}
    ...    verify=ca.crt
    ...    username=${INVOKER_USERNAME}

    ${api_ids}    ${api_names}=    Get Api Ids And Names From Discover Response    ${discover_response}

    # Subscribe to events
    ${events_list}=    Create List    SERVICE_API_UPDATE
    ${aef_ids}=    Create List    ${register_user_info_provider['aef_id']}
    ${event_filter}=    Create Capif Event Filter    aefIds=${aef_ids}
    ${event_filters}=    Create List    ${event_filter}

    ${request_body}=    Create Events Subscription
    ...    events=@{events_list}
    ...    notificationDestination=${MOCK_SERVER_URL}/testing
    ...    eventFilters=${event_filters}
    ${resp}=    Post Request Capif
    ...    /capif-events/v1/${register_user_info_invoker['api_invoker_id']}/subscriptions
    ...    json=${request_body}
    ...    server=${CAPIF_HTTPS_URL}
    ...    verify=ca.crt
    ...    username=${INVOKER_USERNAME}

    # Check Results
    Check Response Variable Type And Values    ${resp}    201    EventSubscription
    ${subscriber_id}    ${subscription_id}=    Check Event Location Header    ${resp}

    # Update Service API
    ${request_body_modified}=    Create Service Api Description    service_1_modified
    ${resp}=    Put Request Capif
    ...    ${resource_url.path}
    ...    json=${request_body_modified}
    ...    server=${CAPIF_HTTPS_URL}
    ...    verify=ca.crt
    ...    username=${APF_PROVIDER_USERNAME}

    Check Response Variable Type And Values    ${resp}    200    ServiceAPIDescription
    ...    apiName=service_1_modified

    # Check Results
    Sleep    3s
    # Get from Mock server the EventNotification Messages sent to callback setup on event subscription.
    ${resp}=    Get Mock Server Messages
    ${notification_events_on_mock_server}=    Set Variable    ${resp.json()}
    # Check if message follow EventNotification definition.
    Check Variable    ${notification_events_on_mock_server}    EventNotification

    # Create Notification Events expected to be received
    ${api_id}=    Fetch From Right    ${resource_url.path}    /
    Set To Dictionary    ${request_body_modified}   apiId=${api_id}
    ${notification_event_expected}=    Create Notification Event
    ...    ${subscription_id}
    ...    SERVICE_API_UPDATE
    ...    serviceAPIDescriptions=${request_body_modified}
    Check Variable    ${notification_event_expected}    EventNotification

    # Check results
    Length Should Be    ${notification_events_on_mock_server}    1
    List Should Contain Value    ${notification_events_on_mock_server}    ${notification_event_expected}
+2 −2
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ def create_notification_event(subscriptionId, event, serviceAPIDescriptions=None
    }
    count=0
    if serviceAPIDescriptions != None:
        if isinstance(serviceAPIDescriptions,list()):
        if isinstance(serviceAPIDescriptions,list):
            result['eventDetail']['serviceAPIDescriptions']=serviceAPIDescriptions
        else:
            result['eventDetail']['serviceAPIDescriptions']=[serviceAPIDescriptions]
@@ -83,7 +83,7 @@ def create_notification_event(subscriptionId, event, serviceAPIDescriptions=None
            result['eventDetail']['invocationLogs']=[invocationLogs]
        count=count+1
    if apiTopoHide != None:
        if isinstance(apiTopoHide):
        if isinstance(apiTopoHide,list):
            result['eventDetail']['apiTopoHide']=apiTopoHide
        else:
            result['eventDetail']['apiTopoHide']=[apiTopoHide]
Loading