From 781473381bdb89e4f4106af53601100c7387a16d Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Wed, 22 May 2024 15:21:26 +0200 Subject: [PATCH 1/9] New registration flow included in all test plan --- doc/testing/testplan/README.md | 1 + .../api_access_control_policy/README.md | 2 +- .../testplan/api_auditing_service/README.md | 2 +- .../testplan/api_discover_service/README.md | 5 +- .../testplan/api_events_service/README.md | 3 +- .../testplan/api_invoker_management/README.md | 19 ++- .../invoker_register_body.json | 7 - .../testplan/api_logging_service/README.md | 2 +- .../api_provider_management/README.md | 148 +++++++++--------- .../provider_register_body.json | 7 - .../testplan/api_publish_service/README.md | 4 +- .../testplan/api_security_service/README.md | 2 +- .../testplan/common_operations/README.md | 81 ++++++---- .../user_getauth_response_body_example.json | 10 ++ .../user_registration_body.json | 6 + 15 files changed, 160 insertions(+), 139 deletions(-) delete mode 100644 doc/testing/testplan/api_invoker_management/invoker_register_body.json delete mode 100644 doc/testing/testplan/api_provider_management/provider_register_body.json create mode 100644 doc/testing/testplan/common_operations/user_getauth_response_body_example.json create mode 100644 doc/testing/testplan/common_operations/user_registration_body.json diff --git a/doc/testing/testplan/README.md b/doc/testing/testplan/README.md index 2031569..340bce2 100644 --- a/doc/testing/testplan/README.md +++ b/doc/testing/testplan/README.md @@ -1,6 +1,7 @@ # Test Plan Index List of Common API Services implemented: +* [Common Operations](./common_operations/README.md) * [Api Invoker Management](./api_invoker_management/README.md) * [Api Provider Management](./api_provider_management/README.md) * [Api Publish Service](./api_publish_service/README.md) diff --git a/doc/testing/testplan/api_access_control_policy/README.md b/doc/testing/testplan/api_access_control_policy/README.md index dae28c5..6233bad 100644 --- a/doc/testing/testplan/api_access_control_policy/README.md +++ b/doc/testing/testplan/api_access_control_policy/README.md @@ -825,5 +825,5 @@ At this documentation you will have all information and related files and exampl [security notification body]: ./security_notification.json "Security Notification Request" [access token req body]: ./access_token_req.json "Access Token Request" [example]: ./access_token_req.json "Access Token Request Example" -[invoker onboarding]: ../common_operations/README.md#register-an-invoker "Invoker Onboarding" +[invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" [provider registration]: ../common_operations/README.md#register-a-provider "Provider Registration" diff --git a/doc/testing/testplan/api_auditing_service/README.md b/doc/testing/testplan/api_auditing_service/README.md index add1a9d..8b856af 100644 --- a/doc/testing/testplan/api_auditing_service/README.md +++ b/doc/testing/testplan/api_auditing_service/README.md @@ -242,6 +242,6 @@ At this documentation you will have all information and related files and exampl [log entry request body]: ../api_logging_service/invocation_log.json "Log Request Body" -[invoker onboarding]: ../common_operations/README.md#register-an-invoker "Invoker Onboarding" +[invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" [provider onboarding]: ../common_operations/README.md#register-a-provider "Provider Onboarding" diff --git a/doc/testing/testplan/api_discover_service/README.md b/doc/testing/testplan/api_discover_service/README.md index 4c5c8f2..aaef9ab 100644 --- a/doc/testing/testplan/api_discover_service/README.md +++ b/doc/testing/testplan/api_discover_service/README.md @@ -348,9 +348,8 @@ At this documentation you will have all information and related files and exampl [service api description]: ../api_publish_service/service_api_description_post_example.json "Service API **Description** Request" [publisher register body]: ../api_publish_service/publisher_register_body.json "Publish register Body" [invoker onboarding body]: ../api_invoker_management/invoker_details_post_example.json "API Invoker Request" - [invoker register body]: ../api_invoker_management/invoker_register_body.json "Invoker Register Body" [provider request body]: ../api_provider_management/provider_details_post_example.json "API Provider Enrolment Request" [provider request patch body]: ../api_provider_management/provider_details_enrolment_details_patch_example.json "API Provider Enrolment Patch Request" - [provider getauth body]: ../api_provider_management/provider_getauth_example.json "Get Auth Example" - [invoker onboarding]: ../common_operations/README.md#register-an-invoker "Invoker Onboarding" + + [invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" [provider registration]: ../common_operations/README.md#register-a-provider "Provider Registration" diff --git a/doc/testing/testplan/api_events_service/README.md b/doc/testing/testplan/api_events_service/README.md index ca259ac..95d55a8 100644 --- a/doc/testing/testplan/api_events_service/README.md +++ b/doc/testing/testplan/api_events_service/README.md @@ -258,10 +258,9 @@ At this documentation you will have all information and related files and exampl -[invoker register body]: ../api_invoker_management/invoker_register_body.json "Invoker Register Body" [invoker onboard request body]: ../api_invoker_management/invoker_details_post_example.json "API Invoker Request" [event subscription request body]: ./event_subscription.json "Event Subscription Request" -[invoker onboarding]: ../common_operations/README.md#register-an-invoker "Invoker Onboarding" +[invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" [Return To All Test Plans]: ../README.md diff --git a/doc/testing/testplan/api_invoker_management/README.md b/doc/testing/testplan/api_invoker_management/README.md index 3b4e130..02f04a9 100644 --- a/doc/testing/testplan/api_invoker_management/README.md +++ b/doc/testing/testplan/api_invoker_management/README.md @@ -13,20 +13,19 @@ At this documentation you will have all information and related files and exampl * NetApp was not registered previously * NetApp was not onboarded previously + * ***Preconditions: The administrator must have previously registered the User.*** **Information of Test**: 1. Create public and private key at invoker - 2. Register of Invoker at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [invoker register body] + 2. Retrieve access_token by User: - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [invoker getauth body] + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] - 4. Onboard Invoker: + 3. Onboard Invoker: * Send POST to *https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers* * Reference Request Body: [invoker onboarding body] * "onboardingInformation"->"apiInvokerPublicKey": must contain public key generated by Invoker. @@ -34,7 +33,7 @@ At this documentation you will have all information and related files and exampl **Execution Steps**: - 1. Register Invoker at CCF + 1. Retrieve access_token by User from register 2. Onboard Invoker at CCF 3. Store signed Certificate @@ -305,8 +304,8 @@ At this documentation you will have all information and related files and exampl [invoker onboarding body]: ./invoker_details_post_example.json "API Invoker Request" -[invoker register body]: ./invoker_register_body.json "Invoker Register Body" +[user_getauth_response_body_example]: ../common_operations/user_getauth_response_body_example.json "User GetAuth response Body Example" [put register body]: ./invoker_details_put_example.json "API Invoker Update Request" [invoker getauth body]: ./invoker_getauth_example.json "Get Auth Example" -[invoker onboarding]: ../common_operations/README.md#register-an-invoker "Invoker Onboarding" +[invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" diff --git a/doc/testing/testplan/api_invoker_management/invoker_register_body.json b/doc/testing/testplan/api_invoker_management/invoker_register_body.json deleted file mode 100644 index e5bf1fc..0000000 --- a/doc/testing/testplan/api_invoker_management/invoker_register_body.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "password": "password", - "username": "ROBOT_TESTING_INVOKER", - "role": "invoker", - "description": "Testing", - "cn": "ROBOT_TESTING_INVOKER" -} diff --git a/doc/testing/testplan/api_logging_service/README.md b/doc/testing/testplan/api_logging_service/README.md index f22795a..5d61b8e 100644 --- a/doc/testing/testplan/api_logging_service/README.md +++ b/doc/testing/testplan/api_logging_service/README.md @@ -235,7 +235,7 @@ At this documentation you will have all information and related files and exampl [log entry request body]: ./invocation_log.json "Log Request Body" -[invoker onboarding]: ../common_operations/README.md#register-an-invoker "Invoker Onboarding" +[invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" [provider onboarding]: ../common_operations/README.md#register-a-provider "Provider Onboarding" diff --git a/doc/testing/testplan/api_provider_management/README.md b/doc/testing/testplan/api_provider_management/README.md index d20f839..fe72d20 100644 --- a/doc/testing/testplan/api_provider_management/README.md +++ b/doc/testing/testplan/api_provider_management/README.md @@ -17,15 +17,14 @@ At this documentation you will have all information and related files and exampl 1. Create public and private key at provider for provider itself and each function (apf, aef and amf) - 2. Register of Provider at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [provider register body] - - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [provider getauth body] + 2. Retrieve access_token by User: + + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + + 3. Register Provider: - 4. Register Provider: * Send POST *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations* * body [provider request body] * Authentication Bearer with access_token @@ -61,22 +60,22 @@ At this documentation you will have all information and related files and exampl **Information of Test**: 1. Create public and private key at provider for provider itself and each function (apf, aef and amf) - - 2. Register of Provider at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [provider register body] - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [provider getauth body] + 2. Retrieve access_token by User: + + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + + 3. Register Provider: - 4. Register Provider: * Send POST *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations* * body [provider request body] * Authentication Bearer with access_token * Store each cert in a file with according name. - 5. Re-Register Provider: + 4. Re-Register Provider: + * Same regSec than Previous registration **Execution Steps**: @@ -90,6 +89,7 @@ At this documentation you will have all information and related files and exampl 1. Re-Register Provider: 1. **403 Forbidden** response. 2. body returned must accomplish **ProblemDetails** data structure, with: + * status 403 * title with message "Forbidden" * detail with message "Provider already registered". @@ -110,22 +110,21 @@ At this documentation you will have all information and related files and exampl **Information of Test**: 1. Create public and private key at provider for provider itself and each function (apf, aef and amf) + 2. Retrieve access_token by User: - 2. Register of Provider at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [provider register body] - - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [provider getauth body] + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + + 3. Register Provider: - 4. Register Provider: * Send POST *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations* * body [provider request body] * Authentication Bearer with access_token * Get Resource URL from Location - 5. Update Provider: + 4. Update Provider: + * Send PUT to Resource URL returned at registration *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{registrationId}* * body [provider request body] with apiProvDomInfo set to ROBOT_TESTING_MOD * Use AMF Certificate. @@ -167,21 +166,21 @@ At this documentation you will have all information and related files and exampl 1. Create public and private key at provider for provider itself and each function (apf, aef and amf) - 2. Register of Provider at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [provider register body] - - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [provider getauth body] + 2. Retrieve access_token by User: + + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + + 3. Register Provider: - 4. Register Provider: * Send POST *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations* * body [provider request body] * Authentication Bearer with access_token * Store each cert in a file with according name. - 5. Update Not Registered Provider: + 4. Update Not Registered Provider: + * Send PUT *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{API_PROVIDER_NOT_REGISTERED}* * body [provider request body] * Use AMF Certificate. @@ -189,7 +188,7 @@ At this documentation you will have all information and related files and exampl **Execution Steps**: 1. Register Provider at CCF - 3. Update Not Registered Provider + 2. Update Not Registered Provider **Expected Result**: @@ -217,21 +216,21 @@ At this documentation you will have all information and related files and exampl 1. Create public and private key at provider for provider itself and each function (apf, aef and amf) - 2. Register of Provider at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [provider register body] - - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [provider getauth body] + 2. Retrieve access_token by User: + + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + + 3. Register Provider: - 4. Register Provider: * Send POST *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations* * body [provider request body] * Authentication Bearer with access_token * Store each cert in a file with according name. - 5. Partial update provider: + 4. Partial update provider: + * Send PATCH *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{registrationId}* * body [provider request patch body] * Use AMF Certificate. @@ -265,21 +264,21 @@ At this documentation you will have all information and related files and exampl 1. Create public and private key at provider for provider itself and each function (apf, aef and amf) - 2. Register of Provider at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [provider register body] - - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [provider getauth body] + 2. Retrieve access_token by User: + + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + + 3. Register Provider: - 4. Register Provider: * Send POST *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations* * body [provider request body] * Authentication Bearer with access_token * Store each cert in a file with according name. - 5. Partial update Provider: + 4. Partial update Provider: + * Send PATCH *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{API_API_PROVIDER_NOT_REGISTERED}* * body [provider request patch body] * Use AMF Certificate. @@ -296,6 +295,7 @@ At this documentation you will have all information and related files and exampl 1. Partial update provider: 1. **404 Not Found** response. 2. body returned must accomplish **ProblemDetails** data structure, with: + * status 404 * title with message "Not Found" * detail with message "Not Exist Provider Enrolment Details". @@ -317,21 +317,21 @@ At this documentation you will have all information and related files and exampl 1. Create public and private key at provider for provider itself and each function (apf, aef and amf) - 2. Register of Provider at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [provider register body] - - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [provider getauth body] + 2. Retrieve access_token by User: + + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + + 3. Register Provider: - 4. Register Provider: * Send POST *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations* * body [provider request body] * Authentication Bearer with access_token * Store each cert in a file with according name. - 5. Delete registered provider: + 4. Delete registered provider: + * Send DELETE *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{registrationId}* * Use AMF Certificate. @@ -362,21 +362,21 @@ At this documentation you will have all information and related files and exampl 1. Create public and private key at provider for provider itself and each function (apf, aef and amf) - 2. Register of Provider at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [provider register body] - - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [provider getauth body] + 2. Retrieve access_token by User: + + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + + 3. Register Provider: - 4. Register Provider: * Send POST *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations* * body [provider request body] * Authentication Bearer with access_token * Store each cert in a file with according name. - 5. Delete registered provider at Provider Management: + 4. Delete registered provider at Provider Management: + * Send DELETE *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{API_PROVIDER_NOT_REGISTERED}* * Use AMF Certificate. @@ -395,11 +395,9 @@ At this documentation you will have all information and related files and exampl * detail with message "Not Exist Provider Enrolment Details". * cause with message "Not found registrations to send this api provider details". -[provider register body]: ./provider_details_post_example.json "API Provider Enrolment Request" - [provider request body]: ./provider_details_post_example.json "API Provider Enrolment Request" [provider request patch body]: ./provider_details_enrolment_details_patch_example.json "API Provider Enrolment Patch Request" -[provider getauth body]: ./provider_getauth_example.json "Get Auth Example" +[user_getauth_response_body_example]: ../common_operations/user_getauth_response_body_example.json "User GetAuth response Body Example" diff --git a/doc/testing/testplan/api_provider_management/provider_register_body.json b/doc/testing/testplan/api_provider_management/provider_register_body.json deleted file mode 100644 index fc26db2..0000000 --- a/doc/testing/testplan/api_provider_management/provider_register_body.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "password": "password", - "username": "ROBOT_TESTING_PUBLISHER", - "role": "provider", - "description": "Testing", - "cn": "ROBOT_TESTING_PUBLISHER" -} diff --git a/doc/testing/testplan/api_publish_service/README.md b/doc/testing/testplan/api_publish_service/README.md index 668662d..21512db 100644 --- a/doc/testing/testplan/api_publish_service/README.md +++ b/doc/testing/testplan/api_publish_service/README.md @@ -647,11 +647,9 @@ At this documentation you will have all information and related files and exampl [service api description]: ./service_api_description_post_example.json "Service API Description Request" [publisher register body]: ./publisher_register_body.json "Publish register Body" [invoker onboarding body]: ../api_invoker_management/invoker_details_post_example.json "API Invoker Request" - [invoker register body]: ../api_invoker_management/invoker_register_body.json "Invoker Register Body" [provider request body]: ../api_provider_management/provider_details_post_example.json "API Provider Enrolment Request" [provider request patch body]: ../api_provider_management/provider_details_enrolment_details_patch_example.json "API Provider Enrolment Patch Request" - [provider getauth body]: ../api_provider_management/provider_getauth_example.json "Get Auth Example" - [invoker onboarding]: ../common_operations/README.md#register-an-invoker "Invoker Onboarding" + [invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" [provider registration]: ../common_operations/README.md#register-a-provider "Provider Registration" diff --git a/doc/testing/testplan/api_security_service/README.md b/doc/testing/testplan/api_security_service/README.md index 306470a..e051f90 100644 --- a/doc/testing/testplan/api_security_service/README.md +++ b/doc/testing/testplan/api_security_service/README.md @@ -1281,7 +1281,7 @@ At this documentation you will have all information and related files and exampl [access token req body]: ./access_token_req.json "Access Token Request" [example]: ./access_token_req.json "Access Token Request Example" - [invoker onboarding]: ../common_operations/README.md#register-an-invoker "Invoker Onboarding" + [invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" [provider registration]: ../common_operations/README.md#register-a-provider "Provider Registration" diff --git a/doc/testing/testplan/common_operations/README.md b/doc/testing/testplan/common_operations/README.md index e755707..d6cda4b 100644 --- a/doc/testing/testplan/common_operations/README.md +++ b/doc/testing/testplan/common_operations/README.md @@ -1,26 +1,57 @@ # Common Operations -## Register an Invoker +## Register new user + +In order to use OpenCAPIF we must add a new user. This new user can onboard/register any Invokers or Providers. + +That new user **must be created by administrator** of Register Service and with the credentials shared by administrator, the new user can get the **access_token** by requesting it to Register service. + +The steps to register a new user at Register Service are: + +### Admin create User +1) **Login as Admin to get access_token:** + + * Send **POST** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/login** + * **Include basic Auth Header with Admin credentials** + * Get **access_token** and **refresh_token** from response + +![Flow](../../../images/flows/01_Login_Admin.png) + +2) **Create User:** + + * Send **POST** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/createUser** + * Include Admin **access_token** in **Authorization Bearer Header** + * Body [user_registration_body] + +![Flow](../../../images/flows/02_Creation_of_user.png) + +### User Retrieve access token and other information +1) **Retrieve access_token by User:** + + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include **basic Auth Header with User credentials** + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + +## Onboard an Invoker ### Steps to perform operation +***Preconditions: The administrator must have previously registered the User.*** + 1. Create public and private key at invoker - 2. Register of Invoker at CCF: - * Send POST to http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register - * Body [invoker register body] + 2. Retrieve access_token by User: + + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [invoker getauth body] + 3. Onboard Invoker: - 4. Onboard Invoker: - * Send POST to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers - * Reference Request Body: [invoker onboarding body] - * "onboardingInformation"->"apiInvokerPublicKey": must contain public key generated by Invoker. - * Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token}) + * Send **POST** to **https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers** + * Reference Request Body: [invoker onboarding body] + * "onboardingInformation"->"apiInvokerPublicKey": must contain public key generated by Invoker. + * Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token}) ### Checks to ensure onboarding - 1. Response to Register: - 1. **201 Created** 2. Response to Get Auth: 1. **200 OK** @@ -38,15 +69,14 @@ ### Steps to Perform operation 1. Create public and private key at provider for provider itself and each function (apf, aef and amf) - 2. Register of Provider at CCF: - * Send POST to *http://{CAPIF_HOSTNAME}:{CAPIF_HTTP_PORT}/register* - * body [provider register body] + 2. Retrieve access_token by User: - 3. Obtain Access Token: - * Send POST to *http://{CAPIF_HOSTNAME}/getauth* - * Body [provider getauth body] + * Send **GET** to **https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth** + * Include basic Auth Header with Admin user/password + * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] + + 3. Register Provider: - 4. Register Provider: * Send POST *https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations* * body [provider request body] * Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token}) @@ -70,18 +100,13 @@ +[user_registration_body]: ./user_registration_body.json "User Registration Body" +[user_getauth_response_body_example]: ./user_getauth_response_body_example.json "User GetAuth response Body Example" - -[invoker register body]: ../api_invoker_management/invoker_register_body.json "Invoker Register Body" [invoker onboarding body]: ../api_invoker_management/invoker_details_post_example.json "API Invoker Request" [invoker getauth body]: ../api_invoker_management/invoker_getauth_example.json "Get Auth Example" -[provider register body]: ../api_provider_management/provider_register_body.json "Provider Register Body" [provider request body]: ../api_provider_management/provider_details_post_example.json "API Provider Enrolment Request" -[provider getauth body]: ../api_provider_management/provider_getauth_example.json "Get Auth Example" - - - [Return To All Test Plans]: ../README.md diff --git a/doc/testing/testplan/common_operations/user_getauth_response_body_example.json b/doc/testing/testplan/common_operations/user_getauth_response_body_example.json new file mode 100644 index 0000000..a4a71fe --- /dev/null +++ b/doc/testing/testplan/common_operations/user_getauth_response_body_example.json @@ -0,0 +1,10 @@ +{ + "access_token": "eyJhbGciOiJS...", + "ca_root": "-----BEGIN CERTIFICATE----- ...", + "ccf_api_onboarding_url": "api-provider-management/v1/registrations", + "ccf_discover_url": "service-apis/v1/allServiceAPIs?api-invoker-id=", + "ccf_onboarding_url": "api-invoker-management/v1/onboardedInvokers", + "ccf_publish_url": "published-apis/v1//service-apis", + "ccf_security_url": "capif-security/v1/trustedInvokers/", + "message": "Token and CA root returned successfully" +} \ No newline at end of file diff --git a/doc/testing/testplan/common_operations/user_registration_body.json b/doc/testing/testplan/common_operations/user_registration_body.json new file mode 100644 index 0000000..123c1e1 --- /dev/null +++ b/doc/testing/testplan/common_operations/user_registration_body.json @@ -0,0 +1,6 @@ +{ + "username": "customUser", + "password": "password", + "description": "description", + "email": "customuser@telefonica.com" +} \ No newline at end of file -- GitLab From 9ded2fcd7b75d8276582cfa67fd77c43392dc607 Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Wed, 22 May 2024 16:40:47 +0200 Subject: [PATCH 2/9] New test with Service Invocation API events --- .../testplan/api_events_service/README.md | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/doc/testing/testplan/api_events_service/README.md b/doc/testing/testplan/api_events_service/README.md index 95d55a8..573a498 100644 --- a/doc/testing/testplan/api_events_service/README.md +++ b/doc/testing/testplan/api_events_service/README.md @@ -256,11 +256,89 @@ At this documentation you will have all information and related files and exampl * cause with message "Event API subscription id not found". +## Test Case 6: Invoker receives Service API Invocation events + +**Test ID**: ***capif_api_events-6*** + +**Description**: + + This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. + +**Pre-Conditions**: + + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) + * CAPIF provider is correctly registered and published APIs. + * API Provider had a Service API Published on CAPIF + * Mock Server is up and running to receive requests. + * Mock Server is clean. + +**Information of Test**: + + 1. Perform [provider registration] + 2. Publish Service API at CCF: + + * Send **POST** to ccf_publish_url **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis** + * body [service api description] with apiName **service_1** + * Store *serviceApiId* + * Use APF Certificate + + 3. Perform [invoker onboarding] + 4. Discover published APIs: + + * Get Api Ids And Api Names from response. + + 5. Event Subscription to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE of provider previously registered: + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** + 2. body [event subscription request body] with: + 1. events: **['SERVICE_API_INVOCATION_SUCCESS','SERVICE_API_INVOCATION_FAILURE']** + 2. eventFilter: only receive events from provider's aefId. + 3. Use Invoker Certificate + + 7. Create Log Entry emulating provider receive Success and Failure api invocation from invoker: + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs** + 2. body [log entry request body] with: + 1. aefId from provider published. + 2. apiInvokerId from invoker onboarded. + 3. apiId of published API + 4. apiName of published API + 5. 200 and 400 results in two logs. + 3. Use AEF Certificate + + +**Execution Steps**: + + 1. Register provider and publish one API at CCF + 2. Register Invoker and Onboard Invoker at CCF + 3. Discover published APIs and extract apiIds and apiNames + 4. Subscribe to **SERVICE_API_INVOCATION_SUCCESS** and **SERVICE_API_INVOCATION_FAILURE** event filtering by aefId. + 5. Retrieve {subscriberId} and {subscriptionId} from Location Header + 6. Emulate Success and Failure on API invocation of provider by Invoker, using Invocation Logs API. + +**Expected Result**: + + 1. Response to Event Subscription must accomplish: + 1. **201 Created** + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** + 3. Response Body must follow **EventSubscription** data structure. + 2. Response to creation of log entry on CCF must accomplish: + 1. **201 Created** + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/api-invocation-logs/{apiVersion}/{aefId}/subscriptions/{logId}** + 3. Mock Server received messages must accomplish:ç + 1. **Two Events have been received**. + 2. Validate received events follow **EventNotification** data structure, with **invocationLog** in **eventDetail** parameter. + 1. One should be **SERVICE_API_INVOCATION_SUCCESS** related with **200** result at Log. + 2. The other one must be **SERVICE_API_INVOCATION_FAILURE** related with **400** result at Log. + + + + [invoker onboard request body]: ../api_invoker_management/invoker_details_post_example.json "API Invoker Request" [event subscription request body]: ./event_subscription.json "Event Subscription Request" [invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" +[provider registration]: ../common_operations/README.md#register-a-provider "Provider Registration" +[log entry request body]: ../api_logging_service/invocation_log.json "Log Request Body" [Return To All Test Plans]: ../README.md -- GitLab From b28d36e4221a28831524d9deca07c19223271e7f Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Wed, 22 May 2024 16:54:47 +0200 Subject: [PATCH 3/9] mock server explanation on robot framework documentation --- doc/testing/robotframework/README.md | 5 +++++ doc/testing/testplan/api_events_service/README.md | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/testing/robotframework/README.md b/doc/testing/robotframework/README.md index 7eb83dd..ac38486 100644 --- a/doc/testing/robotframework/README.md +++ b/doc/testing/robotframework/README.md @@ -21,6 +21,11 @@ Results will be stored at /results Please check parameters (include) under *Test Execution* at [Manual Build And Test Execution](#manual-build-and-test-execution). +### Mock Server +Some tests on Test Plans require mockserver. That mock server must be deployed and reachable by Robot Framework and CCF under test. + +If you want to launch only tests that not needed mockserver, just add "--exclude mockserver" parameter to robot execution. + ## Manual Build And Test Execution * **Build Robot docker image**: diff --git a/doc/testing/testplan/api_events_service/README.md b/doc/testing/testplan/api_events_service/README.md index 573a498..d325991 100644 --- a/doc/testing/testplan/api_events_service/README.md +++ b/doc/testing/testplan/api_events_service/README.md @@ -269,8 +269,8 @@ At this documentation you will have all information and related files and exampl * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) * CAPIF provider is correctly registered and published APIs. * API Provider had a Service API Published on CAPIF - * Mock Server is up and running to receive requests. - * Mock Server is clean. + * **Mock Server is up and running to receive requests.** + * **Mock Server is clean.** **Information of Test**: -- GitLab From b429b3d2e57484f9fbcd3ab3c0e2db35ab6c7daa Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Thu, 23 May 2024 17:32:54 +0200 Subject: [PATCH 4/9] Events Tests cases updated with new test for SERVICE_API_INVOCATION, SERVICE_API and API_INVOKER evetns --- .../testplan/api_events_service/README.md | 276 ++++++++++++++++-- 1 file changed, 259 insertions(+), 17 deletions(-) diff --git a/doc/testing/testplan/api_events_service/README.md b/doc/testing/testplan/api_events_service/README.md index d325991..770c112 100644 --- a/doc/testing/testplan/api_events_service/README.md +++ b/doc/testing/testplan/api_events_service/README.md @@ -3,6 +3,7 @@ At this documentation you will have all information and related files and exampl ## Tests +--- ## Test Case 1: Creates a new individual CAPIF Event Subscription. **Test ID**: ***capif_api_events-1*** @@ -21,7 +22,7 @@ At this documentation you will have all information and related files and exampl 2. Event Subscription: 1. Send POST to *https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions* 2. body [event subscription request body] - 3. Use Invoker Certificate + 3. Use **Invoker Certificate** **Execution Steps**: @@ -45,7 +46,7 @@ At this documentation you will have all information and related files and exampl 3. Event Subscriptions are stored in CAPIF Database - +--- ## Test Case 2: Creates a new individual CAPIF Event Subscription with Invalid SubscriberId **Test ID**: ***capif_api_events-2*** @@ -65,7 +66,7 @@ At this documentation you will have all information and related files and exampl 2. Event Subscription: 1. Send POST to *https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_NOT_REGISTERED}/subscriptions* 2. body [event subscription request body] - 3. Use Invoker Certificate + 3. Use **Invoker Certificate** **Execution Steps**: @@ -91,7 +92,7 @@ At this documentation you will have all information and related files and exampl 3. Event Subscriptions are not stored in CAPIF Database - +--- ## Test Case 3: Deletes an individual CAPIF Event Subscription **Test ID**: ***capif_api_events-3*** @@ -111,11 +112,11 @@ At this documentation you will have all information and related files and exampl 2. Event Subscription: 1. Send POST to *https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions* 2. body [event subscription request body] - 3. Use Invoker Certificate + 3. Use **Invoker Certificate** 3. Remove Event Subscription: 1. Send DELETE to *https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions* - 2. Use Invoker Certificate + 2. Use **Invoker Certificate** **Execution Steps**: @@ -144,7 +145,7 @@ At this documentation you will have all information and related files and exampl 5. Event Subscription is not present at CAPIF Database. - +--- ## Test Case 4: Deletes an individual CAPIF Event Subscription with invalid SubscriberId **Test ID**: ***capif_api_events-4*** @@ -165,11 +166,11 @@ At this documentation you will have all information and related files and exampl 2. Event Subscription: 1. Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions 2. body [event subscription request body] - 3. Use Invoker Certificate + 3. Use **Invoker Certificate** 3. Remove Event Subcription with not valid subscriber: 1. Send DELETE to to https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_ID_NOT_VALID}/subscriptions/{subcriptionId} - 2. Use Invoker Certificate + 2. Use **Invoker Certificate** **Execution Steps**: @@ -199,7 +200,7 @@ At this documentation you will have all information and related files and exampl * detail with message "Invoker or APF or AEF or AMF Not found". * cause with message "Subscriber Not Found". - +--- ## Test Case 5: Deletes an individual CAPIF Event Subscription with invalid SubscriptionId **Test ID**: ***capif_api_events-5*** @@ -220,11 +221,11 @@ At this documentation you will have all information and related files and exampl 2. Event Subscription: 1. Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions 2. body [event subscription request body] - 3. Use Invoker Certificate + 3. Use **Invoker Certificate** 3. Remove Event Subcription with not valid subscriber: 1. Send DELETE to to https://{CAPIF_HOSTNAME}/capif-events/v1/{subcriberId}/subscriptions/{SUBSCRIPTION_ID_NOT_VALID} - 2. Use Invoker Certificate + 2. Use **Invoker Certificate** **Execution Steps**: @@ -255,7 +256,7 @@ At this documentation you will have all information and related files and exampl * detail with message "Service API not existing". * cause with message "Event API subscription id not found". - +--- ## Test Case 6: Invoker receives Service API Invocation events **Test ID**: ***capif_api_events-6*** @@ -279,20 +280,20 @@ At this documentation you will have all information and related files and exampl * Send **POST** to ccf_publish_url **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis** * body [service api description] with apiName **service_1** - * Store *serviceApiId* + * Store **serviceApiId** * Use APF Certificate 3. Perform [invoker onboarding] 4. Discover published APIs: - * Get Api Ids And Api Names from response. + * Get **Api Ids** And **Api Names** from response. 5. Event Subscription to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE of provider previously registered: 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** 2. body [event subscription request body] with: 1. events: **['SERVICE_API_INVOCATION_SUCCESS','SERVICE_API_INVOCATION_FAILURE']** 2. eventFilter: only receive events from provider's aefId. - 3. Use Invoker Certificate + 3. Use **Invoker Certificate** 7. Create Log Entry emulating provider receive Success and Failure api invocation from invoker: 1. Send **POST** to **https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs** @@ -323,15 +324,255 @@ At this documentation you will have all information and related files and exampl 2. Response to creation of log entry on CCF must accomplish: 1. **201 Created** 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/api-invocation-logs/{apiVersion}/{aefId}/subscriptions/{logId}** - 3. Mock Server received messages must accomplish:ç + 3. Mock Server received messages must accomplish: 1. **Two Events have been received**. 2. Validate received events follow **EventNotification** data structure, with **invocationLog** in **eventDetail** parameter. 1. One should be **SERVICE_API_INVOCATION_SUCCESS** related with **200** result at Log. 2. The other one must be **SERVICE_API_INVOCATION_FAILURE** related with **400** result at Log. +--- +## Test Case 7: Invoker subscribe to Service API Available and Unavailable events +**Test ID**: ***capif_api_events-7*** + +**Description**: + + This test case will check that a CAPIF Invoker subscribed to SERVICE_API_AVAILABLE and SERVICE_API_UNAVAILABLE, receive the notification when AEF publish and remove it. + +**Pre-Conditions**: + + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) + * CAPIF provider is correctly registered and published APIs. + * **Mock Server is up and running to receive requests.** + * **Mock Server is clean.** + +**Execution Steps**: + + 1. Register provider and publish one API at CCF + 2. Register Invoker and Onboard Invoker at CCF + 3. Discover published APIs and extract apiIds and apiNames + 4. Subscribe to **SERVICE_API_AVAILABLE** and **SERVICE_API_UNAVAILABLE** event filtering by aefId. + 5. Retrieve {subscriberId} and {subscriptionId} from Location Header + 6. Provider publish new API. + 7. Provider remove published API. + + +**Information of Test**: + + 1. Perform [provider registration] + 2. Publish Service API at CCF: + + * Send **POST** to ccf_publish_url **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis** + * body [service api description] with apiName **service_1** + * Store **serviceApiId** + * Use **APF Certificate** + + 3. Perform [invoker onboarding] + 4. Discover published APIs: + + * Get **Api Ids** And **Api Names** from response. + + 5. Event Subscription to SERVICE_API_AVAILABLE and SERVICE_API_UNAVAILABLE of provider previously registered: + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** + 2. body [event subscription request body] with: + 1. events: **['SERVICE_API_AVAILABLE','SERVICE_API_UNAVAILABLE']** + 2. eventFilter: only receive events from provider's aefId. + 3. Use **Invoker Certificate** + + 6. Publish new Service API at CCF: + + * Send **POST** to ccf_publish_url **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis** + * body [service api description] with apiName **service_2** + * Store **serviceApiId** + * Use **APF Certificate** + + 7. Remove published Service API at CCF: + * Send DELETE to resource URL **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID}** + * Use **APF Certificate** + + +**Expected Result**: + + 1. Response to Event Subscription must accomplish: + 1. **201 Created** + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** + 3. Response Body must follow **EventSubscription** data structure. + + 2. Mock Server received messages must accomplish: + 1. **Two Events have been received**. + 2. Validate received events follow **EventNotification** data structure, with **apiIds** in **eventDetail** parameter. + 1. One should be **SERVICE_API_AVAILABLE** apiId of service_2 published API. + 2. The other one must be **SERVICE_API_UNAVAILABLE** apiId of service_1 published API. + +--- +## Test Case 8: Invoker subscribe to Service API Update + +**Test ID**: ***capif_api_events-8*** + +**Description**: + + This test case will check that a CAPIF Invoker subscribed to SERVICE_API_UPDATE, receive the notification when AEF Update some information on API Published. + +**Pre-Conditions**: + + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) + * CAPIF provider is correctly registered and published APIs. + * API Provider had a Service API Published on CAPIF + * **Mock Server is up and running to receive requests.** + * **Mock Server is clean.** + +**Execution Steps**: + + 1. Register Provider and publish one API at CCF + 2. Register Invoker and Onboard Invoker at CCF + 3. Discover published APIs and extract apiIds and apiNames + 4. Subscribe to **SERVICE_API_UPDATE** event filtering by aefId. + 5. Retrieve {subscriberId} and {subscriptionId} from Location Header at event subscription + 6. Provider update information of Service API Published. + +**Information of Test**: + 1. Check and Clean Mock Server + 2. Perform [provider registration] + 3. Publish Service API at CCF: + * Send **POST** to ccf_publish_url **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis** + * body [service api description] with apiName **service_1** + * Use ***APF Certificate*** + * Store **serviceApiId** + + 4. Perform [invoker onboarding] + 5. Discover published APIs: + + * Get **Api Ids** And **Api Names** from response. + + 6. Event Subscription to SERVICE_API_UPDATE of provider previously registered: + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** + 2. body [event subscription request body] with: + 1. events: **['SERVICE_API_UPDATE']** + 2. eventFilter: only receive events from provider's aefId. + 3. Use **Invoker Certificate** + + 7. Update published API at CCF: + * Send **PUT** to resource URL **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}** + * body [service api description] with overrided *apiName* to *service_1_modified* + * Use **APF Certificate** + +**Expected Result**: + + 1. Response to Event Subscription must accomplish: + 1. **201 Created** + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** + 3. Response Body must follow **EventSubscription** data structure. + 2. Response to Update Published Service API: + 1. **200 OK** + 2. Response Body must follow **ServiceAPIDescription** data structure with: + * apiName **service_1_modified** + 3. Mock Server received messages must accomplish: + 1. **One Event has been received**. + 2. Validate received events follow **EventNotification** data structure, with **serviceAPIDescriptions** in **eventDetail** parameter. + 1. Event should be **SERVICE_API_UPDATE** with **eventDetail** with modified **apiName**. + +--- +## Test Case 9: Provider subscribe to API Invoker events + +**Test ID**: ***capif_api_events-9*** + +**Description**: + + This test case will check that a CAPIF Provider subscribed to API Invoker events (API_INVOKER_ONBOARDED, API_INVOKER_UPDATED and API_INVOKER_OFFBOARDED), receive the notifications when Invoker is onboarded, updated and removed respectively. + +**Pre-Conditions**: + + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) + * CAPIF provider is correctly registered. + * **Mock Server is up and running to receive requests.** + * **Mock Server is clean.** + +**Execution Steps**: + + 1. Register Provider at CCF + 2. Subscribe Provider to **API_INVOKER_ONBOARDED, API_INVOKER_UPDATED and API_INVOKER_OFFBOARDED** events. + 3. Register Invoker and Onboard Invoker at CCF + 4. Update Onboarding Information at CCF with a minor change on "notificationDestination" + 5. Offboard Invoker + +**Information of Test**: + + 1. Check and Clean Mock Server + 2. Perform [provider registration] + 3. Event Subscription to API_INVOKER_ONBOARDED, API_INVOKER_UPDATED and API_INVOKER_OFFBOARDED events: + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** + 2. body [event subscription request body] with: + 1. events: **['API_INVOKER_ONBOARDED', 'API_INVOKER_UPDATED', 'API_INVOKER_OFFBOARDED']** + 3. Use **Provider Certificate** + 4. Perform [invoker onboarding] + 5. Update information of previously onboarded Invoker: + * Send *PUT* to **https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}** + * Reference Request Body is: [put invoker onboarding body] + * "notificationDestination": "**http://host.docker.internal:8086/netapp_new_callback**", + 6. Offboard: + * Send **DELETE** to **https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}** + +**Expected Result**: + + 1. Response to Event Subscription must accomplish: + 1. **201 Created** + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** + 3. Response Body must follow **EventSubscription** data structure. + 2. Response to Onboard request must accomplish: + 1. **201 Created** + 2. Response Body must follow **APIInvokerEnrolmentDetails** data structure with: + * apiInvokerId + * onboardingInformation->apiInvokerCertificate must contain the public key signed. + 3. Response Header **Location** must be received with URI to new resource created, following this structure: *{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}* + 3. Response to Update Request (PUT) with minor change must contain: + 1. **200 OK** response. + 2. notificationDestination on response must contain the new value + 4. Response to Offboard Request (DELETE) must contain: + 1. **204 No Content** + 5. Mock Server received messages must accomplish: + 1. **Three Events have been received**. + 2. Validate received events follow **EventNotification** data structure, with **apiInvokerIds** in **eventDetail** parameter. + 1. One Event should be **API_INVOKER_ONBOARDED** with **eventDetail** with modified **apiInvokerId**. + 2. One Event should be **API_INVOKER_UPDATED** with **eventDetail** with modified **apiInvokerId**. + 3. One Event should be **API_INVOKER_OFFBOARDED** with **eventDetail** with modified **apiInvokerId**. +--- +## Test Case 10: Invoker subscribe to ACL update event + +**Test ID**: ***capif_api_events-10*** + +**Description**: + + This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. + + +--- +## Test Case 11: Invoker subscribe to Service API Available and Unavailable events + +**Test ID**: ***capif_api_events-11*** + +**Description**: + + This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. + +--- +## Test Case 12: Invoker subscribe to ACL unavailable event + +**Test ID**: ***capif_api_events-12*** + +**Description**: + + This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. + +--- +## Test Case 13: Invoker subscribe to API Invoker Authorization Revoked + +**Test ID**: ***capif_api_events-13*** + +**Description**: + + This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. [invoker onboard request body]: ../api_invoker_management/invoker_details_post_example.json "API Invoker Request" @@ -339,6 +580,7 @@ At this documentation you will have all information and related files and exampl [invoker onboarding]: ../common_operations/README.md#onboard-an-invoker "Invoker Onboarding" [provider registration]: ../common_operations/README.md#register-a-provider "Provider Registration" [log entry request body]: ../api_logging_service/invocation_log.json "Log Request Body" +[put register body]: ./invoker_details_put_example.json "API Invoker Update Request" [Return To All Test Plans]: ../README.md -- GitLab From 7f1b84cad8e1698386a2ed176e5de8b8de4d9a1a Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Tue, 28 May 2024 12:32:58 +0200 Subject: [PATCH 5/9] New Event tests defined on test suite --- .../testplan/api_events_service/README.md | 249 +++++++++++++++--- 1 file changed, 211 insertions(+), 38 deletions(-) diff --git a/doc/testing/testplan/api_events_service/README.md b/doc/testing/testplan/api_events_service/README.md index 770c112..76ff8c6 100644 --- a/doc/testing/testplan/api_events_service/README.md +++ b/doc/testing/testplan/api_events_service/README.md @@ -12,7 +12,7 @@ At this documentation you will have all information and related files and exampl This test case will check that a CAPIF subscriber (Invoker or Publisher) can Subscribe to Events **Pre-Conditions**: - + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) **Information of Test**: @@ -25,11 +25,11 @@ At this documentation you will have all information and related files and exampl 3. Use **Invoker Certificate** **Execution Steps**: - + 1. Register Invoker and Onboard Invoker at CCF 2. Subscribe to Events 3. Retrieve {subscriberId} and {subscriptionId} from Location Header - + **Expected Result**: 1. Response to Onboard request must accomplish: @@ -56,7 +56,7 @@ At this documentation you will have all information and related files and exampl This test case will check that a CAPIF subscriber (Invoker or Publisher) cannot Subscribe to Events without valid SubcriberId **Pre-Conditions**: - + * CAPIF subscriber is not pre-authorised (has invalid InvokerId or apfId) **Information of Test**: @@ -69,7 +69,7 @@ At this documentation you will have all information and related files and exampl 3. Use **Invoker Certificate** **Execution Steps**: - + 1. Register Invoker and Onboard Invoker at CCF 2. Subscribe to Events @@ -102,7 +102,7 @@ At this documentation you will have all information and related files and exampl This test case will check that a CAPIF subscriber (Invoker or Publisher) can Delete an Event Subscription **Pre-Conditions**: - + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) **Information of Test**: @@ -119,12 +119,12 @@ At this documentation you will have all information and related files and exampl 2. Use **Invoker Certificate** **Execution Steps**: - + 1. Register Invoker and Onboard Invoker at CCF 2. Subscribe to Events 3. Retrieve {subscriberId} and {subscriptionId} from Location Header 4. Remove Event Subscription - + **Expected Result**: 1. Response to Onboard request must accomplish: @@ -155,7 +155,7 @@ At this documentation you will have all information and related files and exampl This test case will check that a CAPIF subscriber (Invoker or Publisher) cannot Delete to Events without valid SubcriberId **Pre-Conditions**: - + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId). * CAPIF subscriber is subscribed to Events. @@ -173,12 +173,12 @@ At this documentation you will have all information and related files and exampl 2. Use **Invoker Certificate** **Execution Steps**: - + 1. Register Invoker and Onboard Invoker at CCF 2. Subscribe to Events 3. Retrieve Location Header with subscriptionId. 4. Remove Event Subscribed with not valid Subscriber. - + **Expected Result**: 1. Response to Onboard request must accomplish: @@ -210,7 +210,7 @@ At this documentation you will have all information and related files and exampl This test case will check that a CAPIF subscriber (Invoker or Publisher) cannot Delete an Event Subscription without valid SubscriptionId **Pre-Conditions**: - + * CAPIF subscriber is pre-authorised (has invalid InvokerId or apfId). * CAPIF subscriber is subscribed to Events. @@ -228,7 +228,7 @@ At this documentation you will have all information and related files and exampl 2. Use **Invoker Certificate** **Execution Steps**: - + 1. Register Invoker and Onboard Invoker at CCF 2. Subscribe to Events 3. Retrieve Location Header with subscriptionId. @@ -266,7 +266,7 @@ At this documentation you will have all information and related files and exampl This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. **Pre-Conditions**: - + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) * CAPIF provider is correctly registered and published APIs. * API Provider had a Service API Published on CAPIF @@ -304,17 +304,16 @@ At this documentation you will have all information and related files and exampl 4. apiName of published API 5. 200 and 400 results in two logs. 3. Use AEF Certificate - **Execution Steps**: - + 1. Register provider and publish one API at CCF 2. Register Invoker and Onboard Invoker at CCF 3. Discover published APIs and extract apiIds and apiNames 4. Subscribe to **SERVICE_API_INVOCATION_SUCCESS** and **SERVICE_API_INVOCATION_FAILURE** event filtering by aefId. 5. Retrieve {subscriberId} and {subscriptionId} from Location Header 6. Emulate Success and Failure on API invocation of provider by Invoker, using Invocation Logs API. - + **Expected Result**: 1. Response to Event Subscription must accomplish: @@ -340,14 +339,14 @@ At this documentation you will have all information and related files and exampl This test case will check that a CAPIF Invoker subscribed to SERVICE_API_AVAILABLE and SERVICE_API_UNAVAILABLE, receive the notification when AEF publish and remove it. **Pre-Conditions**: - + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) * CAPIF provider is correctly registered and published APIs. * **Mock Server is up and running to receive requests.** * **Mock Server is clean.** **Execution Steps**: - + 1. Register provider and publish one API at CCF 2. Register Invoker and Onboard Invoker at CCF 3. Discover published APIs and extract apiIds and apiNames @@ -355,7 +354,6 @@ At this documentation you will have all information and related files and exampl 5. Retrieve {subscriberId} and {subscriptionId} from Location Header 6. Provider publish new API. 7. Provider remove published API. - **Information of Test**: @@ -390,7 +388,7 @@ At this documentation you will have all information and related files and exampl * Send DELETE to resource URL **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID}** * Use **APF Certificate** - + **Expected Result**: 1. Response to Event Subscription must accomplish: @@ -414,7 +412,7 @@ At this documentation you will have all information and related files and exampl This test case will check that a CAPIF Invoker subscribed to SERVICE_API_UPDATE, receive the notification when AEF Update some information on API Published. **Pre-Conditions**: - + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) * CAPIF provider is correctly registered and published APIs. * API Provider had a Service API Published on CAPIF @@ -422,7 +420,7 @@ At this documentation you will have all information and related files and exampl * **Mock Server is clean.** **Execution Steps**: - + 1. Register Provider and publish one API at CCF 2. Register Invoker and Onboard Invoker at CCF 3. Discover published APIs and extract apiIds and apiNames @@ -483,14 +481,14 @@ At this documentation you will have all information and related files and exampl This test case will check that a CAPIF Provider subscribed to API Invoker events (API_INVOKER_ONBOARDED, API_INVOKER_UPDATED and API_INVOKER_OFFBOARDED), receive the notifications when Invoker is onboarded, updated and removed respectively. **Pre-Conditions**: - + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) * CAPIF provider is correctly registered. * **Mock Server is up and running to receive requests.** * **Mock Server is clean.** **Execution Steps**: - + 1. Register Provider at CCF 2. Subscribe Provider to **API_INVOKER_ONBOARDED, API_INVOKER_UPDATED and API_INVOKER_OFFBOARDED** events. 3. Register Invoker and Onboard Invoker at CCF @@ -505,7 +503,7 @@ At this documentation you will have all information and related files and exampl 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** 2. body [event subscription request body] with: 1. events: **['API_INVOKER_ONBOARDED', 'API_INVOKER_UPDATED', 'API_INVOKER_OFFBOARDED']** - 3. Use **Provider Certificate** + 3. Use **Provider AMF Certificate** 4. Perform [invoker onboarding] 5. Update information of previously onboarded Invoker: * Send *PUT* to **https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}** @@ -538,42 +536,216 @@ At this documentation you will have all information and related files and exampl 2. One Event should be **API_INVOKER_UPDATED** with **eventDetail** with modified **apiInvokerId**. 3. One Event should be **API_INVOKER_OFFBOARDED** with **eventDetail** with modified **apiInvokerId**. --- -## Test Case 10: Invoker subscribe to ACL update event +## Test Case 10: Provider subscribed to ACL update event **Test ID**: ***capif_api_events-10*** **Description**: - This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. + This test case will check that a CAPIF Provider subscribed to ACCESS_CONTROL_POLICY_UPDATE receive a notification when ACL Changes. + +**Pre-Conditions**: + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) + * CAPIF provider is correctly registered. + * API Provider had one Service API Published on CAPIF + * API Invoker had a Security Context for the Service API published by provider. + * **Mock Server is up and running to receive requests.** + * **Mock Server is clean.** + +**Execution Steps**: + + 1. Register Provider at CCF. + 2. Publish a provider API with name **service_1**. + 3. Register Invoker and Onboard Invoker at CCF. + 4. Subscribe Provider to **ACCESS_CONTROL_POLICY_UPDATE** event. + 5. Discover APIs filtered by **aef_id** + 6. Create Security Context for Invoker. + 7. Provider Retrieve ACL + +**Information of Test**: + + 1. Check and Clean Mock Server + 2. Perform [provider registration] + 3. Perform [invoker onboarding] + 4. Event Subscription to **ACCESS_CONTROL_POLICY_UPDATE** event: + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** + 2. body [event subscription request body] with: + 1. events: **['ACCESS_CONTROL_POLICY_UPDATE']** + 2. eventFilters: apiInvokerIds array with apiInvokerId of invoker + 3. Use **Provider AMF Certificate** + 5. Discover published APIs + 6. Create Security Context for Invoker + * Send PUT **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** + * body [service security body] + * Use Invoker Certificate + 7. Provider Retrieve ACL + * Send GET **https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}** + * Use **serviceApiId** and **aefId** + * Use AEF Provider Certificate + +**Expected Result**: + + 1. Response to Event Subscription must accomplish: + 1. **201 Created** + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** + 3. Response Body must follow **EventSubscription** data structure. + 2. Create security context: + 1. **201 Created** response. + 2. body returned must accomplish **ServiceSecurity** data structure. + 3. Location Header must contain the new resource URL **{apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}** + 3. ACL Response: + 1. **200 OK** Response. + 2. body returned must accomplish **AccessControlPolicyList** data structure. + 3. apiInvokerPolicies must: + 1. contain only one object. + 2. apiInvokerId must match apiInvokerId registered previously. + 4. Mock Server received messages must accomplish: + 1. **One Event has been received**. + 2. Validate received event follow **EventNotification** data structure, with **accCtrlPolListExt** in **eventDetail** parameter. + 1. One Event should be **ACCESS_CONTROL_POLICY_UPDATE** with **eventDetail** with **accCtrlPolListExt** including the **apiId** and **apiInvokerPolicies**. --- -## Test Case 11: Invoker subscribe to Service API Available and Unavailable events +## Test Case 11: Provider receives an ACL unavailable event when invoker remove Security Context. **Test ID**: ***capif_api_events-11*** **Description**: - This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. + This test case will check that a CAPIF Invoker subscribed to ACCESS_CONTROL_POLICY_UNAVAILABLE will receive the notification when AEF remove Security Context created previously. + +**Pre-Conditions**: + + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) + * CAPIF provider is correctly registered. + * API Provider had one Service API Published on CAPIF + * **Mock Server is up and running to receive requests.** + * **Mock Server is clean.** + +**Execution Steps**: + + 1. Register Provider at CCF. + 2. Publish a provider API with name **service_1**. + 3. Register Invoker and Onboard Invoker at CCF. + 4. Subscribe Invoker to **ACCESS_CONTROL_POLICY_UNAVAILABLE** event. + 5. Discover APIs filtered by **aef_id** + 6. Create Security Context for Invoker. + 7. Provider Retrieve ACL. + 8. Remove Security Context for Invoker. + +**Information of Test**: + + 1. Check and Clean Mock Server + 2. Perform [provider registration] + 3. Perform [invoker onboarding] + 4. Event Subscription to **ACCESS_CONTROL_POLICY_UNAVAILABLE** event: + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** + 2. body [event subscription request body] with: + 1. events: **['ACCESS_CONTROL_POLICY_UNAVAILABLE']** + 2. eventFilters: apiInvokerIds array with apiInvokerId of invoker + 3. Use **Invoker Certificate** + 5. Discover published APIs + 6. Create Security Context for Invoker + * Send PUT **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** + * body [service security body] + * Use Invoker Certificate + 7. Provider Retrieve ACL + * Send GET **https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}** + * Use **serviceApiId** and **aefId** + * Use **AEF Provider Certificate** + 3. Delete Security Context of Invoker by Provider: + * Send DELETE **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** + * Use **AEF certificate** + +**Expected Result**: + 1. Response to Event Subscription must accomplish: + 1. **201 Created** + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** + 3. Response Body must follow **EventSubscription** data structure. + 2. Create security context: + 1. **201 Created** response. + 2. body returned must accomplish **ServiceSecurity** data structure. + 3. Location Header must contain the new resource URL **{apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}** + 3. ACL Response: + 1. **200 OK** Response. + 2. body returned must accomplish **AccessControlPolicyList** data structure. + 3. apiInvokerPolicies must: + 1. contain only one object. + 2. apiInvokerId must match apiInvokerId registered previously. + 4. Delete security context: + 1. **204 No Content** response. + 5. Mock Server received messages must accomplish: + 1. **One Event has been received**. + 2. Validate received event follow **EventNotification** data structure, without **eventDetail** parameter. + 1. One Event should be **ACCESS_CONTROL_POLICY_UNAVAILABLE** without **eventDetail**. --- -## Test Case 12: Invoker subscribe to ACL unavailable event +## Test Case 12: Invoker receives an Invoker Authorization Revoked and ACL unavailable event when Provider revoke Invoker Authorization. **Test ID**: ***capif_api_events-12*** **Description**: - This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. + This test case will check that a CAPIF Invoker subscribed to API_INVOKER_AUTHORIZATION_REVOKED and ACCESS_CONTROL_POLICY_UNAVAILABLE receive both notification when AEF revoke invoker's authorization. ---- -## Test Case 13: Invoker subscribe to API Invoker Authorization Revoked +**Pre-Conditions**: + + * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) + * CAPIF provider is correctly registered. + * API Provider had one Service API Published on CAPIF + * **Mock Server is up and running to receive requests.** + * **Mock Server is clean.** + +**Execution Steps**: -**Test ID**: ***capif_api_events-13*** + 1. Register Provider at CCF. + 2. Publish a provider API with name **service_1**. + 3. Register Invoker and Onboard Invoker at CCF. + 4. Subscribe Invoker to **ACCESS_CONTROL_POLICY_UNAVAILABLE and API_INVOKER_AUTHORIZATION_REVOKED** events. + 5. Discover APIs filtered by **aef_id** + 6. Create Security Context for Invoker. + 7. Revoke Authorization by Provider. -**Description**: +**Information of Test**: - This test case will check that a CAPIF Invoker subscribed to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE, receive the notification when AEF send to logging service result of invocations to their APIs. + 1. Check and Clean Mock Server + 2. Perform [provider registration] + 3. Perform [invoker onboarding] + 4. Event Subscription to **ACCESS_CONTROL_POLICY_UNAVAILABLE and API_INVOKER_AUTHORIZATION_REVOKED** event: + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** + 2. body [event subscription request body] with: + 1. events: **['ACCESS_CONTROL_POLICY_UNAVAILABLE','API_INVOKER_AUTHORIZATION_REVOKED']** + 2. eventFilters: apiInvokerIds array with apiInvokerId of invoker + 3. Use **Invoker Certificate** + 5. Discover published APIs + 6. Create Security Context for Invoker + * Send PUT **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** + * body [service security body] + * Use Invoker Certificate + 7. Revoke Authorization by Provider: + * Send POST **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/delete** + * body [security notification body] + * Using AEF Certificate. + +**Expected Result**: + 1. Response to Event Subscription must accomplish: + 1. **201 Created** + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** + 3. Response Body must follow **EventSubscription** data structure. + 2. Create security context: + 1. **201 Created** response. + 2. body returned must accomplish **ServiceSecurity** data structure. + 3. Location Header must contain the new resource URL **{apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}** + 4. Revoke Authorization: + 1. **204 No Content** response. + 5. Mock Server received messages must accomplish: + 1. **Two Events has been received**. + 2. Validate received event follow **EventNotification** data structure, without **eventDetail** parameter. + 1. One Event should be **ACCESS_CONTROL_POLICY_UNAVAILABLE** without **eventDetail**. + 2. One Event should be **API_INVOKER_AUTHORIZATION_REVOKED** without **eventDetail**. + +--- [invoker onboard request body]: ../api_invoker_management/invoker_details_post_example.json "API Invoker Request" [event subscription request body]: ./event_subscription.json "Event Subscription Request" @@ -581,6 +753,7 @@ At this documentation you will have all information and related files and exampl [provider registration]: ../common_operations/README.md#register-a-provider "Provider Registration" [log entry request body]: ../api_logging_service/invocation_log.json "Log Request Body" [put register body]: ./invoker_details_put_example.json "API Invoker Update Request" - +[service security body]: ../api_security_service/service_security.json "Service Security Request" +[security notification body]: ./security_notification.json "Security Notification Request" [Return To All Test Plans]: ../README.md -- GitLab From f5c55148870146ed770b3eb721d5335df083ce10 Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Tue, 28 May 2024 12:55:07 +0200 Subject: [PATCH 6/9] Added new tests to Test Suite related with Events. Also the order of each information are refactorized --- .../testplan/api_events_service/README.md | 145 +++++++++--------- 1 file changed, 73 insertions(+), 72 deletions(-) diff --git a/doc/testing/testplan/api_events_service/README.md b/doc/testing/testplan/api_events_service/README.md index 76ff8c6..955267f 100644 --- a/doc/testing/testplan/api_events_service/README.md +++ b/doc/testing/testplan/api_events_service/README.md @@ -15,21 +15,21 @@ At this documentation you will have all information and related files and exampl * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) +**Execution Steps**: + + 1. Register Invoker and Onboard Invoker at CCF + 2. Subscribe to Events + 3. Retrieve {subscriberId} and {subscriptionId} from Location Header + **Information of Test**: 1. Perform [Invoker Onboarding] 2. Event Subscription: - 1. Send POST to *https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions* + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** 2. body [event subscription request body] 3. Use **Invoker Certificate** -**Execution Steps**: - - 1. Register Invoker and Onboard Invoker at CCF - 2. Subscribe to Events - 3. Retrieve {subscriberId} and {subscriptionId} from Location Header - **Expected Result**: 1. Response to Onboard request must accomplish: @@ -37,11 +37,11 @@ At this documentation you will have all information and related files and exampl 2. Response Body must follow **APIInvokerEnrolmentDetails** data structure with: * apiInvokerId * onboardingInformation->apiInvokerCertificate must contain the public key signed. - 3. Response Header **Location** must be received with URI to new resource created, following this structure: *{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}* + 3. Response Header **Location** must be received with URI to new resource created, following this structure: **{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}** 2. Response to Event Subscription must accomplish: 1. **201 Created** - 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: *{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId} + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** 3. Response Body must follow **EventSubscription** data structure. 3. Event Subscriptions are stored in CAPIF Database @@ -59,20 +59,20 @@ At this documentation you will have all information and related files and exampl * CAPIF subscriber is not pre-authorised (has invalid InvokerId or apfId) +**Execution Steps**: + + 1. Register Invoker and Onboard Invoker at CCF + 2. Subscribe to Events + **Information of Test**: 1. Perform [Invoker Onboarding] 2. Event Subscription: - 1. Send POST to *https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_NOT_REGISTERED}/subscriptions* + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_NOT_REGISTERED}/subscriptions** 2. body [event subscription request body] 3. Use **Invoker Certificate** -**Execution Steps**: - - 1. Register Invoker and Onboard Invoker at CCF - 2. Subscribe to Events - **Expected Result**: 1. Response to Onboard request must accomplish: @@ -80,7 +80,7 @@ At this documentation you will have all information and related files and exampl 2. Response Body must follow **APIInvokerEnrolmentDetails** data structure with: * apiInvokerId * onboardingInformation->apiInvokerCertificate must contain the public key signed. - 3. Response Header **Location** must be received with URI to new resource created, following this structure: *{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}* + 3. Response Header **Location** must be received with URI to new resource created, following this structure: **{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}** 2. Response to Event Subscription must accomplish: 1. **404 Not Found** @@ -105,26 +105,26 @@ At this documentation you will have all information and related files and exampl * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority) +**Execution Steps**: + + 1. Register Invoker and Onboard Invoker at CCF + 2. Subscribe to Events + 3. Retrieve {subscriberId} and {subscriptionId} from Location Header + 4. Remove Event Subscription + **Information of Test**: 1. Perform [Invoker Onboarding] 2. Event Subscription: - 1. Send POST to *https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions* + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** 2. body [event subscription request body] 3. Use **Invoker Certificate** 3. Remove Event Subscription: - 1. Send DELETE to *https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions* + 1. Send **DELETE** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** 2. Use **Invoker Certificate** -**Execution Steps**: - - 1. Register Invoker and Onboard Invoker at CCF - 2. Subscribe to Events - 3. Retrieve {subscriberId} and {subscriptionId} from Location Header - 4. Remove Event Subscription - **Expected Result**: 1. Response to Onboard request must accomplish: @@ -132,11 +132,11 @@ At this documentation you will have all information and related files and exampl 2. Response Body must follow **APIInvokerEnrolmentDetails** data structure with: * apiInvokerId * onboardingInformation->apiInvokerCertificate must contain the public key signed. - 3. Response Header **Location** must be received with URI to new resource created, following this structure: *{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}* + 3. Response Header **Location** must be received with URI to new resource created, following this structure: **{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}** 2. Response to Event Subscription must accomplish: 1. **201 Created** - 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: *{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId} + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** 3. Response Body must follow **EventSubscription** data structure. 3. Event Subscriptions are stored in CAPIF Database @@ -159,26 +159,26 @@ At this documentation you will have all information and related files and exampl * CAPIF subscriber is pre-authorised (has valid InvokerId or apfId). * CAPIF subscriber is subscribed to Events. +**Execution Steps**: + + 1. Register Invoker and Onboard Invoker at CCF + 2. Subscribe to Events + 3. Retrieve Location Header with subscriptionId. + 4. Remove Event Subscribed with not valid Subscriber. + **Information of Test**: 1. Perform [Invoker Onboarding] 2. Event Subscription: - 1. Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions + 1. Send **POST** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions** 2. body [event subscription request body] 3. Use **Invoker Certificate** 3. Remove Event Subcription with not valid subscriber: - 1. Send DELETE to to https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_ID_NOT_VALID}/subscriptions/{subcriptionId} + 1. Send **DELETE** to **https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_ID_NOT_VALID}/subscriptions/{subcriptionId}** 2. Use **Invoker Certificate** -**Execution Steps**: - - 1. Register Invoker and Onboard Invoker at CCF - 2. Subscribe to Events - 3. Retrieve Location Header with subscriptionId. - 4. Remove Event Subscribed with not valid Subscriber. - **Expected Result**: 1. Response to Onboard request must accomplish: @@ -186,7 +186,7 @@ At this documentation you will have all information and related files and exampl 2. Response Body must follow **APIInvokerEnrolmentDetails** data structure with: * apiInvokerId * onboardingInformation->apiInvokerCertificate must contain the public key signed. - 3. Response Header **Location** must be received with URI to new resource created, following this structure: *{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}* + 3. Response Header **Location** must be received with URI to new resource created, following this structure: **{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}** 2. Response to Event Subscription must accomplish: 1. 201 Created @@ -195,10 +195,11 @@ At this documentation you will have all information and related files and exampl 3. Event Subscriptions are stored in CAPIF Database 4. Error Response Body must accomplish with **ProblemDetails** data structure with: - * status 404 - * title with message "Not Found" - * detail with message "Invoker or APF or AEF or AMF Not found". - * cause with message "Subscriber Not Found". + + * status 404 + * title with message "Not Found" + * detail with message "Invoker or APF or AEF or AMF Not found". + * cause with message "Subscriber Not Found". --- ## Test Case 5: Deletes an individual CAPIF Event Subscription with invalid SubscriptionId @@ -214,26 +215,26 @@ At this documentation you will have all information and related files and exampl * CAPIF subscriber is pre-authorised (has invalid InvokerId or apfId). * CAPIF subscriber is subscribed to Events. +**Execution Steps**: + + 1. Register Invoker and Onboard Invoker at CCF + 2. Subscribe to Events + 3. Retrieve Location Header with subscriptionId. + 4. Remove Event Subscribed with not valid Subscriber. + **Information of Test**: 1. Perform [Invoker Onboarding] 2. Event Subscription: - 1. Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions + 1. Send **POST** to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions 2. body [event subscription request body] 3. Use **Invoker Certificate** 3. Remove Event Subcription with not valid subscriber: - 1. Send DELETE to to https://{CAPIF_HOSTNAME}/capif-events/v1/{subcriberId}/subscriptions/{SUBSCRIPTION_ID_NOT_VALID} + 1. Send **DELETE** to to https://{CAPIF_HOSTNAME}/capif-events/v1/{subcriberId}/subscriptions/{SUBSCRIPTION_ID_NOT_VALID} 2. Use **Invoker Certificate** -**Execution Steps**: - - 1. Register Invoker and Onboard Invoker at CCF - 2. Subscribe to Events - 3. Retrieve Location Header with subscriptionId. - 4. Remove Event Subscribed with not valid Subscriber. - **Expected Result**: 1. Response to Onboard request must accomplish: @@ -241,11 +242,11 @@ At this documentation you will have all information and related files and exampl 2. Response Body must follow **APIInvokerEnrolmentDetails** data structure with: * apiInvokerId * onboardingInformation->apiInvokerCertificate must contain the public key signed. - 3. Response Header **Location** must be received with URI to new resource created, following this structure: *{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}* + 3. Response Header **Location** must be received with URI to new resource created, following this structure: **{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}** 2. Response to Event Subscription must accomplish: 1. **201 Created** - 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: *{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId} + 2. The URI of the created resource shall be returned in the "Location" HTTP header, following this structure: **{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}** 3. Response Body must follow **EventSubscription** data structure. 3. Event Subscriptions are stored in CAPIF Database @@ -273,6 +274,15 @@ At this documentation you will have all information and related files and exampl * **Mock Server is up and running to receive requests.** * **Mock Server is clean.** +**Execution Steps**: + + 1. Register provider and publish one API at CCF + 2. Register Invoker and Onboard Invoker at CCF + 3. Discover published APIs and extract apiIds and apiNames + 4. Subscribe to **SERVICE_API_INVOCATION_SUCCESS** and **SERVICE_API_INVOCATION_FAILURE** event filtering by aefId. + 5. Retrieve {subscriberId} and {subscriptionId} from Location Header + 6. Emulate Success and Failure on API invocation of provider by Invoker, using Invocation Logs API. + **Information of Test**: 1. Perform [provider registration] @@ -305,15 +315,6 @@ At this documentation you will have all information and related files and exampl 5. 200 and 400 results in two logs. 3. Use AEF Certificate -**Execution Steps**: - - 1. Register provider and publish one API at CCF - 2. Register Invoker and Onboard Invoker at CCF - 3. Discover published APIs and extract apiIds and apiNames - 4. Subscribe to **SERVICE_API_INVOCATION_SUCCESS** and **SERVICE_API_INVOCATION_FAILURE** event filtering by aefId. - 5. Retrieve {subscriberId} and {subscriptionId} from Location Header - 6. Emulate Success and Failure on API invocation of provider by Invoker, using Invocation Logs API. - **Expected Result**: 1. Response to Event Subscription must accomplish: @@ -385,7 +386,7 @@ At this documentation you will have all information and related files and exampl * Use **APF Certificate** 7. Remove published Service API at CCF: - * Send DELETE to resource URL **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID}** + * Send **DELETE** to resource URL **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID}** * Use **APF Certificate** @@ -453,7 +454,7 @@ At this documentation you will have all information and related files and exampl 7. Update published API at CCF: * Send **PUT** to resource URL **https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}** - * body [service api description] with overrided *apiName* to *service_1_modified* + * body [service api description] with overrided **apiName** to **service_1_modified** * Use **APF Certificate** **Expected Result**: @@ -506,7 +507,7 @@ At this documentation you will have all information and related files and exampl 3. Use **Provider AMF Certificate** 4. Perform [invoker onboarding] 5. Update information of previously onboarded Invoker: - * Send *PUT* to **https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}** + * Send **PUT** to **https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}** * Reference Request Body is: [put invoker onboarding body] * "notificationDestination": "**http://host.docker.internal:8086/netapp_new_callback**", 6. Offboard: @@ -523,7 +524,7 @@ At this documentation you will have all information and related files and exampl 2. Response Body must follow **APIInvokerEnrolmentDetails** data structure with: * apiInvokerId * onboardingInformation->apiInvokerCertificate must contain the public key signed. - 3. Response Header **Location** must be received with URI to new resource created, following this structure: *{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}* + 3. Response Header **Location** must be received with URI to new resource created, following this structure: **{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}** 3. Response to Update Request (PUT) with minor change must contain: 1. **200 OK** response. 2. notificationDestination on response must contain the new value @@ -576,11 +577,11 @@ At this documentation you will have all information and related files and exampl 3. Use **Provider AMF Certificate** 5. Discover published APIs 6. Create Security Context for Invoker - * Send PUT **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** + * Send **PUT** **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** * body [service security body] * Use Invoker Certificate 7. Provider Retrieve ACL - * Send GET **https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}** + * Send **GET** **https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}** * Use **serviceApiId** and **aefId** * Use AEF Provider Certificate @@ -646,15 +647,15 @@ At this documentation you will have all information and related files and exampl 3. Use **Invoker Certificate** 5. Discover published APIs 6. Create Security Context for Invoker - * Send PUT **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** + * Send **PUT** **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** * body [service security body] * Use Invoker Certificate 7. Provider Retrieve ACL - * Send GET **https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}** + * Send **GET** **https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}** * Use **serviceApiId** and **aefId** * Use **AEF Provider Certificate** 3. Delete Security Context of Invoker by Provider: - * Send DELETE **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** + * Send **DELETE** **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** * Use **AEF certificate** **Expected Result**: @@ -719,11 +720,11 @@ At this documentation you will have all information and related files and exampl 3. Use **Invoker Certificate** 5. Discover published APIs 6. Create Security Context for Invoker - * Send PUT **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** + * Send **PUT** **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}** * body [service security body] * Use Invoker Certificate 7. Revoke Authorization by Provider: - * Send POST **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/delete** + * Send **POST** **https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/delete** * body [security notification body] * Using AEF Certificate. -- GitLab From e99086196dd115b12ec38907e47ac65ca8fa4432 Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Wed, 29 May 2024 13:10:40 +0200 Subject: [PATCH 7/9] Remove Tests tag on events test suite --- doc/testing/testplan/api_events_service/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/testing/testplan/api_events_service/README.md b/doc/testing/testplan/api_events_service/README.md index 955267f..ca220d9 100644 --- a/doc/testing/testplan/api_events_service/README.md +++ b/doc/testing/testplan/api_events_service/README.md @@ -1,8 +1,6 @@ # Test Plan for CAPIF Api Events Service At this documentation you will have all information and related files and examples of test plan for this API. -## Tests - --- ## Test Case 1: Creates a new individual CAPIF Event Subscription. -- GitLab From 35fc6c3fdf880a211716dc4f8a748d857f5deb97 Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Wed, 29 May 2024 13:33:10 +0200 Subject: [PATCH 8/9] updated documentation --- doc/testing/robotframework/README.md | 52 +++++++++++++++++-- .../testplan/api_events_service/README.md | 14 ++--- .../testplan/common_operations/README.md | 6 +++ 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/doc/testing/robotframework/README.md b/doc/testing/robotframework/README.md index ac38486..dfc697e 100644 --- a/doc/testing/robotframework/README.md +++ b/doc/testing/robotframework/README.md @@ -24,14 +24,17 @@ Please check parameters (include) under *Test Execution* at [Manual Build And Te ### Mock Server Some tests on Test Plans require mockserver. That mock server must be deployed and reachable by Robot Framework and CCF under test. -If you want to launch only tests that not needed mockserver, just add "--exclude mockserver" parameter to robot execution. +If you want to launch only tests that not needed mockserver, just add "--exclude mockserver" parameter to robot execution: +``` +./runCapifTests.sh --include --exclude mockserver +``` ## Manual Build And Test Execution * **Build Robot docker image**: ``` cd tools/robot -docker build . -t 5gnow-robot-test:latest +docker build . -t capif-robot-test:latest ``` * **Tests Execution**: @@ -40,11 +43,34 @@ Execute all tests locally: ``` =path in local machine to repository cloned. =path to a folder on local machine to store results of Robot Framework execution. -=Is the hostname set when run.sh is executed, by default it will be capifcore. +=Is the hostname set when run.sh is executed, by default it is capifcore. =This is the port to reach when robot framework want to reach CAPIF deployment using http, this should be set to port without TLS set on Nginx, 8080 by default. +=This is the port to be used when we want to use https connection, this should be set to port with TLS set on Nginx, 443 by default +=This is the hostname of register service deployed. By default it is register. +=This is the port to be used to reach register service deployed. By default it is 8084. +=This is the hostname of vault service. By default it is vault. +=This is the port to be used to reach vault service. By default it is 8200. +=Vault token to be used on request through vault. By default it is "read-ca-token". +=Setup Mock server url to be used in notifications at tests marked with mockserver tag. By default it is not set. To execute all tests run : -docker run -ti --rm --network="host" -v /tests:/opt/robot-tests/tests -v :/opt/robot-tests/results 5gnow-robot-test:latest --variable CAPIF_HOSTNAME:capifcore --variable CAPIF_HTTP_PORT:8080 --include all +docker run -ti --rm --network="host" \ + --add-host host.docker.internal:host-gateway \ + --add-host vault:host-gateway \ + --add-host register:host-gateway \ + --add-host mockserver:host-gateway \ + -v /tests:/opt/robot-tests/tests \ + -v :/opt/robot-tests/results capif-robot-test:latest \ + --variable CAPIF_HOSTNAME:$CAPIF_HOSTNAME \ + --variable CAPIF_HTTP_PORT:$CAPIF_HTTP_PORT \ + --variable CAPIF_HTTPS_PORT:$CAPIF_HTTPS_PORT \ + --variable CAPIF_REGISTER:$CAPIF_REGISTER \ + --variable CAPIF_REGISTER_PORT:$CAPIF_REGISTER_PORT \ + --variable CAPIF_VAULT:$CAPIF_VAULT \ + --variable CAPIF_VAULT_PORT:$CAPIF_VAULT_PORT \ + --variable CAPIF_VAULT_TOKEN:$CAPIF_VAULT_TOKEN \ + --variable MOCK_SERVER_URL:$MOCK_SERVER_URL \ + --include all ``` Execute specific tests locally: @@ -62,7 +88,23 @@ To run more specific tests, for example, only one functionality: "capif_security_api And Run: -docker run -ti --rm --network="host" -v /tests:/opt/robot-tests/tests -v :/opt/robot-tests/results 5gnow-robot-test:latest --variable CAPIF_HOSTNAME:capifcore --variable CAPIF_HTTP_PORT:8080 --include +docker run -ti --rm --network="host" \ + --add-host host.docker.internal:host-gateway \ + --add-host vault:host-gateway \ + --add-host register:host-gateway \ + --add-host mockserver:host-gateway \ + -v /tests:/opt/robot-tests/tests \ + -v :/opt/robot-tests/results capif-robot-test:latest \ + --variable CAPIF_HOSTNAME:$CAPIF_HOSTNAME \ + --variable CAPIF_HTTP_PORT:$CAPIF_HTTP_PORT \ + --variable CAPIF_HTTPS_PORT:$CAPIF_HTTPS_PORT \ + --variable CAPIF_REGISTER:$CAPIF_REGISTER \ + --variable CAPIF_REGISTER_PORT:$CAPIF_REGISTER_PORT \ + --variable CAPIF_VAULT:$CAPIF_VAULT \ + --variable CAPIF_VAULT_PORT:$CAPIF_VAULT_PORT \ + --variable CAPIF_VAULT_TOKEN:$CAPIF_VAULT_TOKEN \ + --variable MOCK_SERVER_URL:$MOCK_SERVER_URL \ + --include ``` ## Test result review diff --git a/doc/testing/testplan/api_events_service/README.md b/doc/testing/testplan/api_events_service/README.md index ca220d9..91ef445 100644 --- a/doc/testing/testplan/api_events_service/README.md +++ b/doc/testing/testplan/api_events_service/README.md @@ -258,7 +258,7 @@ At this documentation you will have all information and related files and exampl --- ## Test Case 6: Invoker receives Service API Invocation events -**Test ID**: ***capif_api_events-6*** +**Test ID**: ***capif_api_events-6***, ***mockserver*** **Description**: @@ -331,7 +331,7 @@ At this documentation you will have all information and related files and exampl --- ## Test Case 7: Invoker subscribe to Service API Available and Unavailable events -**Test ID**: ***capif_api_events-7*** +**Test ID**: ***capif_api_events-7***, ***mockserver*** **Description**: @@ -404,7 +404,7 @@ At this documentation you will have all information and related files and exampl --- ## Test Case 8: Invoker subscribe to Service API Update -**Test ID**: ***capif_api_events-8*** +**Test ID**: ***capif_api_events-8***, ***mockserver*** **Description**: @@ -473,7 +473,7 @@ At this documentation you will have all information and related files and exampl --- ## Test Case 9: Provider subscribe to API Invoker events -**Test ID**: ***capif_api_events-9*** +**Test ID**: ***capif_api_events-9***, ***mockserver*** **Description**: @@ -537,7 +537,7 @@ At this documentation you will have all information and related files and exampl --- ## Test Case 10: Provider subscribed to ACL update event -**Test ID**: ***capif_api_events-10*** +**Test ID**: ***capif_api_events-10***, ***mockserver*** **Description**: @@ -607,7 +607,7 @@ At this documentation you will have all information and related files and exampl --- ## Test Case 11: Provider receives an ACL unavailable event when invoker remove Security Context. -**Test ID**: ***capif_api_events-11*** +**Test ID**: ***capif_api_events-11***, ***mockserver*** **Description**: @@ -681,7 +681,7 @@ At this documentation you will have all information and related files and exampl --- ## Test Case 12: Invoker receives an Invoker Authorization Revoked and ACL unavailable event when Provider revoke Invoker Authorization. -**Test ID**: ***capif_api_events-12*** +**Test ID**: ***capif_api_events-12***, ***mockserver*** **Description**: diff --git a/doc/testing/testplan/common_operations/README.md b/doc/testing/testplan/common_operations/README.md index d6cda4b..0f8cf9f 100644 --- a/doc/testing/testplan/common_operations/README.md +++ b/doc/testing/testplan/common_operations/README.md @@ -32,6 +32,8 @@ The steps to register a new user at Register Service are: * Include **basic Auth Header with User credentials** * Retrieve **access_token** and the urls needed for next requests from response body [user_getauth_response_body_example] +![Flow](../../../images/flows/03_Register_of_AEF_GetAuth.png) + ## Onboard an Invoker ### Steps to perform operation @@ -64,6 +66,8 @@ The steps to register a new user at Register Service are: * onboardingInformation->apiInvokerCertificate must contain the public key signed. 3. Response Header **Location** must be received with URI to new resource created, following this structure: *{apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}* +### Example Flow +![Flow](../../../images/flows/07_Invoker_Onboarding.png) ## Register a Provider @@ -98,6 +102,8 @@ The steps to register a new user at Register Service are: 2. **apiProvCert** under **regInfo** is set properly 4. Location Header must contain the new resource URL *{apiRoot}/api-provider-management/v1/registrations/{registrationId}* +### Example Flow +![Flow](../../../images/flows/07_Invoker_Onboarding.png) [user_registration_body]: ./user_registration_body.json "User Registration Body" -- GitLab From ec55e4d680407b8f1cdd6f3932ee2de23ce75629 Mon Sep 17 00:00:00 2001 From: Jorge Moratinos Salcines Date: Thu, 30 May 2024 17:56:38 +0200 Subject: [PATCH 9/9] Updated over index. Added achitecture and release notes added --- doc/architecture.md | 5 ++ doc/images/logo_osl.png | Bin 15556 -> 0 bytes doc/images/logo_osl_square.png | Bin 64579 -> 0 bytes doc/images/logo_osl_square_non_transp.png | Bin 7391 -> 0 bytes doc/images/openslice_logo.png | Bin 16566 -> 0 bytes doc/images/openslice_logo_old.png | Bin 29968 -> 0 bytes doc/index.md | 69 ++++------------------ doc/releasenotes.md | 45 ++++++++++++++ doc/testing/robotframework/README.md | 10 +++- mkdocs.yml | 2 + 10 files changed, 71 insertions(+), 60 deletions(-) create mode 100644 doc/architecture.md delete mode 100644 doc/images/logo_osl.png delete mode 100644 doc/images/logo_osl_square.png delete mode 100644 doc/images/logo_osl_square_non_transp.png delete mode 100644 doc/images/openslice_logo.png delete mode 100644 doc/images/openslice_logo_old.png create mode 100644 doc/releasenotes.md diff --git a/doc/architecture.md b/doc/architecture.md new file mode 100644 index 0000000..ef7fcf9 --- /dev/null +++ b/doc/architecture.md @@ -0,0 +1,5 @@ +drawing + +# Architecture + + diff --git a/doc/images/logo_osl.png b/doc/images/logo_osl.png deleted file mode 100644 index e6eb89a7da94edc6ea26dcb1972d9e101b0dffee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15556 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 F`9GVZepdhh diff --git a/doc/images/logo_osl_square.png b/doc/images/logo_osl_square.png deleted file mode 100644 index 37cbc4ba60771e54c3ea2f18fa89a7e3af387360..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64579 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 z 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 E12f_TBme*a diff --git a/doc/images/logo_osl_square_non_transp.png b/doc/images/logo_osl_square_non_transp.png deleted file mode 100644 index a7f8684e95a987b15dacb02675bd3ac9ec7ed091..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7391 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 diff --git a/doc/images/openslice_logo.png b/doc/images/openslice_logo.png deleted file mode 100644 index 2fa5efc135209ce66cee07ea7a76dc0a4cff9244..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16566 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

