diff --git a/README.md b/README.md index 1a99ee20..2c05ce9c 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ - [CAPIF Tool Release 2.0](#capif-tool-release-20) - [CAPIF Tool Release 2.1](#capif-tool-release-21) - [CAPIF Tool Release 3.0](#capif-tool-release-30) +- [CAPIF Tool Release 3.1](#capif-tool-release-31) # Repository structure @@ -250,6 +251,13 @@ Changes at Tests: * Complete code refactor of all tests * Complete test plan review, including all services (except auditing and logging) +# CAPIF Tool Release 3.1 + +* Delete a service automatically if the provider that contains the APF that published it is deleted +* Clear the security context of an invoker automatically if the invoker is deleted +* Delete automatically the entry in the security info of the security context if the provider that has the aef that published the service is deleted +* Delete automatically the entry in the security info of the security context if the service on which that context was created is deleted + diff --git a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/controllers/default_controller.py b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/controllers/default_controller.py index d51968e6..92a71e56 100644 --- a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/controllers/default_controller.py +++ b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/controllers/default_controller.py @@ -34,7 +34,7 @@ def onboarded_invokers_onboarding_id_delete(onboarding_id): # noqa: E501 if res.status_code == 204: current_app.logger.info("Invoker Removed") publisher_ops.publish_message("events", "API_INVOKER_UPDATED") - publisher_ops.publish_message("internal-messages", "invoker-removed:"+onboarding_id) + publisher_ops.publish_message("internal-messages", f"invoker-removed:{onboarding_id}") return res diff --git a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/publisher.py b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/publisher.py index f7b0c3c4..3898c4b8 100644 --- a/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/publisher.py +++ b/services/TS29222_CAPIF_API_Invoker_Management_API/api_invoker_management/core/publisher.py @@ -1,5 +1,6 @@ import redis import sys +from flask import current_app class Publisher(): diff --git a/services/TS29222_CAPIF_API_Invoker_Management_API/requirements.txt b/services/TS29222_CAPIF_API_Invoker_Management_API/requirements.txt index 86d08f7b..de9d2229 100644 --- a/services/TS29222_CAPIF_API_Invoker_Management_API/requirements.txt +++ b/services/TS29222_CAPIF_API_Invoker_Management_API/requirements.txt @@ -4,8 +4,8 @@ python_dateutil >= 2.6.0 setuptools >= 21.0.0 Flask == 2.0.3 pymongo == 4.0.1 -flask_jwt_extended -pyopenssl -rfc3987 -redis -flask_executor \ No newline at end of file +flask_jwt_extended == 4.4.4 +pyopenssl == 23.0.0 +rfc3987 == 1.3.8 +redis == 4.5.1 +flask_executor == 1.0.0 \ No newline at end of file diff --git a/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/provider_enrolment_details_api.py b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/provider_enrolment_details_api.py index b8a1fb9e..3ddab693 100644 --- a/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/provider_enrolment_details_api.py +++ b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/provider_enrolment_details_api.py @@ -74,9 +74,12 @@ def delete_api_provider_enrolment_details(self, api_prov_dom_id): 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' ] mycol.delete_one({'api_prov_dom_id': api_prov_dom_id}) out = "The provider matching apiProvDomainId " + api_prov_dom_id + " was offboarded." current_app.logger.debug("Removed provider domain from database") + self.publish_ops.publish_message("internal-messages", f"provider-removed:{aef_id[0]}:{apf_id[0]}") return make_response(object=out, status=204) except Exception as e: diff --git a/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/publisher.py b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/publisher.py new file mode 100644 index 00000000..38d7259f --- /dev/null +++ b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/publisher.py @@ -0,0 +1,10 @@ +import redis +import sys + +class Publisher(): + + def __init__(self): + self. r = redis.Redis(host='redis', port=6379, db=0) + + def publish_message(self, channel, message): + self.r.publish(channel, message) \ No newline at end of file diff --git a/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/resources.py b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/resources.py index 94e29ec0..86e99d4b 100644 --- a/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/resources.py +++ b/services/TS29222_CAPIF_API_Provider_Management_API/api_provider_management/core/resources.py @@ -1,7 +1,9 @@ from abc import ABC, abstractmethod from ..db.db import MongoDatabse +from .publisher import Publisher class Resource(ABC): def __init__(self): - self.db = MongoDatabse() \ No newline at end of file + self.db = MongoDatabse() + self.publish_ops = Publisher() \ No newline at end of file diff --git a/services/TS29222_CAPIF_API_Provider_Management_API/requirements.txt b/services/TS29222_CAPIF_API_Provider_Management_API/requirements.txt index 0660f523..84332fb4 100644 --- a/services/TS29222_CAPIF_API_Provider_Management_API/requirements.txt +++ b/services/TS29222_CAPIF_API_Provider_Management_API/requirements.txt @@ -4,6 +4,9 @@ python_dateutil >= 2.6.0 setuptools >= 21.0.0 Flask == 2.0.3 pymongo == 4.0.1 -flask_jwt_extended -pyopenssl +redis == 4.5.1 +flask_executor == 1.0.0 +flask_jwt_extended == 4.4.4 +pyopenssl == 23.0.0 +rfc3987 == 1.3.8 diff --git a/services/TS29222_CAPIF_Auditing_API/requirements.txt b/services/TS29222_CAPIF_Auditing_API/requirements.txt index 45c243e3..ec07ff82 100644 --- a/services/TS29222_CAPIF_Auditing_API/requirements.txt +++ b/services/TS29222_CAPIF_Auditing_API/requirements.txt @@ -5,6 +5,5 @@ setuptools >= 21.0.0 Flask == 2.0.3 pymongo == 4.0.1 elasticsearch == 8.4.3 -flask_jwt_extended -pyopenssl -flask-mqtt +flask_jwt_extended == 4.4.4 +pyopenssl == 23.0.0 diff --git a/services/TS29222_CAPIF_Discover_Service_API/requirements.txt b/services/TS29222_CAPIF_Discover_Service_API/requirements.txt index d348641b..f815469f 100644 --- a/services/TS29222_CAPIF_Discover_Service_API/requirements.txt +++ b/services/TS29222_CAPIF_Discover_Service_API/requirements.txt @@ -4,5 +4,5 @@ python_dateutil >= 2.6.0 setuptools >= 21.0.0 Flask == 2.0.3 pymongo == 4.0.1 -flask_jwt_extended -pyopenssl \ No newline at end of file +flask_jwt_extended == 4.4.4 +pyopenssl == 23.0.0 diff --git a/services/TS29222_CAPIF_Events_API/capif_events/__main__.py b/services/TS29222_CAPIF_Events_API/capif_events/__main__.py index ef555ce5..96c7a164 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/__main__.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/__main__.py @@ -16,7 +16,6 @@ from multiprocessing import Process from threading import Thread from flask_executor import Executor -from flask_apscheduler import APScheduler from logging.handlers import RotatingFileHandler @@ -61,8 +60,7 @@ def verbose_formatter(): configure_logging(app.app) executor = Executor(app.app) subscriber = Subscriber() -scheduler = APScheduler() -scheduler.init_app(app.app) + @app.app.before_first_request def create_listener_message(): diff --git a/services/TS29222_CAPIF_Events_API/capif_events/core/consumer_messager.py b/services/TS29222_CAPIF_Events_API/capif_events/core/consumer_messager.py index dc61cd1e..4424fba9 100644 --- a/services/TS29222_CAPIF_Events_API/capif_events/core/consumer_messager.py +++ b/services/TS29222_CAPIF_Events_API/capif_events/core/consumer_messager.py @@ -25,10 +25,10 @@ def listen(self): self.notification.send_notifications(raw_message["data"].decode('utf-8')) elif raw_message["type"] == "message" and raw_message["channel"].decode('utf-8') == "internal-messages": - message, invoker_id = raw_message["data"].decode('utf-8').split(":") + message, *invoker_id = raw_message["data"].decode('utf-8').split(":") if message == "invoker-removed": current_app.logger.debug("Recevived message, invoker remove, removing event subscriptions") - self.event_ops.delete_all_events(invoker_id) + self.event_ops.delete_all_events(invoker_id[0]) diff --git a/services/TS29222_CAPIF_Events_API/requirements.txt b/services/TS29222_CAPIF_Events_API/requirements.txt index 91e31cef..279753e4 100644 --- a/services/TS29222_CAPIF_Events_API/requirements.txt +++ b/services/TS29222_CAPIF_Events_API/requirements.txt @@ -4,9 +4,9 @@ python_dateutil >= 2.6.0 setuptools >= 21.0.0 Flask == 2.0.3 pymongo == 4.0.1 -flask_jwt_extended -pyopenssl -rfc3987 -redis -flask_executor -Flask-APScheduler +flask_jwt_extended == 4.4.4 +pyopenssl == 23.0.0 +rfc3987 == 1.3.8 +redis == 4.5.1 +flask_executor == 1.0.0 + diff --git a/services/TS29222_CAPIF_Logging_API_Invocation_API/requirements.txt b/services/TS29222_CAPIF_Logging_API_Invocation_API/requirements.txt index 45c243e3..203f4fad 100644 --- a/services/TS29222_CAPIF_Logging_API_Invocation_API/requirements.txt +++ b/services/TS29222_CAPIF_Logging_API_Invocation_API/requirements.txt @@ -5,6 +5,6 @@ setuptools >= 21.0.0 Flask == 2.0.3 pymongo == 4.0.1 elasticsearch == 8.4.3 -flask_jwt_extended -pyopenssl -flask-mqtt +flask_jwt_extended == 4.4.4 +pyopenssl == 23.0.0 + diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/__main__.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/__main__.py index b816113f..d6936035 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/__main__.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/__main__.py @@ -10,6 +10,8 @@ from pymongo import MongoClient from .config import Config from logging.handlers import RotatingFileHandler +from .core.consumer_messager import Subscriber +from flask_executor import Executor def configure_logging(app): @@ -50,6 +52,12 @@ def verbose_formatter(): jwt = JWTManager(app.app) configure_logging(app.app) +executor = Executor(app.app) +subscriber = Subscriber() + +@app.app.before_first_request +def up_listener(): + executor.submit(subscriber.listen) if __name__ == '__main__': diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py index 1d67744d..54b2e0e6 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/controllers/default_controller.py @@ -79,6 +79,7 @@ def apf_id_service_apis_service_api_id_delete(service_api_id, apf_id): # noqa: if res.status_code == 204: current_app.logger.info("Removed service published") publisher_ops.publish_message("events", "SERVICE_API_UNAVAILABLE") + publisher_ops.publish_message("internal-messages", f"service-removed:{service_api_id}") return res diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/consumer_messager.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/consumer_messager.py new file mode 100644 index 00000000..8108f450 --- /dev/null +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/consumer_messager.py @@ -0,0 +1,29 @@ +# subscriber.py +import redis +import time +import sys +import json +import asyncio +from threading import Thread +from .internal_service_ops import InternalServiceOps +from flask import current_app + +class Subscriber(): + + def __init__(self): + self.r = redis.Redis(host='redis', port=6379, db=0) + self.security_ops = InternalServiceOps() + self.p = self.r.pubsub() + self.p.subscribe("internal-messages") + + def listen(self): + current_app.logger.info("Listening publish messages") + for raw_message in self.p.listen(): + if raw_message["type"] == "message" and raw_message["channel"].decode('utf-8') == "internal-messages": + message, *ids = raw_message["data"].decode('utf-8').split(":") + if message == "provider-removed": + self.security_ops.delete_intern_service(ids[1]) + + + + diff --git a/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/internal_service_ops.py b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/internal_service_ops.py new file mode 100644 index 00000000..70855c57 --- /dev/null +++ b/services/TS29222_CAPIF_Publish_Service_API/published_apis/core/internal_service_ops.py @@ -0,0 +1,13 @@ + +from flask import current_app +from .resources import Resource + +class InternalServiceOps(Resource): + + def delete_intern_service(self, apf_id): + + current_app.logger.info("Provider removed, removing services published by APF") + mycol = self.db.get_col_by_name(self.db.service_api_descriptions) + my_query = {'apf_id': apf_id} + mycol.delete_many(my_query) + current_app.logger.info("Removed service") \ No newline at end of file diff --git a/services/TS29222_CAPIF_Publish_Service_API/requirements.txt b/services/TS29222_CAPIF_Publish_Service_API/requirements.txt index 0e173577..acf2b9c8 100644 --- a/services/TS29222_CAPIF_Publish_Service_API/requirements.txt +++ b/services/TS29222_CAPIF_Publish_Service_API/requirements.txt @@ -4,7 +4,8 @@ python_dateutil >= 2.6.0 setuptools >= 21.0.0 Flask == 2.0.3 pymongo == 4.0.1 -flask_jwt_extended -pyopenssl -redis +flask_jwt_extended == 4.4.4 +pyopenssl == 23.0.0 +redis == 4.5.1 +flask_executor == 1.0.0 diff --git a/services/TS29222_CAPIF_Security_API/capif_security/__main__.py b/services/TS29222_CAPIF_Security_API/capif_security/__main__.py index e98259b9..d24b496d 100644 --- a/services/TS29222_CAPIF_Security_API/capif_security/__main__.py +++ b/services/TS29222_CAPIF_Security_API/capif_security/__main__.py @@ -8,7 +8,6 @@ from .core.consumer_messager import Subscriber from threading import Thread from flask_executor import Executor -from flask_apscheduler import APScheduler from logging.handlers import RotatingFileHandler import sys @@ -56,8 +55,6 @@ def main(): JWTManager(app.app) subscriber = Subscriber() - scheduler = APScheduler() - scheduler.init_app(app.app) configure_logging(app.app) executor = Executor(app.app) diff --git a/services/TS29222_CAPIF_Security_API/capif_security/core/consumer_messager.py b/services/TS29222_CAPIF_Security_API/capif_security/core/consumer_messager.py index 71309d8f..cd8b22dd 100644 --- a/services/TS29222_CAPIF_Security_API/capif_security/core/consumer_messager.py +++ b/services/TS29222_CAPIF_Security_API/capif_security/core/consumer_messager.py @@ -17,11 +17,15 @@ def __init__(self): self.p.subscribe("internal-messages") def listen(self): + current_app.logger.info("Listening security context messages") for raw_message in self.p.listen(): if raw_message["type"] == "message" and raw_message["channel"].decode('utf-8') == "internal-messages": - message, invoker_id = raw_message["data"].decode('utf-8').split(":") + message, *ids = raw_message["data"].decode('utf-8').split(":") if message == "invoker-removed": - self.security_ops.delete_intern_servicesecurity(invoker_id) + self.security_ops.delete_intern_servicesecurity(ids[0]) + if message == "provider-removed" or message == "service-removed": + self.security_ops.update_intern_servicesecurity(ids[0]) + diff --git a/services/TS29222_CAPIF_Security_API/capif_security/core/internal_security_ops.py b/services/TS29222_CAPIF_Security_API/capif_security/core/internal_security_ops.py index 5b30874f..d531f79f 100644 --- a/services/TS29222_CAPIF_Security_API/capif_security/core/internal_security_ops.py +++ b/services/TS29222_CAPIF_Security_API/capif_security/core/internal_security_ops.py @@ -10,4 +10,19 @@ def delete_intern_servicesecurity(self, api_invoker_id): mycol = self.db.get_col_by_name(self.db.security_info) my_query = {'api_invoker_id': api_invoker_id} mycol.delete_many(my_query) - current_app.logger.info("Removed security context") \ No newline at end of file + current_app.logger.info("Removed security context") + + def update_intern_servicesecurity(self, id): + + current_app.logger.info("Provider Removed, updating security context") + security_col = self.db.get_col_by_name(self.db.security_info) + + + security_contexts = security_col.find({"$or":[{"security_info.aef_id":id}, {"security_info.api_id":id}]}) + + for security_context in security_contexts: + new_security_info = [info for info in security_context["security_info"] if info["aef_id"]!=id and info["api_id"] != id] + security_context["security_info"] = new_security_info + security_col.update_one({'api_invoker_id':security_context["api_invoker_id"]}, {"$set":security_context}) + + current_app.logger.info("Updated security context") \ No newline at end of file diff --git a/services/TS29222_CAPIF_Security_API/requirements.txt b/services/TS29222_CAPIF_Security_API/requirements.txt index 0854cc80..578188ff 100644 --- a/services/TS29222_CAPIF_Security_API/requirements.txt +++ b/services/TS29222_CAPIF_Security_API/requirements.txt @@ -4,10 +4,8 @@ python_dateutil >= 2.6.0 setuptools >= 21.0.0 Flask == 2.0.3 pymongo == 4.0.1 -flask_jwt_extended -pyjwt -rfc3987 -redis -pyopenssl -flask_executor -Flask-APScheduler \ No newline at end of file +flask_jwt_extended == 4.4.4 +pyopenssl == 23.0.0 +rfc3987 == 1.3.8 +redis == 4.5.1 +flask_executor == 1.0.0