Commit bc24eef1 authored by Jorge Moratinos's avatar Jorge Moratinos
Browse files

Merge branch 'staging' into OCF86-upgrade-tests-to-allow-vendorextensions

parents 89b6206c 9c1640e1
Loading
Loading
Loading
Loading
Loading
+58 −32
Original line number Diff line number Diff line
@@ -3,18 +3,21 @@ from pymongo import ReturnDocument
import secrets
import requests
from .responses import bad_request_error, not_found_error, forbidden_error, internal_server_error, make_response
from flask import current_app, Flask, Response
from flask import current_app, Response
import json
from datetime import datetime
from ..util import dict_to_camel_case, clean_empty, serialize_clean_camel_case
from ..util import dict_to_camel_case, serialize_clean_camel_case
from .auth_manager import AuthManager
from .resources import Resource
from ..config import Config
from api_invoker_management.models.api_invoker_enrolment_details import APIInvokerEnrolmentDetails
from .redis_event import RedisEvent
from .redis_internal_event import RedisInternalEvent
from .publisher import Publisher

publisher_ops = Publisher()


class InvokerManagementOperations(Resource):

    def __check_api_invoker_id(self, api_invoker_id):
@@ -41,7 +44,8 @@ class InvokerManagementOperations(Resource):
            'common_name': invoker_id
        }

        response = requests.request("POST", url, headers=headers, data=data, verify = self.config["ca_factory"].get("verify", False))
        response = requests.request("POST", url, headers=headers, data=data,
                                    verify=self.config["ca_factory"].get("verify", False))
        print(response)
        response_payload = json.loads(response.text)

@@ -52,17 +56,18 @@ class InvokerManagementOperations(Resource):
        self.auth_manager = AuthManager()
        self.config = Config().get_config()


    def add_apiinvokerenrolmentdetail(self, apiinvokerenrolmentdetail, username, uuid):

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

        # try:
        current_app.logger.debug("Creating invoker resource")
        res = mycol.find_one({'onboarding_information.api_invoker_public_key': apiinvokerenrolmentdetail.onboarding_information.api_invoker_public_key})
        res = mycol.find_one({'onboarding_information.api_invoker_public_key':
                             apiinvokerenrolmentdetail.onboarding_information.api_invoker_public_key})

        if res is not None:
            current_app.logger.error("Generating forbbiden error, invoker registered")
            current_app.logger.error(
                "Generating forbbiden error, invoker registered")
            return forbidden_error(detail="Invoker already registered", cause="Identical invoker public key")

        if rfc3987.match(apiinvokerenrolmentdetail.notification_destination, rule="URI") is None:
@@ -72,11 +77,13 @@ class InvokerManagementOperations(Resource):
        current_app.logger.debug("Signing Certificate")

        api_invoker_id = 'INV'+str(secrets.token_hex(15))
        cert = self.__sign_cert(apiinvokerenrolmentdetail.onboarding_information.api_invoker_public_key, api_invoker_id)
        cert = self.__sign_cert(
            apiinvokerenrolmentdetail.onboarding_information.api_invoker_public_key, api_invoker_id)

        apiinvokerenrolmentdetail.api_invoker_id = api_invoker_id
        current_app.logger.debug(cert)
        apiinvokerenrolmentdetail.onboarding_information.api_invoker_certificate = cert['data']['certificate']
        apiinvokerenrolmentdetail.onboarding_information.api_invoker_certificate = cert[
            'data']['certificate']

        # Onboarding Date Record
        invoker_dict = apiinvokerenrolmentdetail.to_dict()
@@ -89,14 +96,19 @@ class InvokerManagementOperations(Resource):
        current_app.logger.debug("Invoker inserted in database")
        current_app.logger.debug("Netapp onboarded sucessfuly")

        self.auth_manager.add_auth_invoker(cert['data']['certificate'], api_invoker_id)
        self.auth_manager.add_auth_invoker(
            cert['data']['certificate'], api_invoker_id)

        res = make_response(object=serialize_clean_camel_case(apiinvokerenrolmentdetail), status=201)
        res.headers['Location'] = "/api-invoker-management/v1/onboardedInvokers/" + str(api_invoker_id)
        res = make_response(object=serialize_clean_camel_case(
            apiinvokerenrolmentdetail), status=201)
        res.headers['Location'] = "/api-invoker-management/v1/onboardedInvokers/" + \
            str(api_invoker_id)

        if res.status_code == 201:
            current_app.logger.info("Invoker Created")
            RedisEvent("API_INVOKER_ONBOARDED", ["apiInvokerIds"], [[str(api_invoker_id)]]).send_event()
            RedisEvent("API_INVOKER_ONBOARDED",
                       ["apiInvokerIds"],
                       [[str(api_invoker_id)]]).send_event()
        return res

    def update_apiinvokerenrolmentdetail(self, onboard_id, apiinvokerenrolmentdetail):
