Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ocf/sdk
1 result
Show changes
Commits on Source (4)
Showing with 353 additions and 61 deletions
......@@ -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"]
......
......@@ -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
......
......@@ -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
![GENERAL CAPIF USAGE FLOW](./images/flows-updated_opencapif.jpg)
......@@ -166,10 +165,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 +183,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 +216,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.
![Events_feature](./images/flows-event_subscription.jpg)
......@@ -238,6 +243,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 +302,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.
![Invoker_get_token](./images/flows-invoker_get_tokens.jpg)
......@@ -337,7 +345,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.
......
......@@ -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": [
......
......@@ -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)
......@@ -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
......
......@@ -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
......@@ -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)
......
{
"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
......@@ -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
......@@ -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=(
......
{
"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"
}
}
}
......@@ -85,7 +85,7 @@
}
},
"log": {
"apiName": "API of dummy Network-App to test",
"apiName": "test1",
"apiVersion": "v1",
"resourceName": "MONITORING_SUBSCRIPTIONS",
"uri": "/{scsAsId}/subscriptions",
......
......@@ -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):
......
{
"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"
}
]
}
......
......@@ -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"
......