From b662499ffef95c22a1b10f3b8090184dc29ede22 Mon Sep 17 00:00:00 2001 From: JorgeEcheva26 <jorge.echevarriauribarri.practicas@telefonica.es> Date: Fri, 28 Mar 2025 11:32:33 +0100 Subject: [PATCH 1/5] future updates, upgrade of api translator and adaptation to supported features requierement for new release --- README.md | 14 +-- README_pipy.md | 3 +- ...capif .jpg => flows-updated_opencapif.jpg} | Bin doc/sdk_full_documentation.md | 19 ++- opencapif_sdk/api_schema_translator.py | 79 +++++++++--- opencapif_sdk/capif_event_feature.py | 20 +-- opencapif_sdk/capif_logging_feature.py | 4 +- opencapif_sdk/capif_provider_connector.py | 119 ++++++++++++++++++ opencapif_sdk/service_discoverer.py | 18 +-- scripts/capif_sdk_register.json | 10 ++ scripts/utilities.py | 2 +- test/capif_sdk_config_release_2.json | 97 ++++++++++++++ test/capif_sdk_config_sample_test.json | 2 +- test/main.py | 2 +- test/test1.json | 8 +- test/test_main.py | 14 ++- 16 files changed, 352 insertions(+), 59 deletions(-) rename doc/images/{flows-updated_opencapif .jpg => flows-updated_opencapif.jpg} (100%) create mode 100644 scripts/capif_sdk_register.json create mode 100644 test/capif_sdk_config_release_2.json diff --git a/README.md b/README.md index df44a94..744840a 100644 --- a/README.md +++ b/README.md @@ -88,19 +88,19 @@ OpenCAPIF SDK brings a set of functions to integrate with the 5G Core's function | /registrations/{registrationId} (PUT) | [update_provider()](./doc/sdk_full_documentation.md#update-and-offboard-provider) | Updates a service provider's registration for a specific `registrationId`. | | /registrations/{registrationId} (DELETE) | [offboard_provider()](./doc/sdk_full_documentation.md#update-and-offboard-provider) | Deletes a service provider's registration for a specific `registrationId`. | | /allServiceAPIs (GET) | [discover()](./doc/sdk_full_documentation.md#discover-process) | Retrieves a list of all available service APIs. | -| /trustedInvokers (PUT//POST) | [get_tokens()](./doc/sdk_full_documentation.md#discover-process) | Registers or updates trusted invokers. | -| /securities/{securityId}/token (GET) | [get_tokens()](./doc/sdk_full_documentation.md#obtain-invoker-tokens) | Retrieves a security token for a specific `securityId`. This JWT token is used to query the targeted services. | +| /trustedInvokers (PUT//POST) | [get_tokens(supp_features)](./doc/sdk_full_documentation.md#discover-process) | Registers or updates trusted invokers. | +| /securities/{securityId}/token (GET) | [get_tokens(supp_features)](./doc/sdk_full_documentation.md#obtain-invoker-tokens) | Retrieves a security token for a specific `securityId`. This JWT token is used to query the targeted services. | | /{apfId}/service-apis(POST) | [publish_services()](./doc/sdk_full_documentation.md#services-publishing) | Registers a new service API into the system for a specific `apfId` | | /{apfId}/service-apis/{serviceApiId} (DELETE) | [unpublish_service()](./doc/sdk_full_documentation.md#services-deletion) | Deletes a service API from the system for a specific `apfId`and `serviceApiId` | | /{apfId}/service-apis/{serviceApiId} (PUT) | [update_service()](./doc/sdk_full_documentation.md#services-update) | Updates the details of an existing service API for a specific `apfId`and `serviceApiId` | | /{apfId}/service-apis/{serviceApiId} (GET) | [get_service()](./doc/sdk_full_documentation.md#get-services) | Retrieves the details of a specific service API for a specific `apfId` and `serviceApiId` | | /{apfId}/service-apis (GET) | [get_all_services()](./doc/sdk_full_documentation.md#get-all-services) | Retrieves a list of all available service APIs for a specific `apfId` | | /aef-security/v1/check-authentication (POST) | [check_authentication(supported_features)](./doc/sdk_full_documentation.md#check_authentication) | This custom operation allows the API invoker to confirm the `supported_features` from the API exposing function(AEF) | -| /api-invocation-logs/v1/{aefId}/logs (POST) | [create_logs(aefId, jwt)](./doc/sdk_full_documentation.md#create_logs) | This operation allows to the Provider to notice to the CCF about the query of an invoker with the JWT token recieved -| /capif-events/v1/{subscriberId}/subscriptions (POST) | [create_subscription(name, id)](./doc/sdk_full_documentation.md#create_subscription) | This operation allows to the Invoker/AEF/APF/AMF to ask to the CCF about notifications related to certain functionalities. +| /api-invocation-logs/v1/{aefId}/logs (POST) | [create_logs(aefId, jwt,supp_features)](./doc/sdk_full_documentation.md#create_logs) | This operation allows to the Provider to notice to the CCF about the query of an invoker with the JWT token recieved +| /capif-events/v1/{subscriberId}/subscriptions (POST) | [create_subscription(name, id, supp_features)](./doc/sdk_full_documentation.md#create_subscription) | This operation allows to the Invoker/AEF/APF/AMF to ask to the CCF about notifications related to certain functionalities. | /capif-events/v1/{subscriberId}/subscriptions/{subscriptionId} (DELETE) | [delete_subscription(name, id)](./doc/sdk_full_documentation.md#delete_subscription) | This operation allows to the Invoker/AEF/APF/AMF to withdraw the petition to receive notifications related to certain functionalities. -| /capif-events/v1/{subscriberId}/subscriptions/{subscriptionId} (PUT) | [update_subscription(name, id)](./doc/sdk_full_documentation.md#update_subscription) | This operation allows to the Invoker/AEF/APF/AMF to modify to the petition to receive notifications related to certain functionalities. **ONLY AVAILABLE IN OPENCAPIF RELEASE 2** -| /capif-events/v1/{subscriberId}/subscriptions/{subscriptionId} (PATCH) | [patch_subscription(name, id)](./doc/sdk_full_documentation.md#patch_subscription) | This operation allows to the Invoker/AEF/APF/AMF to modify to the petition to receive notifications related to certain functionalities. **ONLY AVAILABLE IN OPENCAPIF RELEASE 2** +| /capif-events/v1/{subscriberId}/subscriptions/{subscriptionId} (PUT) | [update_subscription(name, id, supp_features)](./doc/sdk_full_documentation.md#update_subscription) | This operation allows to the Invoker/AEF/APF/AMF to modify to the petition to receive notifications related to certain functionalities. **ONLY AVAILABLE IN OPENCAPIF RELEASE 2** +| /capif-events/v1/{subscriberId}/subscriptions/{subscriptionId} (PATCH) | [patch_subscription(name, id, supp_features)](./doc/sdk_full_documentation.md#patch_subscription) | This operation allows to the Invoker/AEF/APF/AMF to modify to the petition to receive notifications related to certain functionalities. **ONLY AVAILABLE IN OPENCAPIF RELEASE 2** NOTE: Above mentioned CAPIF APIs are defined in these 3GPP references: - [CAPIF Invoker API specification](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_API_Invoker_Management_API.yaml) @@ -161,7 +161,7 @@ Now, it is described in 4 simple steps how a Provider can be developed in just s provider.onboard_provider() #translator = opencapif_sdk.api_schema_translator("./path/to/openapi.yaml") - #translator.build("api_description_name",ip="0.0.0.0",port=9090,supported_features="0",api_supp_features="0") + #translator.build("https://192.168.1.10:8080/exampleAPI/v1", "0", "0") provider.api_description_path = "./api_description_name.json" APF = provider.provider_capif_ids["APF-1"] diff --git a/README_pipy.md b/README_pipy.md index 09b4ab3..19aaa41 100644 --- a/README_pipy.md +++ b/README_pipy.md @@ -198,7 +198,8 @@ This schema could be obtained by applying this code. import opencapif_sdk translator = api_schema_translator("./path/to/openapi.yaml") - translator.build("api_description_name",ip="0.0.0.0",port=9090) + translator.build("https://192.168.1.10:8080/exampleAPI/v1", "0", "0") + ``` This code will read `openapi.yaml`, ensure the structure of it and translate the content into ServiceAPIDescription schema, then will create a .json named `api_description_name`. Also it is necessary to fill the ip and port fields to create correctly the schema. # OpenCAPIF SDK known issues diff --git a/doc/images/flows-updated_opencapif .jpg b/doc/images/flows-updated_opencapif.jpg similarity index 100% rename from doc/images/flows-updated_opencapif .jpg rename to doc/images/flows-updated_opencapif.jpg diff --git a/doc/sdk_full_documentation.md b/doc/sdk_full_documentation.md index 990c0a8..bf4e610 100644 --- a/doc/sdk_full_documentation.md +++ b/doc/sdk_full_documentation.md @@ -166,10 +166,12 @@ The provider must be onboarded before using these features. ### Create logs OpenCAPIF SDK references: -- **Function**: `create_logs(aefId, jwt)` +- **Function**: `create_logs(aefId, jwt, supp_features)` The provider notifies to the CCF that the published API has been used by certain invoker. +`supp_features` parameter is optional and it stands for communicating to the CCF the supported features.It's default value its 0. + For leveraging this feature the Provider must have onboarded and published an API previously. **Required SDK input**: @@ -182,10 +184,12 @@ For leveraging this feature the Provider must have onboarded and published an AP ### Create subscription OpenCAPIF SDK references: -- **Function**: `create_subscription(name, id)` +- **Function**: `create_subscription(name, id, supp_features)` The provider ask to the CCF about notifications related to services such as SERVICE_API_AVAILABLE or API_INVOKER_UPDATED. +`supp_features` parameter is optional and it stands for communicating to the CCF the supported features.It's default value its 0. + This services are specificated in [CAPIF Events API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Events_API.yaml) explained in [SDK configuration](./sdk_configuration.md#descriptions-of-capif_sdk_config-fields) For leveraging this feature the Provider must have onboarded previously. @@ -213,12 +217,14 @@ For leveraging this feature the Provider must have onboarded and created a subsc ### Update subscription OpenCAPIF SDK references: -- **Function**: `update_subscription(name, id)` +- **Function**: `update_subscription(name, id, supp_features)` The provider ask to the CCF about updating the subscription for receiving different services such as SERVICE_API_AVAILABLE or API_INVOKER_UPDATED, changing the URL for receiving the notifications... This services are specificated in [CAPIF Events API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Events_API.yaml) explained in [SDK configuration](./sdk_configuration.md#descriptions-of-capif_sdk_config-fields) +`supp_features` parameter is optional and it stands for communicating to the CCF the supported features.It's default value its 0. + For leveraging this feature the Provider must have onboarded and created a subscription previously.  @@ -238,6 +244,8 @@ OpenCAPIF SDK references: The provider ask to the CCF about updating the subscription for receiving different services such as SERVICE_API_AVAILABLE or API_INVOKER_UPDATED. +`supp_features` parameter is optional and it stands for communicating to the CCF the supported features.It's default value its 0. + This services are specificated in [CAPIF Events API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Events_API.yaml) explained in [SDK configuration](./sdk_configuration.md#events_configuration) For leveraging this feature the Provider must have onboarded and created a subscription previously. @@ -295,10 +303,11 @@ Use the [discover_filter](./sdk_configuration.md) to retrieve access to target A ### Obtain JWT Tokens OpenCAPIF SDK references: -- **Function**: `get_tokens()` +- **Function**: `get_tokens(supp_features)` - **Script**: `invoker_service_get_token.py` The SDK facilitates JWT token creation for secure access to target APIs. This process stores JWT access token in `capif_api_security_context_details.json`. +`supp_features` parameter is optional and it stands for retrieve the token of the services that have certain supported features.It's default value its 0.  @@ -337,7 +346,7 @@ This schema could be obtained by applying this code. import opencapif_sdk translator = opencapif_sdk.api_schema_translator("./path/to/openapi.yaml") - translator.build("api_description_name",ip="0.0.0.0",port=9090,supported_features="0",api_supp_features="0") + translator.build("https://192.168.1.10:8080/exampleAPI/v1", "0", "0") ``` This code will read `openapi.yaml`, ensure the structure of it and translate the content into ServiceAPIDescription schema, then will create a .json named `api_description_name`. Also it is necessary to fill the ip and port fields to create correctly the schema. The supported_features and api_supp_features fields corresponds to the capabilities of the provider and the service that the user is sharing. diff --git a/opencapif_sdk/api_schema_translator.py b/opencapif_sdk/api_schema_translator.py index a55ae0c..2d4ebc0 100644 --- a/opencapif_sdk/api_schema_translator.py +++ b/opencapif_sdk/api_schema_translator.py @@ -2,9 +2,9 @@ import json import logging import os import re +import socket import yaml - log_path = 'logs/builder_logs.log' log_dir = os.path.dirname(log_path) @@ -34,31 +34,81 @@ class api_schema_translator: self.api_info = self.__load_api_file(self.api_path) self.__validate_api_info() - def build(self, api_name, supported_features, api_supp_features, ip=None, port=None, fqdn=None, ipv6Addr=None): + def __validate_ip_port(self, ip, port): + """Validates if an IP address and port are correctly formatted.""" + try: + socket.inet_aton(ip) # Validate IPv4 + return isinstance(port, int) and 0 < port < 65536 + except socket.error: + return False + + def __parse_url(self, url): + """ + Parses the URL to extract apiRoot, apiName, and apiVersion. + Handles: + - URLs with or without ports. + - API root as an IP, FQDN, or preconfigured prefix. + - Assigns default ports for HTTP (80) and HTTPS (443) if missing. + """ + + pattern = r"^(https?:\/\/)?([\w\.-]+|\[.*\])(?::(\d+))?(\/[\w\/-]+)?\/([\w-]+)\/(v\d+)$" + match = re.match(pattern, url) + + if not match: + self.logger.error(f"Invalid URL format: {url}") + return None, None, None, None, None, None, None + + scheme, host, port, api_root, api_name, api_version = match.groups() + + # Asignar puerto por defecto si no está presente + if not port: + port = 443 if scheme == "https" else 80 # Si no hay esquema, asumimos HTTPS + else: + port = int(port) + + if not api_name or not api_version: + self.logger.error(f"URL must contain API name and version in the format: <apiRoot>/<apiName>/v<version>") + return None, None, None, None, None, None, None + + # Si apiRoot está presente, eliminar "/" innecesarios + api_root = api_root.strip('/') if api_root else None + + if re.match(r"^\d{1,3}(\.\d{1,3}){3}$", host): # IPv4 + return host, port, None, None, api_root, api_name, api_version + elif re.match(r"^\[.*\]$", host): # IPv6 (brackets format) + return None, port, None, host.strip("[]"), api_root, api_name, api_version + else: # FQDN + return None, port, host, None, api_root, api_name, api_version + + def build(self, url, supported_features, api_supp_features): """ Builds the API description and saves it to a JSON file. - Supports either IPv4 (ip), IPv6 (ipv6Addr), or FQDN (fqdn). + Extracts apiRoot, apiName, and apiVersion. + Supports IP, FQDN, and URLs with or without explicit ports. """ - # Validate required fields + # Validación de campos requeridos if not supported_features or not api_supp_features: self.logger.error("Both 'supported_features' and 'api_supp_features' are required. Aborting build.") return - # Validate that at least one of ip, ipv6Addr, or fqdn is provided - if not (ip or ipv6Addr or fqdn): - self.logger.error("At least one of 'ip', 'ipv6Addr', or 'fqdn' must be provided. Aborting build.") + # Parseamos la URL + ip, port, fqdn, ipv6Addr, api_root, api_name, api_version = self.__parse_url(url) + + # Validamos que al menos una dirección sea válida + if not (ip or fqdn or ipv6Addr or api_root): + self.logger.error("Invalid URL: No valid IP, IPv6, FQDN, or API root found. Aborting build.") return - # Validate IP and port if IPv4 is provided + # Validamos IP y puerto si es IPv4 if ip and not self.__validate_ip_port(ip, port): self.logger.error("Invalid IP or port. Aborting build.") return - # Build the API data + # Construcción de la API try: api_data = { - "apiName": self.api_info["info"].get("title", api_name), - "aefProfiles": self.__build_aef_profiles(ip, port, fqdn, ipv6Addr), + "apiName": api_name, + "aefProfiles": self.__build_aef_profiles(ip, port, api_version, fqdn, ipv6Addr), "description": self.api_info["info"].get("description", "No description provided"), "supportedFeatures": supported_features, "shareableInfo": { @@ -73,7 +123,7 @@ class api_schema_translator: "ccfId": "string" } - # Save the API data to a JSON file + # Guardamos los datos en un archivo JSON with open(f"{api_name}.json", "w") as outfile: json.dump(api_data, outfile, indent=4) self.logger.info(f"API description saved to {api_name}.json") @@ -81,7 +131,6 @@ class api_schema_translator: except Exception as e: self.logger.error(f"An error occurred during the build process: {e}") - def __load_api_file(self, api_file: str): """Loads the Swagger API configuration file and converts YAML to JSON format if necessary.""" try: @@ -112,7 +161,7 @@ class api_schema_translator: else: self.logger.info("All required components are present in the API specification.") - def __build_aef_profiles(self, ip, port, fqdn=None, ipv6Addr=None): + def __build_aef_profiles(self, ip, port, api_version, fqdn=None, ipv6Addr=None, ): """Builds the aefProfiles section based on the paths and components in the API info.""" aef_profiles = [] @@ -149,7 +198,7 @@ class api_schema_translator: "aefId": "", # Placeholder AEF ID "versions": [ { - "apiVersion": "v1", + "apiVersion": f"{api_version}", "expiry": "2100-11-30T10:32:02.004Z", "resources": resources, "custOperations": [ diff --git a/opencapif_sdk/capif_event_feature.py b/opencapif_sdk/capif_event_feature.py index 90b4664..ba28632 100644 --- a/opencapif_sdk/capif_event_feature.py +++ b/opencapif_sdk/capif_event_feature.py @@ -30,7 +30,7 @@ logging.basicConfig( class capif_invoker_event_feature(capif_invoker_connector): - def create_subscription(self, name): + def create_subscription(self, name, supp_features=0): invoker_capif_details = self.invoker_capif_details @@ -48,7 +48,7 @@ class capif_invoker_event_feature(capif_invoker_connector): "websocketUri": f"{self.capif_callback_url}", "requestWebsocketUri": True }, - "supportedFeatures": f"{self.supported_features}" + "supportedFeatures": f"{supp_features}" } try: @@ -150,7 +150,7 @@ class capif_invoker_event_feature(capif_invoker_connector): self.logger.error("Subscription file not found at path: %s", path) return None, {"error": "Subscription file not found"} - def update_subcription(self, name): + def update_subcription(self, name, supp_features=0): invoker_capif_details = self.invoker_capif_details subscriberId = invoker_capif_details["api_invoker_id"] @@ -167,7 +167,7 @@ class capif_invoker_event_feature(capif_invoker_connector): "websocketUri": f"{self.capif_callback_url}", "requestWebsocketUri": True }, - "supportedFeatures": f"{self.supported_features}" + "supportedFeatures": f"{supp_features}" } if os.path.exists(path): subscription = self._load_config_file(path) @@ -209,7 +209,7 @@ class capif_invoker_event_feature(capif_invoker_connector): class capif_provider_event_feature(capif_provider_connector): - def create_subscription(self, name, id): + def create_subscription(self, name, id, supp_features=0): subscriberId = id @@ -226,7 +226,7 @@ class capif_provider_event_feature(capif_provider_connector): "notificationDestination": f"{self.notification_destination}", "requestTestNotification": True, "websockNotifConfig": self.websock_notif_config, - "supportedFeatures": f"{self.supported_features}" + "supportedFeatures": f"{supp_features}" } number_low = number.lower() @@ -345,7 +345,7 @@ class capif_provider_event_feature(capif_provider_connector): self.logger.error("Subscription file not found at path: %s", path) return None, {"error": "Subscription file not found"} - def update_subcription(self, name, id): + def update_subcription(self, name, id, supp_features=0): subscriberId = id @@ -362,7 +362,7 @@ class capif_provider_event_feature(capif_provider_connector): "notificationDestination": f"{self.notification_destination}", "requestTestNotification": True, "websockNotifConfig": self.websock_notif_config, - "supportedFeatures": f"{self.supported_features}" + "supportedFeatures": f"{supp_features}" } if os.path.exists(path): @@ -420,5 +420,5 @@ class capif_provider_event_feature(capif_provider_connector): self.logger.error("Subscription file not found at path: %s", path) return None, {"error": "Subscription file not found"} - def patch_subcription(self, name, id): - self.update_subcription(self, name, id) + def patch_subcription(self, name, id, supp_features=0): + self.update_subcription(self, name, id, supp_features) diff --git a/opencapif_sdk/capif_logging_feature.py b/opencapif_sdk/capif_logging_feature.py index 8c8c2c5..cbd25de 100644 --- a/opencapif_sdk/capif_logging_feature.py +++ b/opencapif_sdk/capif_logging_feature.py @@ -195,7 +195,7 @@ class capif_logging_feature: if not self.api_id: raise ValueError(f"No ID was found for the API '{name}'.") - def create_logs(self, aefId, jwt): + def create_logs(self, aefId, jwt, supp_features=0): api_invoker_id = self._decrypt_jwt(jwt) @@ -216,7 +216,7 @@ class capif_logging_feature: "aefId": f"{aefId}", "apiInvokerId": f"{api_invoker_id}", "logs": [log_entry], - "supportedFeatures": f"{self.supported_features}" + "supportedFeatures": f"{supp_features}" } provider_details = self.__load_provider_api_details() AEF_api_prov_func_id = aefId diff --git a/opencapif_sdk/capif_provider_connector.py b/opencapif_sdk/capif_provider_connector.py index 58437f1..ce5cc83 100644 --- a/opencapif_sdk/capif_provider_connector.py +++ b/opencapif_sdk/capif_provider_connector.py @@ -1334,3 +1334,122 @@ class capif_provider_connector: self.logger.warning( f"Configuration file {config_file} not found. Using defaults or environment variables.") return {} + + def check_invoker_authentication(self, invoker_id, aef_id, api_id, supportedfeatures_rx, cert_rx): + service_security = self._get_trusted_invokers(invoker_id, aef_id) + + # Check if _get_trusted_invokers returned an error and propagate it + if isinstance(service_security, dict) and "status" in service_security: + return service_security + + result = self._check_service_security(service_security, aef_id, api_id, supportedfeatures_rx, cert_rx) + + return result + + def _get_trusted_invokers(self, invoker_id, aef_id): + if not aef_id: + return self._problem_details( + status=500, + title="Internal Server Error", + detail="AEF ID is missing.", + instance=f"/capif-security/v1/trustedInvokers/{invoker_id}", + cause="MissingAEFId" + ) + if not invoker_id: + return self._problem_details( + status=500, + title="Internal Server Error", + detail="Invoker ID is missing.", + instance=f"/capif-security/v1/trustedInvokers/{invoker_id}", + cause="MissingAEFId" + ) + + url = f"{self.capif_https_url}/capif-security/v1/trustedInvokers/{invoker_id}?authenticationInfo={True}&authorizationInfo={True}" + + provider_details = self._load_provider_api_details() + key = self._find_key_by_value(data=provider_details, target_value=aef_id) + + if not key: + return self._problem_details( + status=500, + title="Internal Server Error", + detail=f"No key found for AEF ID: {aef_id}.", + instance=url, + cause="KeyNotFound" + ) + + keylow = key.lower() + cert = ( + os.path.join(self.provider_folder, f"{keylow}.crt"), + os.path.join(self.provider_folder, f"{key}_private_key.key") + ) + + try: + response = requests.get( + url, + headers={"Content-Type": "application/json"}, + cert=cert, + verify=os.path.join(self.provider_folder, "ca.crt") + ) + response.raise_for_status() + return response.json() + + except requests.RequestException as e: + return self._problem_details( + status=502, + title="Bad Gateway", + detail=f"Failed to retrieve trusted invokers: {str(e)}", + instance=url, + cause="RequestFailure" + ) + + def _check_service_security(self, service_security, aef_id, api_id, supportedfeatures_rx, cert_rx): + service_security_selected = self._find_security_info(service_security, aef_id, api_id) + + # If _find_security_info returns an error, propagate it + if isinstance(service_security_selected, dict) and "status" in service_security_selected: + return service_security_selected + + if service_security_selected["selSecurityMethod"] == "PKI": + # TOBEDONE + print("To be done") + + return {"status": 200, "message": {"supportedFeatures": supportedfeatures_rx}} + + def _find_security_info(self, service_security, aef_id, api_id): + if not service_security or "securityInfo" not in service_security: + return self._problem_details( + status=500, + title="Internal Server Error", + detail="Service security information is missing or malformed.", + instance="/capif-security/v1/securityInfo", + cause="MalformedServiceSecurity" + ) + + for entry in service_security["securityInfo"]: + if entry["aefId"] == aef_id and entry["apiId"] == api_id: + return entry + + return self._problem_details( + status=404, + title="Security Information Not Found", + detail=f"No security information found for AEF ID: {aef_id}, API ID: {api_id}.", + instance=f"/capif-security/v1/securityInfo/{aef_id}/{api_id}", + cause="SecurityInfoNotFound" + ) + + def _problem_details(self, status, title, detail, instance, cause, invalidParams=None, supportedFeatures=None): + """Generates the error message structure according to the ProblemDetails standard""" + problem = { + "type": "https://example.com/probs/security-error", + "title": title, + "status": status, + "detail": detail, + "instance": instance, + "cause": cause + } + if invalidParams: + problem["invalidParams"] = invalidParams + if supportedFeatures: + problem["supportedFeatures"] = supportedFeatures + return problem diff --git a/opencapif_sdk/service_discoverer.py b/opencapif_sdk/service_discoverer.py index 5a09156..1865793 100644 --- a/opencapif_sdk/service_discoverer.py +++ b/opencapif_sdk/service_discoverer.py @@ -163,11 +163,11 @@ class service_discoverer: url += "/" return url - def get_security_context(self): + def get_security_context(self, supp_features): self.logger.info("Getting security context for all API's filtered") self.logger.info("Trying to update security context") - self.__update_security_service() + self.__update_security_service(supp_features) self.__cache_security_context() def get_access_token(self): @@ -195,7 +195,7 @@ class service_discoverer: "Error when saving the security context: %s", str(e)) raise - def __update_security_service(self): + def __update_security_service(self, supp_features): """ Actualiza el servicio de seguridad. @@ -212,7 +212,7 @@ class service_discoverer: "websocketUri": "string", "requestWebsocketUri": True }, - "supportedFeatures": f"{self.supported_features}" + "supportedFeatures": f"{supp_features}" } number_of_apis = len( @@ -244,7 +244,7 @@ class service_discoverer: if response.status_code == 404: self.logger.warning( "Received 404 exception from target CAPIF. This means it is the first time this CAPIF user is getting the JWT token, redirecting to register security service in CAPIF. The process continues correctly.") - self.__register_security_service() + self.__register_security_service(supp_features) else: self.logger.error("HTTP error occurred: %s", str(http_err)) raise @@ -254,7 +254,7 @@ class service_discoverer: "Error trying to update Security context: %s", str(e)) raise - def __register_security_service(self): + def __register_security_service(self, supp_features): """ :param api_id: El id del API devuelto por descubrir servicios :param aef_id: El aef_id devuelto por descubrir servicios @@ -270,7 +270,7 @@ class service_discoverer: "websocketUri": "string", "requestWebsocketUri": True }, - "supportedFeatures": f"{self.supported_features}" + "supportedFeatures": f"{supp_features}" } number_of_apis = len( @@ -445,9 +445,9 @@ class service_discoverer: self.invoker_capif_details["access_token"] = token self.__cache_security_context() - def get_tokens(self): + def get_tokens(self, supp_features=0): - self.get_security_context() + self.get_security_context(supp_features) token = self.get_access_token() self.token = token self.save_security_token(token) diff --git a/scripts/capif_sdk_register.json b/scripts/capif_sdk_register.json new file mode 100644 index 0000000..fdaef6d --- /dev/null +++ b/scripts/capif_sdk_register.json @@ -0,0 +1,10 @@ +{ + "register_host": "localhost", + "capif_register_port": "443", + "capif_register_username": "admin", + "capif_register_password": "password123", + "capif_username":"echeva", + "capif_password":"echevapass", + "config_path":"./register", + "uuid":"" +} \ No newline at end of file diff --git a/scripts/utilities.py b/scripts/utilities.py index 0b4855f..61f9637 100755 --- a/scripts/utilities.py +++ b/scripts/utilities.py @@ -5,4 +5,4 @@ def get_config_file() -> str: def get_register_file() -> str: - return "../config/capif_sdk_register.json" \ No newline at end of file + return "../scripts/capif_sdk_register.json" \ No newline at end of file diff --git a/test/capif_sdk_config_release_2.json b/test/capif_sdk_config_release_2.json new file mode 100644 index 0000000..7618e8b --- /dev/null +++ b/test/capif_sdk_config_release_2.json @@ -0,0 +1,97 @@ +{ + "capif_host": "capifcore", + "register_host": "localhost", + "capif_https_port": "443", + "capif_register_port": "8084", + "capif_username": "echeva", + "capif_password": "echevapass", + "debug_mode": "True", + "invoker": { + "invoker_folder": "/Users/IDB0128/Documents/OpenCapif/test_invoker_certificate_folder", + "capif_callback_url": "http://localhost:5000", + "supported_features": "0", + "check_authentication_data": { + "ip": "", + "port": "" + }, + "cert_generation": { + "csr_common_name": "Echeva", + "csr_organizational_unit": "discovery", + "csr_organization": "telefonica", + "csr_locality": "madrid", + "csr_state_or_province_name": "madrid", + "csr_country_name": "ES", + "csr_email_address": "adios@gmail.com" + }, + "discover_filter": { + "api-name": "", + "api-version": "", + "comm-type": "", + "protocol": "", + "aef-id": "", + "data-format": "", + "api-cat": "", + "preferred-aef-loc": "", + "req-api-prov-name": "", + "supported-features": "", + "api-supported-features": "", + "ue-ip-addr": "", + "service-kpis": "" + }, + "events": { + "description": ["SERVICE_API_AVAILABLE"], + "eventFilters": [ + { + "apiIds": [""], + "apiInvokerIds": [""], + "aefIds": [""] + } + ] + } + }, + "provider": { + "provider_folder": "/Users/IDB0128/Documents/OpenCapif/test_provider_certificate_folder", + "supported_features": "0", + "cert_generation": { + "csr_common_name": "provider", + "csr_organizational_unit": "discovery", + "csr_organization": "telefonica", + "csr_locality": "madrid", + "csr_state_or_province_name": "madrid", + "csr_country_name": "ES", + "csr_email_address": "hola@gmail.com" + }, + "apfs": "2", + "aefs": "3", + "publish_req": { + "service_api_id": "", + "publisher_apf_id": "", + "publisher_aefs_ids": ["", ""] + }, + "api_description_path": "", + "events": { + "description": ["SERVICE_API_AVAILABLE"], + "eventFilters": [ + { + "apiIds": [""], + "apiInvokerIds": [""], + "aefIds": [""] + } + ], + "notificationDestination" : "http://localhost:5000", + "websockNotifConfig": { + "websocketUri" : "http://localhost:5000", + "requestWebsocketUri": true + } + }, + "log": { + "apiName": "test1", + "apiVersion": "v1", + "resourceName": "MONITORING_SUBSCRIPTIONS", + "uri": "/{scsAsId}/subscriptions", + "protocol": "HTTP_2", + "operation": "GET", + "result": "200" + } + } +} diff --git a/test/capif_sdk_config_sample_test.json b/test/capif_sdk_config_sample_test.json index 21f55ed..f6e1dbd 100644 --- a/test/capif_sdk_config_sample_test.json +++ b/test/capif_sdk_config_sample_test.json @@ -85,7 +85,7 @@ } }, "log": { - "apiName": "API of dummy Network-App to test", + "apiName": "test1", "apiVersion": "v1", "resourceName": "MONITORING_SUBSCRIPTIONS", "uri": "/{scsAsId}/subscriptions", diff --git a/test/main.py b/test/main.py index 520d07d..e8536d3 100644 --- a/test/main.py +++ b/test/main.py @@ -5,7 +5,7 @@ import json from opencapif_sdk import capif_invoker_connector, capif_provider_connector, service_discoverer, capif_logging_feature, capif_invoker_event_feature, capif_provider_event_feature -capif_sdk_config_path = "./capif_sdk_config_sample_test.json" +capif_sdk_config_path = "./capif_sdk_config_release_2.json" def preparation_for_update(APFs, AEFs, second_network_app_api,capif_provider_connector): diff --git a/test/test1.json b/test/test1.json index aa13df9..c0231a1 100644 --- a/test/test1.json +++ b/test/test1.json @@ -1,8 +1,8 @@ { - "apiName": "API of dummy Network-App to test", + "apiName": "test1", "aefProfiles": [ { - "aefId": "AEF31918dcf7bd894c86af1faf271a2d7", + "aefId": "AEFa6953ae0f359180257403c58e55a72", "versions": [ { "apiVersion": "v1", @@ -134,11 +134,11 @@ ], "interfaceDescriptions": [ { - "port": 9090, + "port": 8080, "securityMethods": [ "OAUTH" ], - "ipv4Addr": "0.0.0.0" + "ipv4Addr": "192.168.1.10" } ] } diff --git a/test/test_main.py b/test/test_main.py index 6d5dc3c..ec04c92 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -41,7 +41,7 @@ def test_provider_publish(test_provider_update): AEF1 = provider.provider_capif_ids['AEF-1'] translator = api_schema_translator("./test1.yaml") - translator.build("test1",ip="0.0.0.0",port=9090,supported_features="0",api_supp_features="0") + translator.build(url="https://192.168.1.10:8080/test1/v1",supported_features= "0",api_supp_features= "0") provider.api_description_path="./test1.json" # Update configuration file provider.publish_req['publisher_apf_id'] = APF1 @@ -80,6 +80,14 @@ def test_logs(test_provider_publish,tokens): capif_log.create_logs(aefId=AEF1,jwt=token) +""" def test_check_invoker(test_provider_publish,tokens): + provider=capif_provider_connector(capif_sdk_config_path) + discoverer = service_discoverer(config_file=capif_sdk_config_path) + AEF1 = provider.provider_capif_ids['AEF-1'] + api_invoker_id = discoverer.invoker_capif_details['api_invoker_id'] + provider.get_trusted_invokers(api_invoker_id,AEF1) + """ + def test_invoker_discover(invoker_setup,test_provider_publish): discoverer = service_discoverer(config_file=capif_sdk_config_path) discoverer.discover() @@ -89,7 +97,7 @@ def test_provider_unpublish_1(test_events): provider=capif_provider_connector(capif_sdk_config_path) APF1 = provider.provider_capif_ids['APF-1'] provider.publish_req['publisher_apf_id'] = APF1 - service_api_id = provider.provider_service_ids["API of dummy Network-App to test"] + service_api_id = provider.provider_service_ids["test1"] provider.publish_req['service_api_id'] = service_api_id provider.unpublish_service() @@ -99,7 +107,7 @@ def test_provider_update_service(test_provider_publish): AEF1 = provider.provider_capif_ids['AEF-1'] provider.publish_req['publisher_apf_id'] = APF1 provider.publish_req['publisher_aefs_ids'] = [AEF1] - service_api_id = provider.provider_service_ids["API of dummy Network-App to test"] + service_api_id = provider.provider_service_ids["test1"] provider.publish_req['service_api_id'] = service_api_id provider.api_description_path="test1.json" -- GitLab From 1857ff5d9443db7877eecb874d5f2576e7ec57b8 Mon Sep 17 00:00:00 2001 From: JorgeEcheva26 <jorge.echevarriauribarri.practicas@telefonica.es> Date: Fri, 28 Mar 2025 11:43:16 +0100 Subject: [PATCH 2/5] update in api translator,upgrade in supported features for new release, --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9d4f0f5..fe14cdf 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ with open(os.path.join(this_directory, "README_pipy.md"), encoding="utf-8") as f setup( name="opencapif_sdk", - version="0.1.20", + version="0.1.21", author="JorgeEcheva, dgs-cgm", author_email="jorge.echevarriauribarri.practicas@telefonica.com, daniel.garciasanchez@telefonica.com", description=( -- GitLab From 3a641d28714bf83ecef5b81ff8b54c6e78715cc1 Mon Sep 17 00:00:00 2001 From: JorgeEcheva26 <jorge.echevarriauribarri.practicas@telefonica.es> Date: Fri, 28 Mar 2025 11:54:59 +0100 Subject: [PATCH 3/5] gitlab update --- doc/sdk_full_documentation.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/sdk_full_documentation.md b/doc/sdk_full_documentation.md index bf4e610..7b5a4e0 100644 --- a/doc/sdk_full_documentation.md +++ b/doc/sdk_full_documentation.md @@ -12,7 +12,6 @@ Before using the SDK, the following steps should be completed: - Follow the [installation instructions](./sdk_developers.md), - Configure the SDK by completing the relevant sections in the [configuration guide](./sdk_configuration.md), depending on the CAPIF role the Network App will assume. - ## Available SDK Usage Modes  -- GitLab From 3c192d291cccbf18ba9da44bc7b672142c8c10f2 Mon Sep 17 00:00:00 2001 From: JorgeEcheva26 <jorge.echevarriauribarri.practicas@telefonica.es> Date: Fri, 28 Mar 2025 13:53:47 +0100 Subject: [PATCH 4/5] changes in onboarding and update of invokers and providers --- README.md | 8 ++++---- doc/sdk_full_documentation.md | 20 +++++++++++++++----- opencapif_sdk/capif_event_feature.py | 10 +++++----- opencapif_sdk/capif_invoker_connector.py | 16 ++++++++-------- opencapif_sdk/capif_logging_feature.py | 2 +- opencapif_sdk/capif_provider_connector.py | 16 ++++++++-------- opencapif_sdk/service_discoverer.py | 2 +- test/test1.json | 2 +- 8 files changed, 43 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 744840a..9feb29d 100644 --- a/README.md +++ b/README.md @@ -81,11 +81,11 @@ OpenCAPIF SDK brings a set of functions to integrate with the 5G Core's function | **3GPP CAPIF API** | **OpenCAPIF SDK function** | **Description** | |-------------------------------------------------------|-------------------------------------------------------------|-------------------------------------------------------------| -| /onboardedInvokers (POST) | [onboard_invoker()](./doc/sdk_full_documentation.md#invoker-onboarding) | Registers a new invoker. | -| /onboardedInvokers/{onboardingId} (PUT) | [update_invoker()](./doc/sdk_full_documentation.md#update-and-offboard-invoker) | Updates an existing invoker for a specific `onboardingId`. | +| /onboardedInvokers (POST) | [onboard_invoker(supp_features)](./doc/sdk_full_documentation.md#invoker-onboarding) | Registers a new invoker. | +| /onboardedInvokers/{onboardingId} (PUT) | [update_invoker(supp_features)](./doc/sdk_full_documentation.md#update-and-offboard-invoker) | Updates an existing invoker for a specific `onboardingId`. | | /onboardedInvokers/{onboardingId} (DELETE) | [offboard_invoker()](./doc/sdk_full_documentation.md#update-and-offboard-invoker) | Deletes an invoker for a specific `onboardingId`. | -| registrations (POST) | [onboard_provider()](./doc/sdk_full_documentation.md#provider-onboarding) | Registers a new service provider. | -| /registrations/{registrationId} (PUT) | [update_provider()](./doc/sdk_full_documentation.md#update-and-offboard-provider) | Updates a service provider's registration for a specific `registrationId`. | +| registrations (POST) | [onboard_provider(supp_features)](./doc/sdk_full_documentation.md#provider-onboarding) | Registers a new service provider. | +| /registrations/{registrationId} (PUT) | [update_provider(supp_features)](./doc/sdk_full_documentation.md#update-and-offboard-provider) | Updates a service provider's registration for a specific `registrationId`. | | /registrations/{registrationId} (DELETE) | [offboard_provider()](./doc/sdk_full_documentation.md#update-and-offboard-provider) | Deletes a service provider's registration for a specific `registrationId`. | | /allServiceAPIs (GET) | [discover()](./doc/sdk_full_documentation.md#discover-process) | Retrieves a list of all available service APIs. | | /trustedInvokers (PUT//POST) | [get_tokens(supp_features)](./doc/sdk_full_documentation.md#discover-process) | Registers or updates trusted invokers. | diff --git a/doc/sdk_full_documentation.md b/doc/sdk_full_documentation.md index 7b5a4e0..899db88 100644 --- a/doc/sdk_full_documentation.md +++ b/doc/sdk_full_documentation.md @@ -71,10 +71,12 @@ The provider_capif_ids variable stores the `provider_service_ids.json` content i ### Provider Onboarding OpenCAPIF SDK references: -- **Function**: `onboard_provider()` +- **Function**: `onboard_provider(supp_features)` - **Script**: `provider_capif_connector.py` The SDK simplifies the onboarding process, allowing providers to register multiple APFs and AEFs. All APFs, AEFs, and AMF certificates are created and stored in `provider_service_ids.json`. +`supp_features` parameter is optional and it stands for communicating to the CCF the supported features.It's default value its 0. +  @@ -152,12 +154,14 @@ Retrieve information about all previously published services in `service_receive ### Update and Offboard Provider OpenCAPIF SDK references: -- **Functions**: `update_provider()` and `offboard_provider()` +- **Functions**: `update_provider(supp_features)` and `offboard_provider()` - **Scripts**: `provider_capif_connector_update.py` and `provider_capif_connector_offboarding.py` -`update_provider()`: The provider updates his features such as `APFs`, `AEFs`, etc... +`update_provider(supp_features)`: The provider updates his features such as `APFs`, `AEFs`, etc... `offboard_provider()`: The provider offboards from CAPIF, this will cause the erase of the published APIs that were currently exposed. +`supp_features` parameter is optional and it stands for communicating to the CCF the supported features.It's default value its 0. + The provider must be onboarded before using these features.  @@ -278,11 +282,14 @@ The invoker_capif_details variable stores the `capif_api_security_context_detail ### Invoker onboarding OpenCAPIF SDK references: -- **Function**: `onboard_invoker()` +- **Function**: `onboard_invoker(supp_features)` - **Script**: `invoker_capif_connector.py` The SDK streamlines the invoker onboarding process, storing the `api_invoker_id` in the `capif_api_security_context_details.json`. +`supp_features` parameter is optional and it stands for communicating to the CCF the supported features.It's default value its 0. + +  ### Service Discovery @@ -327,11 +334,14 @@ It is mandatory to have obtained the [JWT token](#obtain-jwt-tokens) previously. ### Update and Offboard Invoker OpenCAPIF SDK references: -- **Functions**: `update_invoker()` and `offboard_invoker()` +- **Functions**: `update_invoker(supp_features)` and `offboard_invoker()` - **Scripts**: `invoker_capif_connector_update.py` and `invoker_capif_connector_offboarding.py` Onboarding is required before utilizing these functions. +`supp_features` parameter is optional and it stands for communicating to the CCF the supported features.It's default value its 0. + +  ## Other Features diff --git a/opencapif_sdk/capif_event_feature.py b/opencapif_sdk/capif_event_feature.py index ba28632..e8feceb 100644 --- a/opencapif_sdk/capif_event_feature.py +++ b/opencapif_sdk/capif_event_feature.py @@ -30,7 +30,7 @@ logging.basicConfig( class capif_invoker_event_feature(capif_invoker_connector): - def create_subscription(self, name, supp_features=0): + def create_subscription(self, name, supp_features="0"): invoker_capif_details = self.invoker_capif_details @@ -150,7 +150,7 @@ class capif_invoker_event_feature(capif_invoker_connector): self.logger.error("Subscription file not found at path: %s", path) return None, {"error": "Subscription file not found"} - def update_subcription(self, name, supp_features=0): + def update_subcription(self, name, supp_features="0"): invoker_capif_details = self.invoker_capif_details subscriberId = invoker_capif_details["api_invoker_id"] @@ -209,7 +209,7 @@ class capif_invoker_event_feature(capif_invoker_connector): class capif_provider_event_feature(capif_provider_connector): - def create_subscription(self, name, id, supp_features=0): + def create_subscription(self, name, id, supp_features="0"): subscriberId = id @@ -345,7 +345,7 @@ class capif_provider_event_feature(capif_provider_connector): self.logger.error("Subscription file not found at path: %s", path) return None, {"error": "Subscription file not found"} - def update_subcription(self, name, id, supp_features=0): + def update_subcription(self, name, id, supp_features="0"): subscriberId = id @@ -420,5 +420,5 @@ class capif_provider_event_feature(capif_provider_connector): self.logger.error("Subscription file not found at path: %s", path) return None, {"error": "Subscription file not found"} - def patch_subcription(self, name, id, supp_features=0): + def patch_subcription(self, name, id, supp_features="0"): self.update_subcription(self, name, id, supp_features) diff --git a/opencapif_sdk/capif_invoker_connector.py b/opencapif_sdk/capif_invoker_connector.py index 7a3753c..e973a69 100644 --- a/opencapif_sdk/capif_invoker_connector.py +++ b/opencapif_sdk/capif_invoker_connector.py @@ -175,7 +175,7 @@ class capif_invoker_connector: url = url + "/" return url - def onboard_invoker(self) -> None: + def onboard_invoker(self, supp_features="0") -> None: self.logger.info("Registering and onboarding Invoker") try: public_key = self.__create_private_and_public_keys() @@ -184,7 +184,7 @@ class capif_invoker_connector: capif_discover_url = capif_postauth_info["ccf_discover_url"] capif_access_token = capif_postauth_info["access_token"] api_invoker_id = self.__onboard_invoker_and_create_certificate( - public_key, capif_onboarding_url, capif_access_token + public_key, capif_onboarding_url, capif_access_token, supp_features ) self.__write_to_file(api_invoker_id, capif_discover_url) self.logger.info("Invoker registered and onboarded successfully") @@ -322,7 +322,7 @@ class capif_invoker_connector: raise def __onboard_invoker_and_create_certificate( - self, public_key, capif_onboarding_url, capif_access_token + self, public_key, capif_onboarding_url, capif_access_token, supp_features ): self.logger.info( "Onboarding Invoker to CAPIF and creating signed certificate by giving our public key to CAPIF") @@ -330,7 +330,7 @@ class capif_invoker_connector: url = self.capif_https_url + capif_onboarding_url payload_dict = { "notificationDestination": self.capif_callback_url, - "supportedFeatures": f"{self.supported_features}", + "supportedFeatures": supp_features, "apiInvokerInformation": self.csr_common_name, "websockNotifConfig": { "requestWebsocketUri": True, @@ -396,7 +396,7 @@ class capif_invoker_connector: self.logger.error(f"Error during writing to file: {e}") raise - def update_invoker(self): + def update_invoker(self,supp_features="0"): self.logger.info("Updating Invoker") try: @@ -408,7 +408,7 @@ class capif_invoker_connector: public_key = file.read() self.__update_invoker_to_capif_and_create_the_signed_certificate( - public_key, capif_onboarding_url, capif_access_token + public_key, capif_onboarding_url, capif_access_token, supp_features ) self.logger.info("Invoker updated successfully") @@ -417,7 +417,7 @@ class capif_invoker_connector: raise def __update_invoker_to_capif_and_create_the_signed_certificate( - self, public_key, capif_onboarding_url, capif_access_token + self, public_key, capif_onboarding_url, capif_access_token, supp_features ): self.logger.info( "Updating Invoker to CAPIF and creating signed certificate by giving our public key to CAPIF") @@ -433,7 +433,7 @@ class capif_invoker_connector: url = self.capif_https_url + capif_onboarding_url + "/" + invokerid payload_dict = { "notificationDestination": self.capif_callback_url, - "supportedFeatures": f"{self.supported_features}", + "supportedFeatures": supp_features, "apiInvokerInformation": self.csr_common_name, "websockNotifConfig": { "requestWebsocketUri": True, diff --git a/opencapif_sdk/capif_logging_feature.py b/opencapif_sdk/capif_logging_feature.py index cbd25de..ad436e6 100644 --- a/opencapif_sdk/capif_logging_feature.py +++ b/opencapif_sdk/capif_logging_feature.py @@ -195,7 +195,7 @@ class capif_logging_feature: if not self.api_id: raise ValueError(f"No ID was found for the API '{name}'.") - def create_logs(self, aefId, jwt, supp_features=0): + def create_logs(self, aefId, jwt, supp_features="0"): api_invoker_id = self._decrypt_jwt(jwt) diff --git a/opencapif_sdk/capif_provider_connector.py b/opencapif_sdk/capif_provider_connector.py index ce5cc83..a493ed3 100644 --- a/opencapif_sdk/capif_provider_connector.py +++ b/opencapif_sdk/capif_provider_connector.py @@ -251,7 +251,7 @@ class capif_provider_connector: return public_key - def __onboard_exposer_to_capif(self, access_token, capif_onboarding_url): + def __onboard_exposer_to_capif(self, access_token, capif_onboarding_url, supp_features): self.logger.info( "Onboarding Provider to CAPIF and waiting signed certificate by giving our public keys to CAPIF") @@ -277,7 +277,7 @@ class capif_provider_connector: for role in roles ], "apiProvDomInfo": "This is provider", - "suppFeat": self.supported_features, + "suppFeat": supp_features, "failReason": "string", "regSec": access_token, } @@ -380,7 +380,7 @@ class capif_provider_connector: f"Error acquiring authorization: {e} - Response: {response.text}") raise - def onboard_provider(self) -> None: + def onboard_provider(self, supp_features="0") -> None: """ Retrieves and stores the certificate from CAPIF, acquires authorization, and registers the provider. """ @@ -397,7 +397,7 @@ class capif_provider_connector: # Onboard provider to CAPIF onboarding_response = self.__onboard_exposer_to_capif( - access_token, capif_onboarding_url + access_token, capif_onboarding_url, supp_features ) # Save onboarding details to file @@ -1137,7 +1137,7 @@ class capif_provider_connector: f"Unexpected error while loading NEF API details: {e}") raise - def update_provider(self): + def update_provider(self, supp_features="0"): self.certs_modifications() capif_postauth_info = self.__save_capif_ca_root_file_and_get_auth_token() @@ -1145,7 +1145,7 @@ class capif_provider_connector: access_token = capif_postauth_info["access_token"] ccf_publish_url = capif_postauth_info["ccf_publish_url"] onboarding_response = self.update_onboard( - capif_onboarding_url, access_token) + capif_onboarding_url, access_token, supp_features) capif_registration_id = onboarding_response["apiProvDomId"] self.__write_to_file( @@ -1171,7 +1171,7 @@ class capif_provider_connector: self.logger.info("Certificate removal process completed.") - def update_onboard(self, capif_onboarding_url, access_token): + def update_onboard(self, capif_onboarding_url, access_token, supp_features): self.logger.info( "Onboarding Provider to CAPIF and waiting signed certificate by giving our public keys to CAPIF") api_details = self._load_provider_api_details() @@ -1199,7 +1199,7 @@ class capif_provider_connector: for role in roles ], "apiProvDomInfo": "This is provider", - "suppFeat": self.supported_features, + "suppFeat": supp_features, "failReason": "string", "regSec": access_token, } diff --git a/opencapif_sdk/service_discoverer.py b/opencapif_sdk/service_discoverer.py index 1865793..a000142 100644 --- a/opencapif_sdk/service_discoverer.py +++ b/opencapif_sdk/service_discoverer.py @@ -445,7 +445,7 @@ class service_discoverer: self.invoker_capif_details["access_token"] = token self.__cache_security_context() - def get_tokens(self, supp_features=0): + def get_tokens(self, supp_features="0"): self.get_security_context(supp_features) token = self.get_access_token() diff --git a/test/test1.json b/test/test1.json index c0231a1..1d4d717 100644 --- a/test/test1.json +++ b/test/test1.json @@ -2,7 +2,7 @@ "apiName": "test1", "aefProfiles": [ { - "aefId": "AEFa6953ae0f359180257403c58e55a72", + "aefId": "AEF74c34d6307c5c6bd0f82a5ee5d272c", "versions": [ { "apiVersion": "v1", -- GitLab From 08ab48f9f12df1f6440ef3ee13b91f47f095628f Mon Sep 17 00:00:00 2001 From: JorgeEcheva26 <jorge.echevarriauribarri.practicas@telefonica.es> Date: Wed, 30 Apr 2025 10:32:46 +0200 Subject: [PATCH 5/5] small readme changes --- README_pipy.md | 246 ++++++++++++------------------------------ doc/sdk_developers.md | 2 +- 2 files changed, 71 insertions(+), 177 deletions(-) diff --git a/README_pipy.md b/README_pipy.md index 19aaa41..4cf2a97 100644 --- a/README_pipy.md +++ b/README_pipy.md @@ -1,217 +1,111 @@ # OpenCAPIF SDK -This repository develops a Python Software Development Kit(SDK) which focuses on connecting to OpenCAPIF (Common API Framework for 3GPP Northbound APIs) in a simple way, lowering integration complexity and allowing developers to focus on Network Applications (Network Apps) or services development. +This repository develops a Python Software Development Kit (SDK) which facilitates integration with OpenCAPIF (Common API Framework for 3GPP Northbound APIs), significantly reducing integration complexity and enabling developers to focus on building Network Applications (Network Apps). -OpenCAPIF SDK provides a set of libraries to enable either CAPIF provider and invoker roles, and other functions to simplify procedures calls towards OpenCAPIF entity. +The SDK supports both CAPIF roles: **Invoker** and **Provider**, and provides simplified methods for interacting with OpenCAPIF. It is compatible with the following public releases: -Current version of OpenCAPIF SDK is compatible with following publicly available releases: - [OpenCAPIF Release 1.0](https://ocf.etsi.org/documentation/v1.0.0-release/) -- OpenCAPIF Release 2.0 +- [OpenCAPIF Release 2.0](https://ocf.etsi.org/documentation/v2.0.0-release/) -# Network App developers +## Network App Developers -In the scope of CAPIF, a Network App (Network Application) refers to an external application or service that interacts with the 3GPP network via standardized APIs. These Network Apps typically leverage the capabilities and services provided by the underlying mobile network infrastructure, such as network slicing, quality of service (QoS), or location services. +A Network App refers to an external application or service that interacts with the 3GPP network using standardized APIs. These apps can be created by network operators, third-party providers, or other entities to access advanced network services like QoS, network slicing, or location services via CAPIF. -Network Apps can be developed by third-party service providers, network operators, or other stakeholders to offer a wide range of services, including enhanced communication features, IoT solutions, or content delivery, and they use CAPIF as the unified framework for securely discovering, accessing, and utilizing 3GPP network APIs. +Network Apps take on one of two roles: -For that purpose Network Apps play 2 different roles when interacting with CAPIF: -- **Invoker**: a Network App acting as an Invoker is responsible for consuming APIs exposed by other services. This role represents an external application or service that calls the 3GPP northbound APIs to utilize the network’s functionalities. +- **Invoker**: Consumes APIs exposed by providers to access network capabilities. +- **Provider**: Exposes APIs/services to be consumed by invokers. A Provider includes: + - **AMF** (API Management Function): Manages onboarding/offboarding, monitoring, and auditing. + - **APF** (API Publishing Function): Publishes APIs to the CCF. + - **AEF** (API Exposing Function): Exposes APIs, validates authorizations, and logs usage. -- **Provider**: a Network App acting as a Provider is responsible for exposing its own APIs/services for use by Invokers. This role represents an entity that offers services through APIs, making them available to other external applications or Invokers.A provider also is distinguished for having three parts. +The SDK maps Python functions to CAPIF OpenAPI endpoints as defined in [3GPP TS 29.222 v18.5.0](https://www.etsi.org/deliver/etsi_ts/129200_129299/129222/18.05.00_60/ts_129222v180500p.pdf). A detailed mapping of supported endpoints is available in the full documentation. - - The **AMF (API Management Function)**, supplies the API provider domain with administrative capabilities. Some of these capabilities include, auditing the service API invocation logs received from the CCF, on-boarding/off-boarding new API invokers and monitoring the status of the service APIs.One provider can have only one AMF. +## Requirements - - The **APF (API Publishing Function)**, is responsible for the publication of the service APIs to CCF in order to enable the discovery capability to the API Invokers.One provider can have multiple APFs. +To use the OpenCAPIF SDK, a registered account on the CAPIF instance is needed. Contact the system administrator to obtain a CAPIF username and password. - - The **AEF (API Exposing Function)**, is responsible for the exposure of the service APIs. Assuming that API Invokers are authorized by the CCF, AEF validates the authorization and subsequently provides the direct communication entry points to the service APIs. AEF may also authorize API invokers and record the invocations in log files.One provider can have multiple AEFs +## Installation -OpenCAPIF SDK brings a set of functions to integrate with the 5G Core's function CAPIF, as defined in [3GPP Technical Specification (TS) 29.222 V18.5.0 Common API Framework for 3GPP Northbound APIs](https://www.etsi.org/deliver/etsi_ts/129200_129299/129222/18.05.00_60/ts_129222v180500p.pdf). This section shows the mapping between the Python functions available in this SDK and the CAPIF OpenAPI APIs defined the reference standard: +Install the SDK using pip: -| **3GPP CAPIF API** | **OpenCAPIF SDK function** | **Description** | -|-------------------------------------------------------|-------------------------------------------------------------|-------------------------------------------------------------| -| /onboardedInvokers (POST) | [onboard_invoker()](./doc/sdk_full_documentation.md#invoker-onboarding) | Registers a new invoker. | -| /onboardedInvokers/{onboardingId} (PUT) | [update_invoker()](./doc/sdk_full_documentation.md#update-and-offboard-invoker) | Updates an existing invoker for a specific `onboardingId`. | -| /onboardedInvokers/{onboardingId} (DELETE) | [offboard_invoker()](./doc/sdk_full_documentation.md#update-and-offboard-invoker) | Deletes an invoker for a specific `onboardingId`. | -| registrations (POST) | [onboard_provider()](./doc/sdk_full_documentation.md#provider-onboarding) | Registers a new service provider. | -| /registrations/{registrationId} (PUT) | [update_provider()](./doc/sdk_full_documentation.md#update-and-offboard-provider) | Updates a service provider's registration for a specific `registrationId`. | -| /registrations/{registrationId} (DELETE) | [offboard_provider()](./doc/sdk_full_documentation.md#update-and-offboard-provider) | Deletes a service provider's registration for a specific `registrationId`. | -| /allServiceAPIs (GET) | [discover()](./doc/sdk_full_documentation.md#discover-process) | Retrieves a list of all available service APIs. | -| /trustedInvokers (PUT//POST) | [get_tokens()](./doc/sdk_full_documentation.md#discover-process) | Registers or updates trusted invokers. | -| /securities/{securityId}/token (GET) | [get_tokens()](./doc/sdk_full_documentation.md#obtain-invoker-tokens) | Retrieves a security token for a specific `securityId`. This JWT token is used to query the targeted services. | -| /{apfId}/service-apis(POST) | [publish_services()](./doc/sdk_full_documentation.md#services-publishing) | Registers a new service API into the system for a specific `apfId` | -| /{apfId}/service-apis/{serviceApiId} (DELETE) | [unpublish_service()](./doc/sdk_full_documentation.md#services-deletion) | Deletes a service API from the system for a specific `apfId`and `serviceApiId` | -| /{apfId}/service-apis/{serviceApiId} (PUT) | [update_service()](./doc/sdk_full_documentation.md#services-update) | Updates the details of an existing service API for a specific `apfId`and `serviceApiId` | -| /{apfId}/service-apis/{serviceApiId} (GET) | [get_service()](./doc/sdk_full_documentation.md#get-services) | Retrieves the details of a specific service API for a specific `apfId` and `serviceApiId` | -| /{apfId}/service-apis (GET) | [get_all_services()](./doc/sdk_full_documentation.md#get-all-services) | Retrieves a list of all available service APIs for a specific `apfId` | -| /aef-security/v1/check-authentication (POST) | [check_authentication()](./doc/sdk_full_documentation.md#check_authentication) | This custom operation allows the API invoker to confirm the `supported_features` from the API exposing function(AEF) | -| /api-invocation-logs/v1/{aefId}/logs (POST) | [create_logs( aefId, api_invoker_id)](./doc/sdk_full_documentation.md#create_logs) | This operation allows to the Provider to notice to the CCF about the query of an invoker for an especific `aefId` -| /capif-events/v1/{subscriberId}/subscriptions (POST) | [create_subscription(name, id)](./doc/sdk_full_documentation.md#create_subscription) | This operation allows to the Invoker/AEF/APF/AMF to ask to the CCF about notifications related to certain functionalities. -| /capif-events/v1/{subscriberId}/subscriptions/{subscriptionId} (DELETE) | [delete_subscription(name, id)](./doc/sdk_full_documentation.md#delete_subscription) | This operation allows to the Invoker/AEF/APF/AMF to withdraw the petition to receive notifications related to certain functionalities. -| /capif-events/v1/{subscriberId}/subscriptions/{subscriptionId} (PUT) | [update_subscription(name, id)](./doc/sdk_full_documentation.md#update_subscription) | This operation allows to the Invoker/AEF/APF/AMF to modify to the petition to receive notifications related to certain functionalities. **ONLY AVAILABLE IN OPENCAPIF RELEASE 2** -| /capif-events/v1/{subscriberId}/subscriptions/{subscriptionId} (PATCH) | [patch_subscription(name, id)](./doc/sdk_full_documentation.md#patch_subscription) | This operation allows to the Invoker/AEF/APF/AMF to modify to the petition to receive notifications related to certain functionalities. **ONLY AVAILABLE IN OPENCAPIF RELEASE 2** - -NOTE: Above mentioned CAPIF APIs are defined in these 3GPP references: -- [CAPIF Invoker API specification](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_API_Invoker_Management_API.yaml) -- [CAPIF Provider API specification](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_API_Provider_Management_API.yaml) -- [CAPIF Discover API specification](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Discover_Service_API.yaml) -- [CAPIF Publish API specification](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) -- [CAPIF Security API specification](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Security_API.yaml) -- [AEF Security API specification](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_AEF_Security_API.yaml) -- [CAPIF Logging API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Logging_API_Invocation_API.yaml) -- [CAPIF Events API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Events_API.yaml) -NOTE: In the [3GPP Technical Specification (TS) 29.222 V18.5.0 Common API Framework for 3GPP Northbound APIs](https://www.etsi.org/deliver/etsi_ts/129200_129299/129222/18.05.00_60/ts_129222v180500p.pdf) the `service` concept is understood as equal as the `API` concept. - -## OpenCAPIF SDK requirements - -To use the OpenCAPIF SDK, a registered user account within the target CAPIF instance is required. - -**Contact the administrator to obtain the required predefined credentials (CAPIF username and password).** - -## OpenCAPIF SDK installation - -To use the SDK, binary installer for the latest version is available at the [Python Package Index (Pipy)](https://pypi.org/project/opencapif-sdk/) - -```console +```bash pip install opencapif_sdk ``` -## Configuration via `capif_sdk_config.json` -### Common Fields for Invoker and Provider +## Configuration Overview -Regardless of the role (Invoker or Provider), the following fields are mandatory: +Configuration is done through a JSON file (`capif_sdk_config.json`) with common and role-specific fields. -- `capif_host` -- `register_host` -- `capif_https_port` -- `capif_register_port` -- `capif_username` -- `capif_password` +### Common Fields (Invoker & Provider) +- `capif_host`, `register_host` +- `capif_https_port`, `capif_register_port` +- `capif_username`, `capif_password` - `debug_mode` -### Network App Invoker +### Invoker-specific Fields +- `invoker_folder`, `capif_callback_url` +- `supported_features`, `cert_generation` +- Optional: `discover_filter`, `check_authentication_data` -When configuring the SDK as a **Network App Invoker**, the following fields must be provided: +### Provider-specific Fields +- `provider_folder`, `supported_features`, `cert_generation` +- `APFs`, `AEFs`, `publish_req`, `api_description_path` -- `invoker_folder` -- `capif_callback_url` -- `supported_features` -- `cert_generation` (fields such as `csr_common_name`, `csr_country_name`, etc.) +## Using `discover_filter` -**Optional:** -- `discover_filter`: useful to enable the discovery of specific APIs. Some fields under `discover_filter` structure required to be configured when using discovery filters. Check devoted section below, -- `check_authentication_data`: useful to use `check_authentication()` function to validate features from a target provider, it will be required to fill up the `ip` and `port` parameters within the `check_authentication_data` variable. +The `discover_filter` must comply with the [Discover Service API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Discover_Service_API.yaml). It allows filtering by specific fields (e.g., `api-name`) during discovery. -### Network App Provider +## Using `publish_req` -For SDK configuration as a **Network App Provider**, the following fields are required: +This section enables API publishing using the [CAPIF Publish Service API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml). Required fields include: -- `provider_folder` -- `suported_features` -- `cert_generation` (fields such as `csr_common_name`, `csr_country_name`, etc.) -- `APFs` -- `AEFs` -- `publish_req` +- `service_api_id` +- `publisher_apf_id` +- `publisher_aefs_ids` - `api_description_path` -## Configuration of `discover_filter` - -The `discover_filter` section adheres to the parameters defined in the GET request schema of the [Discover Services API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Discover_Service_API.yaml). - -To use the service discovery functionality, the `discover_filter` fields should be populated with the desired filters. **It is important to note that fields such as `api-name` must contain only one entry of each type (i.e., no lists are allowed in api-name).** - -For instance if the invoker fill the `api-name` field, the `discover()` functionality will retrieve only one API, the one that matches the exact name of the `api-name`. - -Before running the Invoker Service Discovery Functionality, the Invoker must be onboarded to CAPIF. - -## Configuration of `publish_req` - -This section is mandatory when using the [CAPIF Publish Service API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml). The following fields are required: - -- `service_api_id`: Example: `"02eff6e1b3a8f7c8044a92ee8a30bd"` -- `publisher_apf_id`: Example: `"APFa165364a379035d14311deadc04332"` -- `publisher_aefs_ids`: An array of selected AEF IDs. Example: `["AEFfa38f0e855bffb420e4994ecbc8fb9", "AEFe8bfa711f4f0c95ba0b382508e6382"]` - -The `api_description_path` must point to the Publish API to be shared, and it should follow the [ServiceAPIDescription](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) schema. +A helper is available to translate OpenAPI to ServiceAPIDescription format: -To obtain this schema, opencapif_sdk has a facility to translate Openapi structures to ServiceAPIDescription schemas. - -If the `publisher_aefs_ids` do not match the `aefProfiles` in the API description, an error will be raised by the SDK. - -## Descriptions of `capif_sdk_config` Fields - -- `invoker_folder`: The path (relative or absolute) where invoker information (certificates, keys, etc.) is stored. -- `provider_folder`: The path (relative or absolute) where provider information is stored. -- `supported_features`: A string used to indicate the features supported by an API. The string shall contain a bitmask indicating supported features in hexadecimal representation Each character in the string shall take a value of "0" to "9", "a" to "f" or "A" to "F". [More information](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29571_CommonData.yaml) -- `capif_host`: The domain name of the CAPIF host. -- `register_host`: The domain name of the register host. -- `capif_https_port`: The CAPIF host port number. -- `capif_register_port`: The register host port number. -- `capif_callback_url`: The URL used by CAPIF to send invoker notifications ([currently unavailable](sdk-issues.md)). -- `cert_generation`: Fields for certificate generation, with `csr_country_name` requiring a two-letter country code. -- `capif_username`: The CAPIF username. -- `capif_password`: The CAPIF password. -- `apfs`: The number of APFs to be onboarded as a provider (e.g., `5`). -- `aefs`: The number of AEFs to be onboarded as a provider (e.g., `2`). -- `debug_mode`: A boolean value to enable or disable SDK logs (e.g., `True` or `False`). -- [`discover_filter`](#configuration-of-discover_filter): Fields for configuring invoker service discovery. -- [`publish_req`](#configuration-of-publish_req): Fields required for API publishing. -- `api_description_path`: The path to the [ServiceAPIDescription](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) JSON file. -- `check_authentication_data`: The `ip` and `port` of the target Provider's AEF to get their supported features from. - - - -## Important information for Provider consumers - -Within the `provider_folder`, the SDK stores the created folders named with prefix of the provided `capif_username` that has been registered from administrator. At each folder, there will be found the following files: - -- `provider_capif_ids.json`: contains all the APFs and AEFs ids that have already onboarded with this `capif_username`, -- `capif_<api_name>_<api_id>.json`: if it is already published or updated an API, it will contain a copy of the last payload, -- `service_received.json`: if it is already used to get an API or get all APIs functionality, it will contain the response of last request, -- `provider_service_ids.json`: contains the currently published APIs with their `api_id`. - -All the configuration values are available within the object `capif_provider_connector`. - -The `provider_service_ids` variable stores the `provider_service_ids.json` content in a dictionary form. +```python +from opencapif_sdk import api_schema_translator +translator = api_schema_translator("./path/to/openapi.yaml") +translator.build("https://192.168.1.10:8080/exampleAPI/v1", "0", "0") +``` -The `provider_capif_ids` variable stores the `provider_capif_ids.json` content in a dictionary form. +## Configuration Field Descriptions -## Important information for Invoker consumer +Key parameters include: +- `capif_username`, `capif_password`: Credentials +- `supported_features`: Hex bitmask (e.g., "4") +- `debug_mode`: Enables debug logging +- `api_description_path`: Path to the ServiceAPIDescription JSON -In the `invoker_folder`, it will be located several folders with each `capif_username` it has been onboarded as a provider. For each folder, it will be found: +## Provider Artifacts -- `capif_api_security_context_details.json`: This file contains the information of the invoker. It will contain: - - 1. The `api_invoker_id`, - 2. If the Service Discovery Functionality has already been used , it will be found all the available APIs with their information, - 3. If the Service Get Token functionality has already been used , it will be found the access token for using the APIs that has already been discovered. +In `provider_folder/<capif_username>/`, the SDK stores: +- `provider_capif_ids.json` +- `capif_<api_name>_<api_id>.json` +- `service_received.json` +- `provider_service_ids.json` -The `token` variable is also available for retrieving the JWT token after the `get_tokens()` method. +## Invoker Artifacts -The `invoker_capif_details` variable stores the `capif_api_security_context_details.json` content in a dictionary form. +In `invoker_folder/<capif_username>/`, the SDK stores: +- `capif_api_security_context_details.json`, which includes: + - `api_invoker_id` + - Available API metadata (after discovery) + - JWT token (after `get_tokens()`) -## Openapi translation +## Known Issues -The `api_description_path` must point to the Publish API to be shared, and it should follow the [ServiceAPIDescription](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Publish_Service_API.yaml) schema. +The following APIs are not yet supported: +- [CAPIF Access Control Policy](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Access_Control_Policy_API.yaml) +- [CAPIF Auditing API](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Auditing_API.yaml) +- [CAPIF Routing Info](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Routing_Info_API.yaml) +- Security API operations: + - `/trustedInvokers/{apiInvokerId}` (GET, DELETE) + - `/trustedInvokers/{apiInvokerId}/delete` (POST) -This schema could be obtained by applying this code. -```python - import opencapif_sdk - - translator = api_schema_translator("./path/to/openapi.yaml") - translator.build("https://192.168.1.10:8080/exampleAPI/v1", "0", "0") +Some publishing operations fail if the number of AEFs/APFs is increased after initial onboarding. -``` -This code will read `openapi.yaml`, ensure the structure of it and translate the content into ServiceAPIDescription schema, then will create a .json named `api_description_name`. Also it is necessary to fill the ip and port fields to create correctly the schema. -# OpenCAPIF SDK known issues - -There are some features which **are not currently available at latest OpenCAPIF SDK release**. Those are assumed to be technical debt and might be available in future releases: - - - [CAPIF Access control policy management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Access_Control_Policy_API.yaml) - - [CAPIF Auditing API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Auditing_API.yaml) - - [CAPIF Events API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Events_API.yaml) - - [CAPIF Logging API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Logging_API_Invocation_API.yaml) - - [CAPIF Routing info API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Routing_Info_API.yaml) - - [CAPIF Security API management](https://github.com/jdegre/5GC_APIs/blob/Rel-18/TS29222_CAPIF_Security_API.yaml) - - /trustedInvokers/{apiInvokerId}/delete (POST) - - /trustedInvokers/{apiInvokerId} (GET) - - /trustedInvokers/{apiInvokerId} (DELETE) diff --git a/doc/sdk_developers.md b/doc/sdk_developers.md index 1d5d977..6866cd2 100644 --- a/doc/sdk_developers.md +++ b/doc/sdk_developers.md @@ -56,7 +56,7 @@ This article explains how to solve it [here](https://laict.medium.com/install-py 2. Clone GitHub repository: ```console -git clone https://github.com/Telefonica/pesp_capif_sdk.git +git clone https://labs.etsi.org/rep/ocf/sdk.git ``` ```console -- GitLab