@@ -111,16 +123,23 @@ class InvokerManagementOperations(Resource):
                return result

            if apiinvokerenrolmentdetail.onboarding_information.api_invoker_public_key != result["onboarding_information"]["api_invoker_public_key"]:
                cert = self.__sign_cert(apiinvokerenrolmentdetail.onboarding_information.api_invoker_public_key, result["api_invoker_id"])
                apiinvokerenrolmentdetail.onboarding_information.api_invoker_certificate = cert['data']['certificate']
                self.auth_manager.update_auth_invoker(cert['data']["certificate"], onboard_id)
                cert = self.__sign_cert(
                    apiinvokerenrolmentdetail.onboarding_information.api_invoker_public_key, result["api_invoker_id"])
                apiinvokerenrolmentdetail.onboarding_information.api_invoker_certificate = cert[
                    'data']['certificate']
                self.auth_manager.update_auth_invoker(
                    cert['data']["certificate"], onboard_id)

            apiinvokerenrolmentdetail_update = apiinvokerenrolmentdetail.to_dict()
            apiinvokerenrolmentdetail_update = {
                key: value for key, value in apiinvokerenrolmentdetail_update.items() if value is not None
            }

            result = mycol.find_one_and_update(result, {"$set":apiinvokerenrolmentdetail_update}, projection={'_id': 0},return_document=ReturnDocument.AFTER ,upsert=False)
            result = mycol.find_one_and_update(result,
                                               {"$set": apiinvokerenrolmentdetail_update},
                                               projection={'_id': 0},
                                               return_document=ReturnDocument.AFTER,
                                               upsert=False)

            result = {
                key: value for key, value in result.items() if value is not None
@@ -130,10 +149,13 @@ class InvokerManagementOperations(Resource):

            invoker_updated = APIInvokerEnrolmentDetails().from_dict(dict_to_camel_case(result))

            res = make_response(object=serialize_clean_camel_case(invoker_updated), status=200)
            res = make_response(object=serialize_clean_camel_case(
                invoker_updated), status=200)
            if res.status_code == 200:
                current_app.logger.info("Invoker Updated")
                RedisEvent("API_INVOKER_UPDATED", ["apiInvokerIds"], [[onboard_id]]).send_event()
                RedisEvent("API_INVOKER_UPDATED",
                           ["apiInvokerIds"],
                           [[onboard_id]]).send_event()
            return res

        except Exception as e:
@@ -160,13 +182,17 @@ class InvokerManagementOperations(Resource):
            res = make_response(out, status=204)
            if res.status_code == 204:
                current_app.logger.info("Invoker Removed")
                RedisEvent("API_INVOKER_OFFBOARDED", ["apiInvokerIds"], [[onboard_id]]).send_event()
                publisher_ops.publish_message("internal-messages", f"invoker-removed:{onboard_id}")
                RedisEvent("API_INVOKER_OFFBOARDED",
                           ["apiInvokerIds"],
                           [[onboard_id]]).send_event()
                RedisInternalEvent("INVOKER-REMOVED",
                                   "invokerId",
                                   {
                                       "api_invoker_id": onboard_id
                                   }).send_event()
            return res

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

+37 −10
Original line number Diff line number Diff line
@@ -2,6 +2,8 @@
import redis
from .invoker_internal_ops import InvokerInternalOperations
from flask import current_app
import json


class Subscriber():

@@ -14,13 +16,38 @@ class Subscriber():
    def listen(self):
        for raw_message in self.p.listen():
            if raw_message["type"] == "message" and raw_message["channel"].decode('utf-8') == "internal-messages":
                message, invoker_id, api_id = raw_message["data"].decode('utf-8').split(":")
                if message == "security-context-created":
                    current_app.logger.debug("Internal message received, updating Api list on invoker")
                    self.invoker_ops.update_services_list(invoker_id, api_id)
                if message == "security-context-removed":
                    current_app.logger.debug("Internal message received, removing service in  Api list of invoker")
                    self.invoker_ops.remove_services_list(invoker_id, api_id)


                current_app.logger.info("New internal event received")
                internal_redis_event = json.loads(
                    raw_message["data"].decode('utf-8'))
                if internal_redis_event.get('event') == "SECURITY-CONTEXT-CREATED":
                    current_app.logger.debug(
                        "Internal message received, updating Api list on invoker")
                    security_context_information = internal_redis_event.get(
                        'information', None)
                    if security_context_information is not None:
                        api_invoker_id = security_context_information.get(
                            'api_invoker_id')
                        api_id = security_context_information.get('api_id')
                        self.invoker_ops.update_services_list(
                            api_invoker_id, api_id)
                elif internal_redis_event.get('event') == "SECURITY-CONTEXT-REMOVED":
                    current_app.logger.debug(
                        "Internal message received, removing service in  Api list of invoker")
                    security_context_information = internal_redis_event.get(
                        'information', None)
                    if security_context_information is not None:
                        api_invoker_id = security_context_information.get(
                            'api_invoker_id')
                        api_id = security_context_information.get('api_id')
                        self.invoker_ops.remove_services_list(
                            api_invoker_id, api_id)
                elif internal_redis_event.get('event') == "INVOKER-REMOVED":
                    api_invoker_id = internal_redis_event.get(
                        'information', {"api_invoker_id": None}).get('api_invoker_id')
                    if api_invoker_id is not None:
                        self.acls_ops.remove_invoker_acl(api_invoker_id)
                elif internal_redis_event.get('event') == "PROVIDER-REMOVED":
                    aef_ids = internal_redis_event.get(
                        'information', {"aef_ids": []}).get('aef_ids')
                    if len(aef_ids) > 0:
                        self.acls_ops.remove_provider_acls(aef_ids[0])
+35 −0
Original line number Diff line number Diff line
from ..encoder import JSONEncoder
from .publisher import Publisher
import json

publisher_ops = Publisher()


class RedisInternalEvent():
    def __init__(self, event, event_detail_key=None, information=None) -> None:
        self.INTERNAL_MESSAGES = [
            'INVOKER-REMOVED',
            'PROVIDER-REMOVED',
            'SECURITY-CONTEXT-CREATED',
            'SECURITY-CONTEXT-REMOVED',
            'create-acl',
            'remove-acl',
        ]
        if event not in self.INTERNAL_MESSAGES:
            raise Exception(
                "Internal Message (" + event + ") is not on INTERNAL_MESSAGES enum (" + ','.join(self.INTERNAL_MESSAGES) + ")")
        self.redis_event = {
            "event": event
        }
        if event_detail_key is not None and information is not None:
            self.redis_event['key'] = event_detail_key
            self.redis_event['information'] = information

    def to_string(self):
        return json.dumps(self.redis_event, cls=JSONEncoder)

    def send_event(self):
        publisher_ops.publish_message("internal-messages", self.to_string())

    def __call__(self):
        return self.redis_event
+57 −25
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ from .resources import Resource
from .auth_manager import AuthManager

from api_provider_management.models.api_provider_enrolment_details import APIProviderEnrolmentDetails  # noqa: E501
from .redis_internal_event import RedisInternalEvent


class ProviderManagementOperations(Resource):
@@ -40,19 +41,24 @@ class ProviderManagementOperations(Resource):
            my_provider_enrolment_details = mycol.find_one(search_filter)

            if my_provider_enrolment_details is not None:
                current_app.logger.error("Found provider registered with same id")
                current_app.logger.error(
                    "Found provider registered with same id")
                return forbidden_error(detail="Provider already registered", cause="Identical provider reg sec")

            api_provider_enrolment_details.api_prov_dom_id = secrets.token_hex(15)
            api_provider_enrolment_details.api_prov_dom_id = secrets.token_hex(
                15)

            current_app.logger.debug("Generating certs to api prov funcs")

            for api_provider_func in api_provider_enrolment_details.api_prov_funcs:
                api_provider_func.api_prov_func_id = api_provider_func.api_prov_func_role + str(secrets.token_hex(15))
                certificate = sign_certificate(api_provider_func.reg_info.api_prov_pub_key, api_provider_func.api_prov_func_id)
                api_provider_func.api_prov_func_id = api_provider_func.api_prov_func_role + \
                    str(secrets.token_hex(15))
                certificate = sign_certificate(
                    api_provider_func.reg_info.api_prov_pub_key, api_provider_func.api_prov_func_id)
                api_provider_func.reg_info.api_prov_cert = certificate

                self.auth_manager.add_auth_provider(certificate, api_provider_func.api_prov_func_id, api_provider_func.api_prov_func_role, api_provider_enrolment_details.api_prov_dom_id)
                self.auth_manager.add_auth_provider(certificate, api_provider_func.api_prov_func_id,
                                                    api_provider_func.api_prov_func_role, api_provider_enrolment_details.api_prov_dom_id)

            # Onboarding Date Record
            provider_dict = api_provider_enrolment_details.to_dict()
@@ -64,9 +70,11 @@ class ProviderManagementOperations(Resource):

            current_app.logger.debug("Provider inserted in database")

            res = make_response(object=serialize_clean_camel_case(api_provider_enrolment_details), status=201)
            res = make_response(object=serialize_clean_camel_case(
                api_provider_enrolment_details), status=201)

            res.headers['Location'] = "/api-provider-management/v1/registrations/" + str(api_provider_enrolment_details.api_prov_dom_id)
            res.headers['Location'] = "/api-provider-management/v1/registrations/" + \
                str(api_provider_enrolment_details.api_prov_dom_id)
            return res

        except Exception as e:
@@ -84,17 +92,32 @@ class ProviderManagementOperations(Resource):
            if isinstance(result, Response):
                return result

            apf_id = [ provider_func['api_prov_func_id'] for provider_func in result["api_prov_funcs"] if provider_func['api_prov_func_role'] == 'APF' ]
            aef_id = [ provider_func['api_prov_func_id'] for provider_func in result["api_prov_funcs"] if provider_func['api_prov_func_role'] == 'AEF' ]
            amf_id = [ provider_func['api_prov_func_id'] for provider_func in result["api_prov_funcs"] if provider_func['api_prov_func_role'] == 'AMF' ]
            func_ids = list()
            for provider_func in result["api_prov_funcs"]:
                func_ids.append(provider_func['api_prov_func_id'])
            apf_ids = [provider_func['api_prov_func_id']
                       for provider_func in result["api_prov_funcs"] if provider_func['api_prov_func_role'] == 'APF']
            aef_ids = [provider_func['api_prov_func_id']
                       for provider_func in result["api_prov_funcs"] if provider_func['api_prov_func_role'] == 'AEF']
            amf_ids = [provider_func['api_prov_func_id']
                       for provider_func in result["api_prov_funcs"] if provider_func['api_prov_func_role'] == 'AMF']

            mycol.delete_one({'api_prov_dom_id': api_prov_dom_id})
            out =  "The provider matching apiProvDomainId  " + api_prov_dom_id + " was offboarded."
            out = "The provider matching apiProvDomainId  " + \
                api_prov_dom_id + " was offboarded."
            current_app.logger.debug("Removed provider domain from database")

            self.auth_manager.remove_auth_provider([apf_id[0], aef_id[0], amf_id[0]])
            self.auth_manager.remove_auth_provider(func_ids)

            RedisInternalEvent("PROVIDER-REMOVED",
                               "providerIds",
                               {
                                   "apf_ids": apf_ids,
                                   "aef_ids": aef_ids,
                                   "amf_ids": amf_ids,
                                   "all_ids": apf_ids + aef_ids + amf_ids
                               }).send_event()

            self.publish_ops.publish_message("internal-messages", f"provider-removed:{aef_id[0]}:{apf_id[0]}:{amf_id[0]}")
            return make_response(object=out, status=204)

        except Exception as e:
@@ -114,11 +137,14 @@ class ProviderManagementOperations(Resource):

            for func in api_provider_enrolment_details.api_prov_funcs:
                if func.api_prov_func_id is None:
                    func.api_prov_func_id = func.api_prov_func_role + str(secrets.token_hex(15))
                    certificate = sign_certificate(func.reg_info.api_prov_pub_key, func.api_prov_func_id)
                    func.api_prov_func_id = func.api_prov_func_role + \
                        str(secrets.token_hex(15))
                    certificate = sign_certificate(
                        func.reg_info.api_prov_pub_key, func.api_prov_func_id)
                    func.reg_info.api_prov_cert = certificate

                    self.auth_manager.update_auth_provider(certificate, func.api_prov_func_id, api_prov_dom_id, func.api_prov_func_role)
                    self.auth_manager.update_auth_provider(
                        certificate, func.api_prov_func_id, api_prov_dom_id, func.api_prov_func_role)
                else:
                    api_prov_funcs = result["api_prov_funcs"]
                    for api_func in api_prov_funcs:
@@ -126,14 +152,18 @@ class ProviderManagementOperations(Resource):
                            if func.api_prov_func_role != api_func["api_prov_func_role"]:
                                return bad_request_error(detail="Bad Role in provider", cause="Different role in update reqeuest", invalid_params=[{"param": "api_prov_func_role", "reason": "different role with same id"}])
                            if func.reg_info.api_prov_pub_key != api_func["reg_info"]["api_prov_pub_key"]:
                                certificate = sign_certificate(func.reg_info.api_prov_pub_key, api_func["api_prov_func_id"])
                                certificate = sign_certificate(
                                    func.reg_info.api_prov_pub_key, api_func["api_prov_func_id"])
                                func.reg_info.api_prov_cert = certificate
                                self.auth_manager.update_auth_provider(certificate, func.api_prov_func_id, api_prov_dom_id, func.api_prov_func_role)
                                self.auth_manager.update_auth_provider(
                                    certificate, func.api_prov_func_id, api_prov_dom_id, func.api_prov_func_role)

            api_provider_enrolment_details = api_provider_enrolment_details.to_dict()
            api_provider_enrolment_details = clean_empty(api_provider_enrolment_details)
            api_provider_enrolment_details = clean_empty(
                api_provider_enrolment_details)

            result = mycol.find_one_and_update(result, {"$set":api_provider_enrolment_details}, projection={'_id': 0},return_document=ReturnDocument.AFTER ,upsert=False)
            result = mycol.find_one_and_update(result, {"$set": api_provider_enrolment_details}, projection={
                                               '_id': 0}, return_document=ReturnDocument.AFTER, upsert=False)
            result = clean_empty(result)

            current_app.logger.debug("Provider domain updated in database")
@@ -157,9 +187,11 @@ class ProviderManagementOperations(Resource):
                return result

            api_provider_enrolment_details_patch = api_provider_enrolment_details_patch.to_dict()
            api_provider_enrolment_details_patch = clean_empty(api_provider_enrolment_details_patch)
            api_provider_enrolment_details_patch = clean_empty(
                api_provider_enrolment_details_patch)

            result = mycol.find_one_and_update(result, {"$set":api_provider_enrolment_details_patch}, projection={'_id': 0},return_document=ReturnDocument.AFTER ,upsert=False)
            result = mycol.find_one_and_update(result, {"$set": api_provider_enrolment_details_patch}, projection={
                                               '_id': 0}, return_document=ReturnDocument.AFTER, upsert=False)

            result = clean_empty(result)

+35 −0
Original line number Diff line number Diff line
from ..encoder import JSONEncoder
from .publisher import Publisher
import json

publisher_ops = Publisher()


class RedisInternalEvent():
    def __init__(self, event, event_detail_key=None, information=None) -> None:
        self.INTERNAL_MESSAGES = [
            'INVOKER-REMOVED',
            'PROVIDER-REMOVED',
            'SECURITY-CONTEXT-CREATED',
            'SECURITY-CONTEXT-REMOVED',
            'create-acl',
            'remove-acl',
        ]
        if event not in self.INTERNAL_MESSAGES:
            raise Exception(
                "Internal Message (" + event + ") is not on INTERNAL_MESSAGES enum (" + ','.join(self.INTERNAL_MESSAGES) + ")")
        self.redis_event = {
            "event": event
        }
        if event_detail_key is not None and information is not None:
            self.redis_event['key'] = event_detail_key
            self.redis_event['information'] = information

    def to_string(self):
        return json.dumps(self.redis_event, cls=JSONEncoder)

    def send_event(self):
        publisher_ops.publish_message("internal-messages", self.to_string())

    def __call__(self):
        return self.redis_event
Loading