`L#KXwY1xC!kDoppNp z8o`g$fXRX&k-!LiBkxkzEnK5X(5Y59ry@}!967D#=p#AWPl<;Yakgj z!gEEALU5=0w(-_&V|_3Mx~F`H97 zu>E@iEwC*g&$pDr>1F-QPC|{)60G=P zehK!5Tx)3_q#Ryk-LNU+ZxQl$_tg%=UYUce9~=9mT757JM18&D2B~JMiXnm5l6c5S zkV;Lg{QMA*$lAsV!9rmj7a2OY4!kNO0oDONJ}%g>f%W2LQV+Ws za2kCJQmH)FWz_HUpCs~=e(6~nW$PQUO3_+gst|L_e@W(UCc?id{4e7o=(4Dc>ZJa? z*?-;p19ZF5ey|{vQMPKj;qZQ>0h`Q>2a@g0gYMHH5VdwdYIkJzii~J}nBd<-c9|@^ z*NViy>Ums*?ZcIwh%?A$4rYZr9PoErbgv0;wEo=)or}uIHZ6bZEE-N(yqo@g<#HMn zW6hxSR3k4Hiz(xp-@i|52AfcO8*gWGT}FVmf8Rq+r(iml4MYKPgH{<0@Jd7hIA#_Xs{5~eSq_ -# Release X.X.X-rc -This release includes next changes: - -**New register flow to ensure:** - - - isolation between CCF and Register services. - - Improve security, with split resposability between administrator operations and common user. - -**Improved Testing with Robot in order to cover:** - - - New Register flows. - - Allow different URLs for register, ccf and vault services. - -**Improved security on DB:** - - - Credentials requested to access mongo databases. - - Credentials requested also by mongo-express. - -**Scripts upgraded:** - - - docker compose version 2 used on them. - - New cleaning script developed. - -**Cleanup of capif repository:** - - - Documentation is now on splitted repository [OCF Documentation Repository] - - Test plan was moved to [OCF Documentation Repository] - - Obsolote data is removed. - - -# Release 0.0 - -The APIs included in Release 0.0 are: - -- JWT Authentication APIs -- CAPIF Invoker Management API -- CAPIF Publish API -- CAPIF Discover API -- CAPIF Security API -- CAPIF Events API -- CAPIF Provider Management API - -This Release also includes a Robot Test Suite for all those services and a Postman Test Suite for simple testing. - -## What is OpenCAPIF? +# What is OpenCAPIF? OpenCAPIF is an open source implementation of the CAPIF Core Function APIs plus the logic and additional services required to fulfill the 3GPP requirements and deliver the expected functionality. @@ -81,7 +37,7 @@ The following diagram shows how API Invokers and API Providers interact with the If you want to know more about OpenCAPIF check [The story behind openCAPIF](https://ocf.etsi.org/news/20240110_the_story_behind_opencapif/) -## Repository structure +# Repository structure You can check the code at [OpenCAPIF Repository] @@ -101,21 +57,21 @@ CAPIF_API_Services * **tools**: Auxiliary tools. Robot Framework related code and OpenAPI scripts. * **test**: Tests developed using Robot Framework. -## CAPIF_API_Services +# CAPIF_API_Services This repository has the python-flask Mockup servers created with openapi-generator related with CAPIF APIS defined here: [Open API Descriptions of 3GPP 5G APIs] -## How to test CAPIF APIs +# How to test CAPIF APIs The above APIs can be tested either with POSTMAN tool or running the developed tests with Robot Framework. -## Test Plan Documentation +# Test Plan Documentation Complete documentation of tests is available here: [Test Plan Directory] -## Robot Framework +# Robot Framework In order to ensure that modifications over CAPIF services still fulfills the required functionality, the Robot Framework Test Suite must be successfully run. @@ -123,15 +79,15 @@ The Robot Test Suite covers the requirements described in the test plan at [Test Please check the [Testing with Robot Framework] Section -## Using PostMan +# Using PostMan You can also test the CAPIF flow using the Postman tool. To do this, we have created a collection with some examples of CAPIF requests with everything necessary to carry them out. For more information on how to test the APIs with POSTMAN, go to this [POSTMAN Section]. -## Important urls: +# Important urls: -## Mongo CAPIF's DB Dashboard +# Mongo CAPIF's DB Dashboard ``` http://localhost:8082/ (if accessed from localhost) @@ -140,7 +96,7 @@ or http://:8082/ (if accessed from another host) ``` -## Mongo Register's DB Dashboard +# Mongo Register's DB Dashboard ``` http://localhost:8083/ (if accessed from localhost) @@ -149,15 +105,12 @@ or http://:8083/ (if accessed from another host) ``` -## FAQ Documentation +# FAQ Documentation Frequently asked questions can be found here: [FAQ Section] - - - [Test Plan Directory]: ./testing/testplan/README.md "Test Plan Directory" [Testing with Robot Framework]: ./testing/robotframework/README.md "Testing with Robot Framework" [FAQ Section]: ./FAQ.md "FAQ Section" diff --git a/doc/releasenotes.md b/doc/releasenotes.md new file mode 100644 index 0000000..90717e2 --- /dev/null +++ b/doc/releasenotes.md @@ -0,0 +1,45 @@ +# Release X.X.X-rc + +This release includes next changes: + +**New register flow to ensure:** + + - isolation between CCF and Register services. + - Improve security, with split resposability between administrator operations and common user. + +**Improved Testing with Robot in order to cover:** + + - New Register flows. + - Allow different URLs for register, ccf and vault services. + +**Improved security on DB:** + + - Credentials requested to access mongo databases. + - Credentials requested also by mongo-express. + +**Scripts upgraded:** + + - docker compose version 2 used on them. + - New cleaning script developed. + +**Cleanup of capif repository:** + + - Documentation is now on splitted repository [OCF Documentation Repository] + - Test plan was moved to [OCF Documentation Repository] + - Obsolote data is removed. + + +# Release 0.0 + +The APIs included in Release 0.0 are: + +- JWT Authentication APIs +- CAPIF Invoker Management API +- CAPIF Publish API +- CAPIF Discover API +- CAPIF Security API +- CAPIF Events API +- CAPIF Provider Management API + +This Release also includes a Robot Test Suite for all those services and a Postman Test Suite for simple testing. + diff --git a/doc/testing/robotframework/README.md b/doc/testing/robotframework/README.md index dfc697e..fad7717 100644 --- a/doc/testing/robotframework/README.md +++ b/doc/testing/robotframework/README.md @@ -15,7 +15,7 @@ To run any test locally you will need *docker* and *docker-compose* installed in ## Script Test Execution This script will build robot docker image if it's need and execute tests selected by "include" option. Just go to service folder, execute and follow steps. ``` -./runCapifTests.sh --include +./run_capif_tests.sh --include ``` Results will be stored at /results @@ -24,9 +24,15 @@ Please check parameters (include) under *Test Execution* at [Manual Build And Te ### Mock Server Some tests on Test Plans require mockserver. That mock server must be deployed and reachable by Robot Framework and CCF under test. +To run Mock Server locally you can just execute the next script: +``` +cd services +./run_mock_server.sh +``` + If you want to launch only tests that not needed mockserver, just add "--exclude mockserver" parameter to robot execution: ``` -./runCapifTests.sh --include --exclude mockserver +./run_capif_tests.sh --include --exclude mockserver ``` ## Manual Build And Test Execution diff --git a/mkdocs.yml b/mkdocs.yml index fb0bbb0..aa15c1e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -74,6 +74,8 @@ extra: nav: - Overview: - Introduction: index.md + - Release Notes: releasenotes.md + - Architecture: architecture.md - Getting Started: - How to Run: ./gettingstarted/howtorun.md - Testing: -- GitLab