From a3a5a48e73ff4c2cc7e458c77377238ca2c05074 Mon Sep 17 00:00:00 2001 From: Jorge <jorge.moratinossalcines@telefonica.com> Date: Wed, 19 Jun 2024 13:59:34 +0000 Subject: [PATCH] Deployed cd20d91 to develop in public with MkDocs 1.6.0 and mike 2.1.1 --- public/develop/404.html | 2 +- public/develop/FAQ/index.html | 2 +- public/develop/architecture/index.html | 141 +++++++++++++++++- .../gettingstarted/howtorun/index.html | 2 +- .../gettingstarted/repository/index.html | 2 +- .../images/architecture/New_Architecture.png | Bin 0 -> 173563 bytes public/develop/index.html | 2 +- public/develop/releasenotes/index.html | 2 +- public/develop/search/search_index.json | 2 +- public/develop/sitemap.xml | 38 ++--- public/develop/sitemap.xml.gz | Bin 400 -> 398 bytes public/develop/testing/postman/index.html | 2 +- .../develop/testing/robotframework/index.html | 2 +- .../api_access_control_policy/index.html | 2 +- .../testplan/api_auditing_service/index.html | 2 +- .../testplan/api_discover_service/index.html | 2 +- .../testplan/api_events_service/index.html | 2 +- .../api_invoker_management/index.html | 2 +- .../testplan/api_logging_service/index.html | 2 +- .../api_provider_management/index.html | 2 +- .../testplan/api_publish_service/index.html | 2 +- .../testplan/api_security_service/index.html | 2 +- .../testplan/common_operations/index.html | 2 +- public/develop/testing/testplan/index.html | 2 +- 24 files changed, 178 insertions(+), 41 deletions(-) create mode 100644 public/develop/images/architecture/New_Architecture.png diff --git a/public/develop/404.html b/public/develop/404.html index 15fba5fd..b7d327c7 100644 --- a/public/develop/404.html +++ b/public/develop/404.html @@ -16,7 +16,7 @@ <link rel="icon" href="/develop/images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/FAQ/index.html b/public/develop/FAQ/index.html index d1366820..28b57df1 100644 --- a/public/develop/FAQ/index.html +++ b/public/develop/FAQ/index.html @@ -20,7 +20,7 @@ <link rel="icon" href="../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/architecture/index.html b/public/develop/architecture/index.html index 4641f5ef..a5af606d 100644 --- a/public/develop/architecture/index.html +++ b/public/develop/architecture/index.html @@ -22,7 +22,7 @@ <link rel="icon" href="../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> @@ -321,6 +321,17 @@ + <label class="md-nav__link md-nav__link--active" for="__toc"> + + + <span class="md-ellipsis"> + Architecture + </span> + + + <span class="md-nav__icon md-icon"></span> + </label> + <a href="./" class="md-nav__link md-nav__link--active"> @@ -331,6 +342,61 @@ </a> + + +<nav class="md-nav md-nav--secondary" aria-label="Table of contents"> + + + + + + + <label class="md-nav__title" for="__toc"> + <span class="md-nav__icon md-icon"></span> + Table of contents + </label> + <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> + + <li class="md-nav__item"> + <a href="#register-ns" class="md-nav__link"> + <span class="md-ellipsis"> + Register NS + </span> + </a> + +</li> + + <li class="md-nav__item"> + <a href="#vault-ns" class="md-nav__link"> + <span class="md-ellipsis"> + Vault NS + </span> + </a> + +</li> + + <li class="md-nav__item"> + <a href="#mon-ns" class="md-nav__link"> + <span class="md-ellipsis"> + Mon NS + </span> + </a> + +</li> + + <li class="md-nav__item"> + <a href="#new-architecture" class="md-nav__link"> + <span class="md-ellipsis"> + New Architecture + </span> + </a> + +</li> + + </ul> + +</nav> + </li> @@ -551,6 +617,50 @@ + <label class="md-nav__title" for="__toc"> + <span class="md-nav__icon md-icon"></span> + Table of contents + </label> + <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> + + <li class="md-nav__item"> + <a href="#register-ns" class="md-nav__link"> + <span class="md-ellipsis"> + Register NS + </span> + </a> + +</li> + + <li class="md-nav__item"> + <a href="#vault-ns" class="md-nav__link"> + <span class="md-ellipsis"> + Vault NS + </span> + </a> + +</li> + + <li class="md-nav__item"> + <a href="#mon-ns" class="md-nav__link"> + <span class="md-ellipsis"> + Mon NS + </span> + </a> + +</li> + + <li class="md-nav__item"> + <a href="#new-architecture" class="md-nav__link"> + <span class="md-ellipsis"> + New Architecture + </span> + </a> + +</li> + + </ul> + </nav> </div> </div> @@ -565,7 +675,34 @@ <p><img src="../images/logos/OpenCAPIF.png" alt="drawing" width="200"/></p> -<h1 id="architecture">Architecture</h1> +<h1 id="architecture"><strong>Architecture</strong></h1> +<p>The CAPIF architecture has three main components, Register Service, Vault and CCF, which are represented in the following image:</p> +<p><img alt="New Architecture" src="../images/architecture/New_Architecture.png" /></p> +<p>Each component is separated into different namespaces and all communications between them use Rest APIs.</p> +<p>Apart from the communication between components, there are 2 other entities that can use them:</p> +<ul> +<li><strong>Admin/superadmin</strong>: Responsible for managing users with the Register or carrying out special operations in the CCF.</li> +<li><strong>Users</strong>: They are those who want to use CAPIF, registering as a user in the Register and as Invoker or Provider in the CCF.</li> +</ul> +<h2 id="register-ns"><strong>Register NS</strong></h2> +<p>This namespace belongs to the Register service, and we find 2 components:</p> +<ul> +<li><strong>Register Service</strong>: It is responsible for managing all users who use CAPIF, in addition to providing the necessary information for its use.</li> +<li><strong>Register MONGO DATABASE</strong>: It is the Register database, in it we store all the information about registered users.</li> +</ul> +<h2 id="vault-ns"><strong>Vault NS</strong></h2> +<p>This namespace belongs to Vault. </p> +<p>This component is responsible for managing all CAPIF certificates, so other components such as the Register or the CCF communicate with it to create new certificates or request keys.</p> +<h2 id="mon-ns"><strong>Mon NS</strong></h2> +<p>This is the main namespace of CAPIF, since it contains all the CCF services in addition to other components:</p> +<ul> +<li><strong>NGINX</strong>: Responsible for acting as a reverse proxy to distribute CAPIF requests to the different services, controlling whether or not they are authorized to access them.</li> +<li><strong>REDIS</strong>: Used for internal communication of services.</li> +<li><strong>CAPIF MONGO DATABASE</strong>: CAPIF database, where all information related to CAPIF services such as invokers, registered providers or published services is stored.</li> +<li><strong>HELPER</strong>: Service that simplifies integration with third parties such as external management portals.</li> +</ul> +<h2 id="new-architecture"><strong>New Architecture</strong></h2> +<p>You can check the details of all these changes in the conversation on the <a href="https://labs.etsi.org/rep/ocf/community/-/wikis/New%20architecture">OCF wiki</a>.</p> diff --git a/public/develop/gettingstarted/howtorun/index.html b/public/develop/gettingstarted/howtorun/index.html index d3187071..ecc97f2f 100644 --- a/public/develop/gettingstarted/howtorun/index.html +++ b/public/develop/gettingstarted/howtorun/index.html @@ -22,7 +22,7 @@ <link rel="icon" href="../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/gettingstarted/repository/index.html b/public/develop/gettingstarted/repository/index.html index e23c58d1..81f1c3e4 100644 --- a/public/develop/gettingstarted/repository/index.html +++ b/public/develop/gettingstarted/repository/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/images/architecture/New_Architecture.png b/public/develop/images/architecture/New_Architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..6ef7755c59136332a25d9c4ac735fa0784d24442 GIT binary patch literal 173563 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 literal 0 HcmV?d00001 diff --git a/public/develop/index.html b/public/develop/index.html index b079a772..cf981efe 100644 --- a/public/develop/index.html +++ b/public/develop/index.html @@ -20,7 +20,7 @@ <link rel="icon" href="images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/releasenotes/index.html b/public/develop/releasenotes/index.html index ae995001..53eb3a1b 100644 --- a/public/develop/releasenotes/index.html +++ b/public/develop/releasenotes/index.html @@ -22,7 +22,7 @@ <link rel="icon" href="../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/search/search_index.json b/public/develop/search/search_index.json index 1e8854c8..cb6137cb 100644 --- a/public/develop/search/search_index.json +++ b/public/develop/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Introduction","text":""},{"location":"#what-is-opencapif","title":"What is OpenCAPIF?","text":"<p>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.</p> <p>CAPIF is the \u201cCommon API Framework\u201d defined by 3GPP to manage the APIs exposed by 3GPP networks in their northbound interfaces.</p> <p>3GPP specifications for CAPIF defines three types of entities:</p> <ul> <li>API Providers, which are the entities exposing APIs (e.g., NEF)</li> <li>API Invokers, which are the entities that consume APIs (e.g., Applications), and</li> <li>the CAPIF Core Function, which manages the relationships between the other two.</li> </ul> <p></p> <p>The CAPIF Core Function is the cornerstone of the Common API Framework and provides the following capabilities:</p> <ul> <li>Authenticating the API Invoker based on its identity and other information;</li> <li>Supporting mutual authentication with the API Invoker;</li> <li>Providing authorization for the API Invoker prior to accessing the exposed APIs;</li> <li>Publishing, storing, and supporting the discovery of service APIs information;</li> <li>Controlling the service API access based on PLMN operator configured policies;</li> <li>Storing the logs for the service API invocations and providing the service API invocation logs to authorized entities;</li> <li>Charging based on the logs of the service API invocations;</li> <li>Monitoring the service API invocations;</li> <li>Onboarding a new API Invoker and offboarding an API Invoker;</li> <li>Storing policy configurations related to CAPIF and service APIs;</li> <li>Support for accessing the logs for auditing (e.g., detecting abuse); and</li> <li>Support for publishing and discovery of service APIs information among CAPIF Core Function (CAPIF interconnection).</li> </ul> <p>The following diagram shows how API Invokers and API Providers interact with the CAPIF Core Function to Register in CAPIF, Publish APIs, Discover APIs and Consume APIs. It is important to highlight that the CAPIF Core Function is not a classical API Gateway. The API consumption takes place directly between the API Invoker and the API Provider. Therefore, CAPIF does not impact API performance in API consumption between API Invokers and API Providers.</p> <p></p> <p>If you want to know more about OpenCAPIF check The story behind openCAPIF</p>"},{"location":"#repository-structure","title":"Repository structure","text":"<p>You can check the code at OpenCAPIF Repository</p> <pre><code>CAPIF_API_Services\n\u2514\u2500\u2500\u2500helm\n\u2514\u2500\u2500\u2500monitoring\n\u2514\u2500\u2500\u2500services\n\u2514\u2500\u2500\u2500tests\n\u2514\u2500\u2500\u2500tools\n \u2514\u2500\u2500\u2500robot\n \u2514\u2500\u2500\u2500open_api_script\n</code></pre> <ul> <li>helm: This folder contains helm files to deploy capif under k8s environment.</li> <li>monitoring: This folder contains grafana helm files to deploy capif under k8s environment.</li> <li>services: Services developed following CAPIF API specifications. Also, other complementary services (e.g., NGINX and JWTauth services for the authentication of API consuming entities).</li> <li>tools: Auxiliary tools. Robot Framework related code and OpenAPI scripts.</li> <li>test: Tests developed using Robot Framework.</li> </ul>"},{"location":"#capif_api_services","title":"CAPIF_API_Services","text":"<p>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</p>"},{"location":"#how-to-test-capif-apis","title":"How to test CAPIF APIs","text":"<p>The above APIs can be tested either with POSTMAN tool or running the developed tests with Robot Framework.</p>"},{"location":"#test-plan-documentation","title":"Test Plan Documentation","text":"<p>Complete documentation of tests is available here: Test Plan Directory</p>"},{"location":"#robot-framework","title":"Robot Framework","text":"<p>In order to ensure that modifications over CAPIF services still fulfills the required functionality, the Robot Framework Test Suite must be successfully run.</p> <p>The Robot Test Suite covers the requirements described in the test plan at Test Plan Directory folder.</p> <p>Please check the Testing with Robot Framework Section</p>"},{"location":"#using-postman","title":"Using PostMan","text":"<p>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.</p> <p>For more information on how to test the APIs with POSTMAN, go to this POSTMAN Section.</p>"},{"location":"#important-urls","title":"Important urls:","text":""},{"location":"#mongo-capifs-db-dashboard","title":"Mongo CAPIF's DB Dashboard","text":"<pre><code>http://localhost:8082/ (if accessed from localhost) \n\nor\n\nhttp://<Mongo CAPIF Express Host IP>:8082/ (if accessed from another host)\n</code></pre>"},{"location":"#mongo-registers-db-dashboard","title":"Mongo Register's DB Dashboard","text":"<pre><code>http://localhost:8083/ (if accessed from localhost) \n\nor\n\nhttp://<Mongo Register Express Host IP>:8083/ (if accessed from another host)\n</code></pre>"},{"location":"#faq-documentation","title":"FAQ Documentation","text":"<p>Frequently asked questions can be found here: FAQ Section</p>"},{"location":"FAQ/","title":"Frequently Asked Questions (FAQ)","text":""},{"location":"FAQ/#does-the-user-have-to-develop-the-3-elements-of-the-provider-aef-amf-and-apf","title":"Does the user have to develop the 3 elements of the provider (AEF, AMF and APF)?","text":"<p>No, you only have to make the request to the \"/onboarding\" endpoint. In it you must specify a CSR for the AEF, APF and AMF and you will receive the certificates for each of them in the response.</p>"},{"location":"FAQ/#there-is-one-party-that-publishes-the-api-and-another-that-exposes-it-what-is-the-difference","title":"There is one party that publishes the API and another that exposes it, what is the difference?","text":"<p>There are different services, the APF, intended for publishing the APIs, and the AEF, intended so that the invoker can call it. The APF is what connects to the Capif Core Function to publish the service and when the service is up, you need the AEF service so that invokers can connect to it.</p>"},{"location":"FAQ/#before-publishing-an-api-do-you-have-to-be-registered-in-capif","title":"Before publishing an API, do you have to be registered in CAPIF?","text":"<p>Yes, before publishing an API you must register using the POST /register endpoint.</p>"},{"location":"FAQ/#where-is-the-registration-done","title":"Where is the registration done?","text":"<p>Registration is done in a REST API outside of the CAPIF specification taht we have implemented.</p>"},{"location":"FAQ/#is-the-username-and-password-chosen-by-the-user-when-registering-or-is-it-assigned-when-requesting-registration-to-capif-public-instance","title":"Is the username and password chosen by the user when registering or is it assigned when requesting registration to CAPIF public instance?","text":"<p>When you make the request to the \"/endpoint\" of register, you will be returned a username and a password determined by CAPIF.</p>"},{"location":"FAQ/#what-is-a-csr","title":"What is a CSR?","text":"<p>A CSR is a Certificate Signing Request. It is a generated data block where the certificate is planned to be installed and contains key information such as public key, organization, and location, and is used to request a certificate from a certificate authority (CA). In CAPIF, 3 CSRs are necessary to register a provider, for AEF, APF and AMF.</p>"},{"location":"FAQ/#when-doing-the-register_provider-where-can-i-find-the-csrs-that-are-generated","title":"When doing the register_provider where can I find the CSRs that are generated?","text":"<p>When using the \"register_provider\" command, if you add the \"debug\" option, it shows you a json with the data used to register the provider. There we can find in the body a list of 3 elements corresponding to AEF, APF and AMF. IN each of them, the apiProbPubKey field corresponds to the CSR.</p>"},{"location":"FAQ/#how-to-use-the-example-client-capif_invoker_gui","title":"How to use the example client (CAPIF_INVOKER_GUI)?","text":"<p>First you have to make a \"./run.sh host:port\" indicating the address of the public CAPIF. Once the Docker containers are up, you have to do a \"./terminal_to_py_netapp.sh\" and then a \"python main.py\". At this point we will find ourselves in a console with some predefined commands to use the Client. If we press tab twice it will bring up the list of available commands.</p>"},{"location":"FAQ/#where-is-the-capif-public-instance-located","title":"Where is the CAPIF public instance located?","text":"<p>The CAPIF public instance can be found at the following URLs: - capif.mobilesandbox.cloud:37211 (HTTPS) - capif.mobilesandbox.cloud:37212 (HTTP)</p>"},{"location":"FAQ/#do-you-have-to-publish-3-apis-one-for-each-instance","title":"Do you have to publish 3 APIs? one for each instance?","text":"<p>No, you only have to publish a single API but each component is responsible for a specific service, whether publishing or exposing.</p>"},{"location":"FAQ/#once-the-api-is-published-is-it-always-active-or-do-you-have-to-republish-it-every-time-you-want-to-use-it","title":"Once the API is published, is it always active? Or do you have to republish it every time you want to use it?","text":"<p>It is better to unsubscribe the API every time you exit the application since otherwise it could be republished and it would be double.</p>"},{"location":"FAQ/#would-the-same-username-and-password-be-valid-for-different-invokers","title":"Would the same username and password be valid for different invokers?","text":"<p>Yes, a user can have multiple invokers at the same time, and as such, the username and password would be the same.</p>"},{"location":"FAQ/#what-is-the-notfication-destination-field-in-the-register_invoker-request","title":"What is the notfication destination field in the register_invoker request?","text":"<p>This is the callback URL used to notify events. CAPIF has an Event service to subscribe to that notifies actions such as a subscription to an API, a change in the state of an API...</p>"},{"location":"FAQ/#is-the-notification_destination-a-required-field-in-the-register_invoker","title":"Is the notification_destination a required field in the register_invoker","text":"<p>No, it is not mandatory, but if you do not enter it you will not receive any CAPIF events. For example, the APF may delete the API, you will not be notified that the API is no longer available.</p>"},{"location":"FAQ/#what-is-the-purpose-of-the-discover_service-function-in-the-invoker-client","title":"What is the purpose of the \"discover_service\" function in the invoker client?","text":"<p>The discover_service returns a json with all the services that exist exposed in CAPIF at that moment.</p>"},{"location":"FAQ/#what-is-the-purpose-of-the-get_security_auth-function-in-the-invoker-client","title":"What is the purpose of the \"get_security_auth\" function in the invoker client?","text":"<p>Sirve para pedir el token o para refrescarlo en caso de que haya caducado. You have to use that token to call the API from the invoker.</p>"},{"location":"FAQ/#what-is-the-purpose-of-the-register_security_context-function-in-the-invoker-client","title":"What is the purpose of the \"register_security_context\" function in the invoker client?","text":"<p>To consume the API it is necessary to have a Security Context registered with the data and the authentication method.</p>"},{"location":"FAQ/#is-a-user-the-same-as-an-exposer","title":"Is a user the same as an exposer?","text":"<p>No, a user registers in CAPIF and once done can have the role of invoker, provider or both.</p>"},{"location":"FAQ/#where-can-i-put-my-endpoint","title":"Where can I put my endpoint?","text":"<p>You have to set your endpoint when doing the \"publish_service\" functionality: <code>publish_service capif_ops/config_files/service_api_description_hello.json</code></p> <p>In the file \"service_api_description_hello.json\" you configure the service that is going to be exposed and by developing one to suit you, you expose your API.</p>"},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#architecture","title":"Architecture","text":""},{"location":"releasenotes/","title":"Release X.X.X-rc","text":"<p>This release includes next changes:</p> <p>New register flow to ensure:</p> <ul> <li>isolation between CCF and Register services.</li> <li>Improve security, with split resposability between administrator operations and common user.</li> </ul> <p>Improved Testing with Robot in order to cover:</p> <ul> <li>New Register flows.</li> <li>Allow different URLs for register, ccf and vault services.</li> </ul> <p>Improved security on DB:</p> <ul> <li>Credentials requested to access mongo databases.</li> <li>Credentials requested also by mongo-express.</li> </ul> <p>Scripts upgraded:</p> <ul> <li>docker compose version 2 used on them.</li> <li>New cleaning script developed.</li> </ul> <p>Cleanup of capif repository:</p> <ul> <li>Documentation is now on splitted repository [OCF Documentation Repository]</li> <li>Test plan was moved to [OCF Documentation Repository]</li> <li>Obsolote data is removed.</li> </ul>"},{"location":"releasenotes/#release-00","title":"Release 0.0","text":"<p>The APIs included in Release 0.0 are:</p> <ul> <li>JWT Authentication APIs</li> <li>CAPIF Invoker Management API</li> <li>CAPIF Publish API</li> <li>CAPIF Discover API</li> <li>CAPIF Security API</li> <li>CAPIF Events API</li> <li>CAPIF Provider Management API</li> </ul> <p>This Release also includes a Robot Test Suite for all those services and a Postman Test Suite for simple testing.</p>"},{"location":"gettingstarted/howtorun/","title":"How to Run","text":"<ul> <li>Downloading the project</li> <li>1. Create a folder to download the project</li> <li>2. Download the deployment script</li> <li>3. Run the deployment script</li> <li>Run All CAPIF Services locally with Docker images</li> <li>Run All CAPIF Services locally with Docker images and deploy monitoring stack</li> <li>Run each service using Docker</li> <li>Run each service using Python</li> <li>Start Your Testing with OpenCAPIF</li> </ul> <p>Capif services are developed under services folder.</p>"},{"location":"gettingstarted/howtorun/#downloading-the-project","title":"Downloading the project","text":"<p>You can easily download CAPIF to run in local environment following next steps:</p>"},{"location":"gettingstarted/howtorun/#1-create-a-folder-to-download-the-project","title":"1. Create a folder to download the project","text":"<pre><code>mkdir OpenCAPIF\n\ncd OpenCAPIF\n</code></pre>"},{"location":"gettingstarted/howtorun/#2-download-the-deployment-script","title":"2. Download the deployment script","text":"<p>Download the deployment / environment preparation script (press here to directly download script):</p> <pre><code>wget https://labs.etsi.org/rep/ocf/capif/-/raw/staging/deploy.sh\n</code></pre> <p>Make it executable:</p> <pre><code>chmod +x deploy.sh\n</code></pre>"},{"location":"gettingstarted/howtorun/#3-run-the-deployment-script","title":"3. Run the deployment script","text":"<p>This script selects the branch for capif repository project to pull from.</p> <p>If you run the script without selecting a branch the the main branch is going to be selected.</p> <p>We recommend:</p> <ul> <li>main branch for the most stable experience and staging branch for an experience with the latest features (for staging branch installation, it is strongly advisable that you may as well follow the staging documentation)</li> </ul> <pre><code># ./deploy.sh [branch to fetch] [true or false (default) to install monitoring stack or not]\n\nsudo ./deploy.sh staging\n</code></pre> <p>We recommend running the deploy.sh script with root permissions! In other case, some directories may not be accessible by the project building tools and hinder the smooth installation.</p>"},{"location":"gettingstarted/howtorun/#run-all-capif-services-locally-with-docker-images","title":"Run All CAPIF Services locally with Docker images","text":"<p>To run using docker and docker compose, version 2.10 or higher, you must ensure you have those tools installed in your machine. Also to simplify the process, we have 3 scripts allowing docker images to deploy, check and cleanup.</p> <p>All these scripts are available under services directory.</p> <p>To run CAPIF APIs locally using docker and docker-compose you can use run.sh script:</p> <pre><code>./run.sh -h\n\nUsage: ./run.sh <options>\n -c : Setup different hostname for capif\n -m : Launch monitoring service\n -h : show this help\n</code></pre> <p>This script builds and runs all services using docker images, including mongodb and nginx locally and in the background, and imports ca.crt to nginx. By default monitoring is not activated and Nginx is deployed use capifcore as a hostname. </p> <p>Some examples of use:</p> <pre><code># Default values, No monitoring and capifcore as CAPIF_HOSTNAME\n./run.sh\n\n# opencapif.etsi.org as CAPIF_HOSTNAME\n./run.sh -c opencapif.etsi.org\n\n# opencapif.etsi.org as CAPIF_HOSTNAME and monitoring activated\n./run.sh -c opencapif.etsi.org -m \n\n</code></pre> <p>If you want to check if all CAPIF services are running properly in a local machine after executing run.sh, you can use:</p> <pre><code>./check_services_are_running.sh\n</code></pre> <p>This shell script will return 0 if all services are running properly.</p> <p>When we need to stop all CAPIF services, we can use next bash script:</p> <pre><code>./clean_capif_docker_services.sh -a\n</code></pre> <p>NOTE: You can use different flags if you only want to stop some of them, please check the help using</p> <pre><code>./clean_capif_docker_services.sh -h\n\nUsage: clean_capif_docker_services.sh <options>\n -c : clean capif services\n -v : clean vault service\n -r : clean register service\n -m : clean monitoring service\n -a : clean all services\n -h : show this help\n</code></pre> <p>This shell script will remove and clean all CAPIF services started previously with run.sh</p> <p>On the other hand you can check logs using show_logs.sh script, please check options:</p> <pre><code>./show_logs.sh\nYou must specify an option when running the script.\nUsage: ./show_logs.sh <options>\n -c : Show capif services\n -v : Show vault service\n -r : Show register service\n -m : Show monitoring service\n -a : Show all services\n -f : Follow log output\n -h : Show this help\n</code></pre> <p>You can also use option -f in order to follow log output in real time</p>"},{"location":"gettingstarted/howtorun/#run-all-capif-services-locally-with-docker-images-and-deploy-monitoring-stack","title":"Run All CAPIF Services locally with Docker images and deploy monitoring stack","text":"<p>It is now possible to deploy a monitoring stack for CAPIF with Grafana, Prometheus, FluentBit, Loki, Cadvisor, Tempo and Opentelemetry.</p> <p>To deploy CAPIF together with the monitoring stack, it is only necessary to execute the following.</p> <pre><code>./run.sh -m true\n</code></pre> <p>After they have been built, the different panels can be consulted in Grafana at the url</p> <pre><code>http://localhost:3000\n</code></pre> <p>By default, the monitoring option is set to false. Once up, all data sources and dashboards are automatically provisioned.</p>"},{"location":"gettingstarted/howtorun/#run-each-service-using-docker","title":"Run each service using Docker","text":"<p>Also you can run OpenCAPIF service by service using docker:</p> <pre><code>cd <Service>\ndocker build -t capif_security .\ndocker run -p 8080:8080 capif_security\n</code></pre>"},{"location":"gettingstarted/howtorun/#run-each-service-using-python","title":"Run each service using Python","text":"<p>Run using python</p> <pre><code>cd <Service>\npip3 install -r requirements.txt\npython3 -m <service>\n</code></pre>"},{"location":"gettingstarted/howtorun/#start-your-testing-with-opencapif","title":"Start Your Testing with OpenCAPIF","text":"<p>Related with OpenCAPIF Testing, the following sections help you to understand testing implemented and how to run it by yourself:</p> <ul> <li>Test Plan Directory: Here you can find the complete test plan definition that are accomplish by all versions released of OpenCAPIF.</li> <li>Testing with Robot Framework: At this section you can find all information about how to run the test suite implemented using Robot Framework.</li> <li>Testing with Postman: Easy way to understand the complete basic OpenCAPIF flow, acting as invoker and provider.</li> </ul>"},{"location":"testing/postman/","title":"Postman","text":"<p>In this section we can use Postman to publish an API as a provider and use it as an invoker.</p>"},{"location":"testing/postman/#requisites","title":"Requisites","text":"<ul> <li>We will need to have Node.js installed since we will use a small script to create the CSRs of the certificates.</li> <li>An instance of CAPIF (If it is not local, certain variables would have to be modified both in the Node.js script and in the Postman environment variables).</li> </ul>"},{"location":"testing/postman/#first-steps","title":"First steps","text":"<ol> <li>Install the Node dependencies package.json to run the script with:</li> </ol> <pre><code>npm i\n</code></pre> <ol> <li>Run the script.js with the following command:</li> </ol> <pre><code>node script.js\n</code></pre> <ol> <li>Import Postman collection and environment variables (CAPIF.postman_collection.json and CAPIF.postman_environment.json)</li> <li>Select CAPIF Environment before start testing.</li> </ol>"},{"location":"testing/postman/#remote-capif","title":"Remote CAPIF","text":"<p>If the CAPIF is not local, the host and port of both the CAPIF and the register would have to be specified in the variables, and the CAPIF_HOSTNAME in the script, necessary to obtain the server certificate.</p> <p>Enviroments in Postman</p> <pre><code>CAPIF_HOSTNAME capifcore\nCAPIF_PORT 8080\nREGISTER_HOSTNAME register\nREGISTER_PORT 8084\n</code></pre> <p>Const in script.js</p> <pre><code>CAPIF_HOSTNAME capifcore\n</code></pre>"},{"location":"testing/postman/#capif-flows","title":"CAPIF Flows","text":"<p>Once the first steps have been taken, we can now use Postman requests. These requests are numbered in the order that must be followed to obtain everything necessary from CAPIF.</p>"},{"location":"testing/postman/#creation-of-user-by-admin","title":"Creation of User by Admin","text":"<p>The first step would be for an administrator to create a user with which a provider and an invoker will be created. To do this, the admin must log in to obtain the token needed in admin requests.</p>"},{"location":"testing/postman/#01-login_admin","title":"01-Login_admin","text":""},{"location":"testing/postman/#02-creation-of-user","title":"02-Creation of User","text":""},{"location":"testing/postman/#publication-of-an-api","title":"Publication of an API","text":"<p>The next step is to register a provider using the user created by the administrator in order to publish an API.</p>"},{"location":"testing/postman/#03-getauth_provider","title":"03-getauth_provider","text":""},{"location":"testing/postman/#04-onboard_provider","title":"04-onboard_provider","text":"<p>At this point we move on to using certificate authentication in CAPIF. In Postman it is necessary to add the certificates manually and using more than one certificate for the same host as we do in CAPIF complicates things. For this reason, we use the script to overwrite a certificate and a key when it is necessary to have a specific one.</p> <p>To configure go to settings in Postman and open the certificates section. </p> <ul> <li>Here, activate the CA certificates option and add the ca_cert.pem file found in the Responses folder.</li> <li>Adds a client certificate specifying the CAPIF host being used and the files client_cert.crt and client_key.key in the Responses folder.</li> </ul> <p>Once this is done, the node script will be in charge of changing the certificate that is necessary in each request.</p>"},{"location":"testing/postman/#05-publish_api","title":"05-publish_api","text":"<p>Once the api is published, we can start it. In this case we have a test one created in python called hello_api.py that can be executed with the following command:</p> <pre><code>python3 hello_api.py\n</code></pre> <p>The API publication interface is set to localhost with port 8088, so the service must be set up locally. If you wanted to build it on another site, you would have to change the interface description in the body of publish_api.</p> <p>With this the provider part would be finished.</p>"},{"location":"testing/postman/#calling-the-api","title":"Calling the API","text":"<p>Finally, we will create an invoker with the user given by the administrator to be able to use the published api.</p>"},{"location":"testing/postman/#06-getauth_invoker","title":"06-getauth_invoker","text":""},{"location":"testing/postman/#07-onboard_invoker","title":"07-onboard_invoker","text":"<p>At this point we move on to using certificate authentication in CAPIF. If you did not configure the provider's certificates, you would have to do it now.</p>"},{"location":"testing/postman/#08-discover","title":"08-discover","text":""},{"location":"testing/postman/#09-security_context","title":"09-security_context","text":""},{"location":"testing/postman/#10-get_token","title":"10-get_token","text":""},{"location":"testing/postman/#11-call_service","title":"11-call_service","text":"<p>With this, we would have made the API call and finished the flow.</p>"},{"location":"testing/postman/#other-requests","title":"Other requests","text":"<p>Other requests that we have added are the following:</p> <ul> <li>offboard_provider Performs offboarding of the provider, thereby eliminating the published APIs.</li> <li>offboard_invoker Offboards the invoker, also eliminating access to the APIs of that invoker.</li> <li>remove_user Delete the user.</li> <li>refresh_admin_token Return a new access token to the admin.</li> </ul>"},{"location":"testing/postman/#notes","title":"Notes","text":"<ul> <li>This process is designed to teach how requests are made in Postman and the flow that should be followed to publish and use an API.</li> <li>It is possible that if external CAPIFs are used (Public CAPIF) the test data may already be used or the API already registered.</li> <li>It is necessary to have the Node service running to make the certificate change for the requests, otherwise it will not work.</li> <li>We are working on adding more requests to the Postman collection.</li> <li>This collection is a testing guide and is recommended for testing purposes only.</li> </ul>"},{"location":"testing/robotframework/","title":"Robot Framework","text":""},{"location":"testing/robotframework/#steps-to-test","title":"Steps to Test","text":"<p>To run any test locally you will need docker and docker-compose installed in order run services and execute test plan. Steps will be:</p> <ul> <li> <p>Run All Services: See section Run All CAPIF Services</p> </li> <li> <p>Run desired tests: At this point we have 2 options:</p> </li> <li> <p>Using helper script: Script Test Execution</p> </li> <li>Build robot docker image and execute manually robot docker: Manual Build And Test Execution</li> </ul>"},{"location":"testing/robotframework/#script-test-execution","title":"Script Test Execution","text":"<p>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.</p> <pre><code>./run_capif_tests.sh --include <TAG>\n</code></pre> <p>Results will be stored at /results <p>Please check parameters (include) under Test Execution at Manual Build And Test Execution.</p>"},{"location":"testing/robotframework/#mock-server","title":"Mock Server","text":"<p>Some tests on Test Plans require mockserver. That mock server must be deployed and reachable by Robot Framework and CCF under test.</p> <p>To run Mock Server locally you can just execute the next script:</p> <pre><code>cd services\n./run_mock_server.sh\n\nor\n./run.sh -s\n</code></pre> <p>If you want to launch only tests that not needed mockserver, just add \"--exclude mockserver\" parameter to robot execution:</p> <pre><code>./run_capif_tests.sh --include <TAG> --exclude mockserver\n</code></pre> <p>After run tests the Mock Server can be removed from local deployment:</p> <pre><code>./clean_mock_server.sh\n\nor\n./clean_capif_docker_services.sh -s\n</code></pre>"},{"location":"testing/robotframework/#manual-build-and-test-execution","title":"Manual Build And Test Execution","text":"<ul> <li>Build Robot docker image:</li> </ul> <pre><code>cd tools/robot\ndocker build . -t capif-robot-test:latest\n</code></pre> <ul> <li>Tests Execution:</li> </ul> <p>Execute all tests locally:</p> <pre><code><PATH_TO_REPOSITORY>=path in local machine to repository cloned.\n<PATH_RESULT_FOLDER>=path to a folder on local machine to store results of Robot Framework execution.\n<CAPIF_HOSTNAME>=Is the hostname set when run.sh is executed, by default it is capifcore.\n<CAPIF_HTTP_PORT>=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.\n<CAPIF_HTTPS_PORT>=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\n<CAPIF_REGISTER>=This is the hostname of register service deployed. By default it is register.\n<CAPIF_REGISTER_PORT>=This is the port to be used to reach register service deployed. By default it is 8084.\n<CAPIF_VAULT>=This is the hostname of vault service. By default it is vault.\n<CAPIF_VAULT_PORT>=This is the port to be used to reach vault service. By default it is 8200.\n<CAPIF_VAULT_TOKEN>=Vault token to be used on request through vault. By default it is \"read-ca-token\".\n<MOCK_SERVER_URL>=Setup Mock server url to be used in notifications at tests marked with mockserver tag. By default it is not set.\n\nTo execute all tests run :\ndocker run -ti --rm --network=\"host\" \\\n --add-host host.docker.internal:host-gateway \\\n --add-host vault:host-gateway \\\n --add-host register:host-gateway \\\n --add-host mock-server:host-gateway \\\n -v <PATH_TO_REPOSITORY>/tests:/opt/robot-tests/tests \\\n -v <PATH_RESULT_FOLDER>:/opt/robot-tests/results capif-robot-test:latest \\\n --variable CAPIF_HOSTNAME:$CAPIF_HOSTNAME \\\n --variable CAPIF_HTTP_PORT:$CAPIF_HTTP_PORT \\\n --variable CAPIF_HTTPS_PORT:$CAPIF_HTTPS_PORT \\\n --variable CAPIF_REGISTER:$CAPIF_REGISTER \\\n --variable CAPIF_REGISTER_PORT:$CAPIF_REGISTER_PORT \\\n --variable CAPIF_VAULT:$CAPIF_VAULT \\\n --variable CAPIF_VAULT_PORT:$CAPIF_VAULT_PORT \\\n --variable CAPIF_VAULT_TOKEN:$CAPIF_VAULT_TOKEN \\\n --variable MOCK_SERVER_URL:$MOCK_SERVER_URL \\\n --include all\n</code></pre> <p>Execute specific tests locally:</p> <pre><code>To run more specific tests, for example, only one functionality:\n<TAG>=Select one from list:\n \"capif_api_acl\",\n \"capif_api_auditing_service\",\n \"capif_api_discover_service\",\n \"capif_api_events\",\n \"capif_api_invoker_management\",\n \"capif_api_logging_service\",\n \"capif_api_provider_management\",\n \"capif_api_publish_service\",\n \"capif_security_api\n\nAnd Run:\ndocker run -ti --rm --network=\"host\" \\\n --add-host host.docker.internal:host-gateway \\\n --add-host vault:host-gateway \\\n --add-host register:host-gateway \\\n --add-host mock-server:host-gateway \\\n -v <PATH_TO_REPOSITORY>/tests:/opt/robot-tests/tests \\\n -v <PATH_RESULT_FOLDER>:/opt/robot-tests/results capif-robot-test:latest \\\n --variable CAPIF_HOSTNAME:$CAPIF_HOSTNAME \\\n --variable CAPIF_HTTP_PORT:$CAPIF_HTTP_PORT \\\n --variable CAPIF_HTTPS_PORT:$CAPIF_HTTPS_PORT \\\n --variable CAPIF_REGISTER:$CAPIF_REGISTER \\\n --variable CAPIF_REGISTER_PORT:$CAPIF_REGISTER_PORT \\\n --variable CAPIF_VAULT:$CAPIF_VAULT \\\n --variable CAPIF_VAULT_PORT:$CAPIF_VAULT_PORT \\\n --variable CAPIF_VAULT_TOKEN:$CAPIF_VAULT_TOKEN \\\n --variable MOCK_SERVER_URL:$MOCK_SERVER_URL \\\n --include <TAG>\n</code></pre>"},{"location":"testing/robotframework/#test-result-review","title":"Test result review","text":"<p>In order to Review results after tests, you can check general report at /report.html or if you need more detailed information /log.html, example: <ul> <li> <p>Report: </p> </li> <li> <p>Detailed information: </p> </li> </ul> <p>NOTE: If you need more detail at Robot Framework Logs you can set log level option just adding to command --loglevel DEBUG</p>"},{"location":"testing/testplan/","title":"Test Plan Index","text":"<p>List of Common API Services implemented:</p> <ul> <li>Common Operations</li> <li>Api Invoker Management</li> <li>Api Provider Management</li> <li>Api Publish Service</li> <li>Api Discover Service</li> <li>Api Events Service</li> <li>Api Security Service</li> <li>Api Logging Service</li> <li>Api Auditing Service</li> <li>Api Access Control Policy</li> </ul>"},{"location":"testing/testplan/api_access_control_policy/","title":"Test Plan for CAPIF Api Access Control Policy","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_access_control_policy/#test-case-1-retrieve-acl","title":"Test Case 1: Retrieve ACL","text":"<p>Test ID: capif_api_acl-1</p> <p>Description:</p> <p>This test case will check that an API Provider can retrieve ACL from CAPIF</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>contain only one object.</li> <li>apiInvokerId must match apiInvokerId registered previously.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-2-retrieve-acl-with-2-service-apis-published","title":"Test Case 2: Retrieve ACL with 2 Service APIs published","text":"<p>Test ID: capif_api_acl-2</p> <p>Description:</p> <p>This test case will check that an API Provider can retrieve ACL from CAPIF for 2 different serviceApis published.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had two Service API Published on CAPIF</li> <li>API Invoker had a Security Context for both Service APIs published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker for both published APIs</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL for serviceApiId1</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL for serviceApiId2</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId2}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1 and service_2</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information for service_1.</li> <li>Provider Get ACL information for service_2.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>contain one object.</li> <li>apiInvokerId must match apiInvokerId registered previously.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-3-retrieve-acl-with-security-context-created-by-two-different-invokers","title":"Test Case 3: Retrieve ACL with security context created by two different Invokers","text":"<p>Test ID: capif_api_acl-3</p> <p>Description:</p> <p>This test case will check that an API Provider can retrieve ACL from CAPIF containing 2 objects.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>Two API Invokers had a Security Context for same Service API published by provider.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker for both published APIs</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Repeat previous 3 steps in order to have a new Invoker.</p> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1 and service_2</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain two objects.</li> <li>One object must match with apiInvokerId1 and the other one with apiInvokerId2 an registered previously.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-4-retrieve-acl-filtered-by-api-invoker-id","title":"Test Case 4: Retrieve ACL filtered by api-invoker-id","text":"<p>Test ID: capif_api_acl-4</p> <p>Description:</p> <p>This test case will check that an API Provider can retrieve ACL filtering by apiInvokerId from CAPIF containing 1 objects.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>Two API Invokers had a Security Context for same Service API published by provider.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker for both published APIs</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Repeat previous 3 steps in order to have a new Invoker.</p> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}&api-invoker-id={apiInvokerId1}</li> <li>Use serviceApiId, aefId and apiInvokerId1</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}&api-invoker-id={apiInvokerId2}</li> <li>Use serviceApiId, aefId and apiInvokerId2</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1 and service_2</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information with query parameter indicating first api-invoker-id.</li> <li>Provider Get ACL information with query parameter indicating second api-invoker-id.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>ACL Response:</p> <ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain one objects.</li> <li>Object must match with apiInvokerId1.</li> </ol> </li> </ol> </li> <li> <p>ACL Response:</p> <ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain one objects.</li> <li>Object must match with apiInvokerId2.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-5-retrieve-acl-filtered-by-supported-features","title":"Test Case 5: Retrieve ACL filtered by supported-features","text":"<p>Test ID: capif_api_acl-5</p> <p>Description:</p> <p>CURRENTLY NOT SUPPORTED FEATURE</p> <p>This test case will check that an API Provider can retrieve ACL filtering by supportedFeatures from CAPIF containing 1 objects.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>Two API Invokers had a Security Context for same Service API published by provider.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker for both published APIs</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Repeat previous 3 steps in order to have a new Invoker.</p> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}&supported-features={apiInvokerId1}</li> <li>Use serviceApiId, aefId and apiInvokerId1</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}&supported-features={apiInvokerId2}</li> <li>Use serviceApiId, aefId and apiInvokerId2</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1 and service_2</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information with query parameter indicating first supported-features.</li> <li>Provider Get ACL information with query parameter indicating second supported-features.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>ACL Response:</p> <ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain one objects.</li> <li>Object must match with supportedFeatures1.</li> </ol> </li> </ol> </li> <li> <p>ACL Response:</p> <ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain one objects.</li> <li>Object must match with supportedFeatures1.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-6-retrieve-acl-with-aef-id-not-valid","title":"Test Case 6: Retrieve ACL with aef-id not valid","text":"<p>Test ID: capif_api_acl-6</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF if aef-id is not valid</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${AEF_ID_NOT_VALID}</li> <li>Use serviceApiId and AEF_ID_NOT_VALID</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {service_api_id}, aef_id: {aef_id}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-7-retrieve-acl-with-service-id-not-valid","title":"Test Case 7: Retrieve ACL with service-id not valid","text":"<p>Test ID: capif_api_acl-7</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF if service-api-id is not valid</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${NOT_VALID_SERVICE_API_ID}?aef-id=${aef_id}</li> <li>Use NOT_VALID_SERVICE_API_ID and aef_id</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {service_api_id}, aef_id: {aef_id}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-8-retrieve-acl-with-service-api-id-and-aef-id-not-valid","title":"Test Case 8: Retrieve ACL with service-api-id and aef-id not valid","text":"<p>Test ID: capif_api_acl-8</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF if service-api-id and aef-id are not valid</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${NOT_VALID_SERVICE_API_ID}?aef-id=${AEF_ID_NOT_VALID}</li> <li>Use NOT_VALID_SERVICE_API_ID and aef_id</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {NOT_VALID_SERVICE_API_ID}, aef_id: {AEF_ID_NOT_VALID}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-9-retrieve-acl-without-securitycontext-created-previously-by-invoker","title":"Test Case 9: Retrieve ACL without SecurityContext created previously by Invoker","text":"<p>Test ID: capif_api_acl-9</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL if no invoker had requested Security Context to CAPIF</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker created but no Security Context for Service API published had been requested.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li> <p>Discover published APIs</p> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {NOT_VALID_SERVICE_API_ID}, aef_id: {AEF_ID_NOT_VALID}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-10-retrieve-acl-filtered-by-api-invoker-id-not-present","title":"Test Case 10: Retrieve ACL filtered by api-invoker-id not present","text":"<p>Test ID: capif_api_acl-10</p> <p>Description:</p> <p>This test case will check that an API Provider get not found response if filter by not valid api-invoker-id doesn't match any registered ACL.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}&api-invoker-id={NOT_VALID_API_INVOKER_ID}</li> <li>Use serviceApiId, aefId and NOT_VALID_API_INVOKER_ID</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {NOT_VALID_SERVICE_API_ID}, aef_id: {AEF_ID_NOT_VALID}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-11-retrieve-acl-with-apf-certificate","title":"Test Case 11: Retrieve ACL with APF Certificate","text":"<p>Test ID: capif_api_acl-11</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF using APF Certificate</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use APF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-12-retrieve-acl-with-amf-certificate","title":"Test Case 12: Retrieve ACL with AMF Certificate","text":"<p>Test ID: capif_api_acl-12</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF using AMF Certificate</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AMF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-13-retrieve-acl-with-invoker-certificate","title":"Test Case 13: Retrieve ACL with Invoker Certificate","text":"<p>Test ID: capif_api_acl-13</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF using Invoker Certificate</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-14-no-acl-for-invoker-after-be-removed","title":"Test Case 14: No ACL for invoker after be removed","text":"<p>Test ID: capif_api_acl-14</p> <p>Description:</p> <p>This test case will check that ACLs are removed after invoker is removed.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published and ACL is present</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}&api-invoker-id={api-invoker-id}</li> <li>Use serviceApiId, aefId and api-invoker-id</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li>Remove Invoker from CAPIF</li> <li>Provider Retrieve ACL<ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}&api-invoker-id={api-invoker-id}</li> <li>Use serviceApiId, aefId and api-invoker-id</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information of invoker.</li> <li>Remove Invoker from CAPIF.</li> <li>Provider Get ACL information of invoker.</li> </ol> <p>Expected Result: 1. 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.</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {NOT_VALID_SERVICE_API_ID}, aef_id: {AEF_ID_NOT_VALID}, invoker: None and supportedFeatures: None\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/","title":"Test Plan for CAPIF Api Auditing Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_auditing_service/#test-case-1-get-capif-log-entry","title":"Test Case 1: Get CAPIF Log Entry.","text":"<p>Test ID: capif_api_auditing-1</p> <p>Description:</p> <p>This test case will check that a CAPIF AMF can get log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> <li>Log Entry exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Create Log Entry:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs?aef-id={aefId}&api-invoker-id={api-invoker-id}</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry 4. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>200 OK</li> <li>Response Body must follow InvocationLog data structure with:<ul> <li>aefId</li> <li>apiInvokerId</li> <li>logs</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/#test-case-2-get-capif-log-entry-with-no-log-entry-in-capif","title":"Test Case 2: Get CAPIF Log Entry With no Log entry in CAPIF.","text":"<p>Test ID: capif_api_auditing-2</p> <p>Description:</p> <p>This test case will check that a CAPIF AEF can create log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs?aef-id={aefId}&api-invoker-id={api-invoker-id}</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found Log Entry in CAPIF\".</li> <li>cause with message \"Not Exist Logs with the filters applied\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/#test-case-3-get-capif-log-entry-without-aef-id-and-api-invoker-id","title":"Test Case 3: Get CAPIF Log Entry without aef-id and api-invoker-id.","text":"<p>Test ID: capif_api_auditing-3</p> <p>Description:</p> <p>This test case will check that a CAPIF AEF can create log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is no pre-authorised (has no valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> <li>Log Entry exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Create Log Entry:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to *https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry 4. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>400 Bad Request</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 400</li> <li>title with message \"Bad Request\"</li> <li>detail with message \"aef_id and api_invoker_id parameters are mandatory\".</li> <li>cause with message \"Mandatory parameters missing\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/#test-case-4-get-capif-log-entry-with-filtter-api-version","title":"Test Case 4: Get CAPIF Log Entry with filtter api-version.","text":"<p>Test ID: capif_api_auditing-4</p> <p>Description:</p> <p>This test case will check that a CAPIF AMF can get log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> <li>Log Entry exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Create Log Entry:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs?aef-id={aefId}&api-invoker-id={api-invoker-id}&api-version={v1}</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry 4. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>200 OK</li> <li>Response Body must follow InvocationLog data structure with:<ul> <li>aefId</li> <li>apiInvokerId</li> <li>logs</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/#test-case-5-get-capif-log-entry-with-filter-api-version-but-not-exist-in-log-entry","title":"Test Case 5: Get CAPIF Log Entry with filter api-version but not exist in log entry.","text":"<p>Test ID: capif_api_auditing-4</p> <p>Description:</p> <p>This test case will check that a CAPIF AMF can get log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> <li>Log Entry exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Create Log Entry:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs?aef-id={aefId}&api-invoker-id={api-invoker-id}&api-version={v58}</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry 4. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>detail with message \"Parameters do not match any log entry\"</li> <li>cause with message \"No logs found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/","title":"Test Plan for CAPIF Discover Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_discover_service/#test-case-1-discover-published-service-apis-by-authorised-api-invoker","title":"Test Case 1: Discover Published service APIs by Authorised API Invoker","text":"<p>Test ID: capif_api_discover_service-1</p> <p>Description:</p> <p>This test case will check if NetApp (Invoker) can discover published service APIs.</p> <p>Pre-Conditions:</p> <ul> <li>Service APIs are published.</li> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs:<ul> <li>Send GET to https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Use Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains the API Published previously</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-2-discover-published-service-apis-by-non-authorised-api-invoker","title":"Test Case 2: Discover Published service APIs by Non Authorised API Invoker","text":"<p>Test ID: capif_api_discover_service-2</p> <p>Description:</p> <p>This test case will check that an API Publisher can't discover published APIs because is not authorized.</p> <p>Pre-Conditions:</p> <ul> <li>Service APIs are published.</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs by no invoker entity:<ul> <li>Send GET to https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Use not Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by no invoker entity</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Discover Request By no invoker entity:</p> <ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"User not authorized\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-3-discover-published-service-apis-by-not-registered-api-invoker","title":"Test Case 3: Discover Published service APIs by not registered API Invoker","text":"<p>Test ID: capif_api_discover_service-3</p> <p>Description:</p> <p>This test case will check that a not registered invoker is forbidden to discover published APIs.</p> <p>Pre-Conditions:</p> <ul> <li>Service APIs are published.</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs with not valid apiInvoker:<ul> <li>Send GET to https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={INVOKER_NOT_REGISTERED}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Publisher</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Discover Request By Invoker:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"API Invoker does not exist\".</li> <li>cause with message \"API Invoker id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-4-discover-published-service-apis-by-registered-api-invoker-with-1-result-filtered","title":"Test Case 4: Discover Published service APIs by registered API Invoker with 1 result filtered","text":"<p>Test ID: capif_api_discover_service-4</p> <p>Description:</p> <p>This test case will check if NetApp (Invoker) can discover published service APIs.</p> <p>Pre-Conditions:</p> <ul> <li>At least 2 Service APIs are published.</li> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs filtering by api-name:<ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}&api-name=service_1</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> <li>filter by api-name service_1</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 and service_2 at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Discover filtered by api-name service_1 Service APIs by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Publish request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains previously registered Service APIs published.</li> </ul> </li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains only Service API published with api-name service_1</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-5-discover-published-service-apis-by-registered-api-invoker-filtered-with-no-match","title":"Test Case 5: Discover Published service APIs by registered API Invoker filtered with no match","text":"<p>Test ID: capif_api_discover_service-5</p> <p>Description:</p> <p>This test case will check if NetApp (Invoker) can discover published service APIs.</p> <p>Pre-Conditions:</p> <ul> <li>At least 2 Service APIs are published.</li> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs filtering by api-name not published:<ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}&api-name=NOT_VALID_NAME</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> <li>filter by api-name NOT_VALID_NAME</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 and service_2 at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Discover filtered by api-name not published Service APIs by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Publish request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains previously registered Service APIs published.</li> </ul> </li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>404 Not Found response.</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"API Invoker {api_invoker_id} has no API Published that accomplish filter conditions\".</li> <li>cause with message \"No API Published accomplish filter conditions\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-6-discover-published-service-apis-by-registered-api-invoker-not-filtered","title":"Test Case 6: Discover Published service APIs by registered API Invoker not filtered","text":"<p>Test ID: capif_api_discover_service-6</p> <p>Description:</p> <p>This test case will check if NetApp (Invoker) can discover published service APIs.</p> <p>Pre-Conditions:</p> <ul> <li>2 Service APIs are published.</li> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs not filtered:<ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 and service_2 at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Discover without filter by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Discover Request By Invoker:</p> <ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains the 2 previously registered Service APIs published.</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/","title":"Test Plan for CAPIF Api Events Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_events_service/#test-case-1-creates-a-new-individual-capif-event-subscription","title":"Test Case 1: Creates a new individual CAPIF Event Subscription.","text":"<p>Test ID: capif_api_events-1</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) can Subscribe to Events Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Event Subscriptions are stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-2-creates-a-new-individual-capif-event-subscription-with-invalid-subscriberid","title":"Test Case 2: Creates a new individual CAPIF Event Subscription with Invalid SubscriberId","text":"<p>Test ID: capif_api_events-2</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) cannot Subscribe to Events without valid SubcriberId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is not pre-authorised (has invalid InvokerId or apfId)</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_NOT_REGISTERED}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker or APF or AEF or AMF Not found\".</li> <li>cause with message \"Subscriber Not Found\".</li> </ul> </li> </ol> </li> <li> <p>Event Subscriptions are not stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-3-deletes-an-individual-capif-event-subscription","title":"Test Case 3: Deletes an individual CAPIF Event Subscription","text":"<p>Test ID: capif_api_events-3</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) can Delete an Event Subscription</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header</li> <li>Remove Event Subscription</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Remove Event Subscription:</p> <ol> <li>Send DELETE to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Event Subscriptions are stored in CAPIF Database</p> </li> <li> <p>Remove Event Subscription:</p> <ol> <li>204 No Content</li> </ol> </li> <li> <p>Event Subscription is not present at CAPIF Database.</p> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-4-deletes-an-individual-capif-event-subscription-with-invalid-subscriberid","title":"Test Case 4: Deletes an individual CAPIF Event Subscription with invalid SubscriberId","text":"<p>Test ID: capif_api_events-4</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) cannot Delete to Events without valid SubcriberId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId).</li> <li>CAPIF subscriber is subscribed to Events.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> <li>Retrieve Location Header with subscriptionId.</li> <li>Remove Event Subscribed with not valid Subscriber.</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Remove Event Subcription with not valid subscriber:</p> <ol> <li>Send DELETE to https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_ID_NOT_VALID}/subscriptions/{subcriptionId}</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: *{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Event Subscriptions are stored in CAPIF Database</p> </li> <li> <p>Error Response Body must accomplish with ProblemDetails data structure with:</p> <ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker or APF or AEF or AMF Not found\".</li> <li>cause with message \"Subscriber Not Found\".</li> </ul> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-5-deletes-an-individual-capif-event-subscription-with-invalid-subscriptionid","title":"Test Case 5: Deletes an individual CAPIF Event Subscription with invalid SubscriptionId","text":"<p>Test ID: capif_api_events-5</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) cannot Delete an Event Subscription without valid SubscriptionId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has invalid InvokerId or apfId).</li> <li>CAPIF subscriber is subscribed to Events.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> <li>Retrieve Location Header with subscriptionId.</li> <li>Remove Event Subscribed with not valid Subscriber.</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Remove Event Subcription with not valid subscriber:</p> <ol> <li>Send DELETE to to https://{CAPIF_HOSTNAME}/capif-events/v1/{subcriberId}/subscriptions/{SUBSCRIPTION_ID_NOT_VALID}</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Event Subscriptions are stored in CAPIF Database</p> </li> <li>Remove Event Subscription with not valid subscriber:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>detail with message \"Service API not existing\".</li> <li>cause with message \"Event API subscription id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-6-invoker-receives-service-api-invocation-events","title":"Test Case 6: Invoker receives Service API Invocation events","text":"<p>Test ID: capif_api_events-6, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered and published APIs.</li> <li>API Provider had a Service API Published on CAPIF</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register provider and publish one API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover published APIs and extract apiIds and apiNames</li> <li>Subscribe to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE event filtering by aefId.</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header</li> <li>Emulate Success and Failure on API invocation of provider by Invoker, using Invocation Logs API.</li> </ol> <p>Information of Test:</p> <ol> <li>Perform provider registration</li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send POST to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform invoker onboarding</p> </li> <li> <p>Discover published APIs:</p> <ul> <li>Get Api Ids And Api Names from response.</li> </ul> </li> <li> <p>Event Subscription to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE of provider previously registered:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['SERVICE_API_INVOCATION_SUCCESS','SERVICE_API_INVOCATION_FAILURE']</li> <li>eventFilter: only receive events from provider's aefId.</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Create Log Entry emulating provider receive Success and Failure api invocation from invoker:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body with:<ol> <li>aefId from provider published.</li> <li>apiInvokerId from invoker onboarded.</li> <li>apiId of published API</li> <li>apiName of published API</li> <li>200 and 400 results in two logs.</li> </ol> </li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Response to creation of log entry on CCF must accomplish:<ol> <li>201 Created</li> <li>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}</li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>Two Events have been received.</li> <li>Validate received events follow EventNotification data structure, with invocationLog in eventDetail parameter.<ol> <li>One should be SERVICE_API_INVOCATION_SUCCESS related with 200 result at Log.</li> <li>The other one must be SERVICE_API_INVOCATION_FAILURE related with 400 result at Log.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-7-invoker-subscribe-to-service-api-available-and-unavailable-events","title":"Test Case 7: Invoker subscribe to Service API Available and Unavailable events","text":"<p>Test ID: capif_api_events-7, mockserver</p> <p>Description:</p> <p>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. </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered and published APIs.</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register provider and publish one API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover published APIs and extract apiIds and apiNames</li> <li>Subscribe to SERVICE_API_AVAILABLE and SERVICE_API_UNAVAILABLE event filtering by aefId.</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header</li> <li>Provider publish new API.</li> <li>Provider remove published API.</li> </ol> <p>Information of Test:</p> <ol> <li>Perform provider registration</li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send POST to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform invoker onboarding</p> </li> <li> <p>Discover published APIs:</p> <ul> <li>Get Api Ids And Api Names from response.</li> </ul> </li> <li> <p>Event Subscription to SERVICE_API_AVAILABLE and SERVICE_API_UNAVAILABLE of provider previously registered:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['SERVICE_API_AVAILABLE','SERVICE_API_UNAVAILABLE']</li> <li>eventFilter: only receive events from provider's aefId.</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Publish new Service API at CCF:</p> <ul> <li>Send POST to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_2</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Remove published Service API at CCF:</p> <ul> <li>Send DELETE to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Mock Server received messages must accomplish:</p> <ol> <li>Two Events have been received.</li> <li>Validate received events follow EventNotification data structure, with apiIds in eventDetail parameter.<ol> <li>One should be SERVICE_API_AVAILABLE apiId of service_2 published API.</li> <li>The other one must be SERVICE_API_UNAVAILABLE apiId of service_1 published API.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-8-invoker-subscribe-to-service-api-update","title":"Test Case 8: Invoker subscribe to Service API Update","text":"<p>Test ID: capif_api_events-8, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered and published APIs.</li> <li>API Provider had a Service API Published on CAPIF</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider and publish one API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover published APIs and extract apiIds and apiNames</li> <li>Subscribe to SERVICE_API_UPDATE event filtering by aefId.</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header at event subscription</li> <li>Provider update information of Service API Published.</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send POST to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> <li>Store serviceApiId</li> </ul> </li> <li> <p>Perform invoker onboarding</p> </li> <li> <p>Discover published APIs:</p> <ul> <li>Get Api Ids And Api Names from response.</li> </ul> </li> <li> <p>Event Subscription to SERVICE_API_UPDATE of provider previously registered:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['SERVICE_API_UPDATE']</li> <li>eventFilter: only receive events from provider's aefId.</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Update published API at CCF:</p> <ul> <li>Send PUT to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>body [service api description] with overrided apiName to service_1_modified</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Response to Update Published Service API:<ol> <li>200 OK</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiName service_1_modified</li> </ul> </li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>One Event has been received.</li> <li>Validate received events follow EventNotification data structure, with serviceAPIDescriptions in eventDetail parameter.<ol> <li>Event should be SERVICE_API_UPDATE with eventDetail with modified apiName.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-9-provider-subscribe-to-api-invoker-events","title":"Test Case 9: Provider subscribe to API Invoker events","text":"<p>Test ID: capif_api_events-9, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered.</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Subscribe Provider to API_INVOKER_ONBOARDED, API_INVOKER_UPDATED and API_INVOKER_OFFBOARDED events.</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Update Onboarding Information at CCF with a minor change on \"notificationDestination\"</li> <li>Offboard Invoker</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li>Event Subscription to API_INVOKER_ONBOARDED, API_INVOKER_UPDATED and API_INVOKER_OFFBOARDED events:<ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['API_INVOKER_ONBOARDED', 'API_INVOKER_UPDATED', 'API_INVOKER_OFFBOARDED']</li> </ol> </li> <li>Use Provider AMF Certificate</li> </ol> </li> <li>Perform invoker onboarding</li> <li>Update information of previously onboarded Invoker:<ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> <li>\"notificationDestination\": \"http://host.docker.internal:8086/netapp_new_callback\",</li> </ul> </li> <li>Offboard:<ul> <li>Send DELETE to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Update Request (PUT) with minor change must contain:<ol> <li>200 OK response.</li> <li>notificationDestination on response must contain the new value</li> </ol> </li> <li>Response to Offboard Request (DELETE) must contain:<ol> <li>204 No Content</li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>Three Events have been received.</li> <li>Validate received events follow EventNotification data structure, with apiInvokerIds in eventDetail parameter.<ol> <li>One Event should be API_INVOKER_ONBOARDED with eventDetail with modified apiInvokerId.</li> <li>One Event should be API_INVOKER_UPDATED with eventDetail with modified apiInvokerId.</li> <li>One Event should be API_INVOKER_OFFBOARDED with eventDetail with modified apiInvokerId.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-10-provider-subscribed-to-acl-update-event","title":"Test Case 10: Provider subscribed to ACL update event","text":"<p>Test ID: capif_api_events-10, mockserver</p> <p>Description:</p> <p>This test case will check that a CAPIF Provider subscribed to ACCESS_CONTROL_POLICY_UPDATE receive a notification when ACL Changes.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered.</li> <li>API Provider had one Service API Published on CAPIF</li> <li>API Invoker had a Security Context for the Service API published by provider.</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF.</li> <li>Publish a provider API with name service_1.</li> <li>Register Invoker and Onboard Invoker at CCF.</li> <li>Subscribe Provider to ACCESS_CONTROL_POLICY_UPDATE event.</li> <li>Discover APIs filtered by aef_id</li> <li>Create Security Context for Invoker.</li> <li>Provider Retrieve ACL</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li>Perform invoker onboarding</li> <li>Event Subscription to ACCESS_CONTROL_POLICY_UPDATE event:<ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['ACCESS_CONTROL_POLICY_UPDATE']</li> <li>eventFilters: apiInvokerIds array with apiInvokerId of invoker</li> </ol> </li> <li>Use Provider AMF Certificate</li> </ol> </li> <li>Discover published APIs</li> <li>Create Security Context for Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li>Provider Retrieve ACL<ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Create security context:<ol> <li>201 Created response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}</li> </ol> </li> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>contain only one object.</li> <li>apiInvokerId must match apiInvokerId registered previously.</li> </ol> </li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>One Event has been received.</li> <li>Validate received event follow EventNotification data structure, with accCtrlPolListExt in eventDetail parameter.<ol> <li>One Event should be ACCESS_CONTROL_POLICY_UPDATE with eventDetail with accCtrlPolListExt including the apiId and apiInvokerPolicies.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-11-provider-receives-an-acl-unavailable-event-when-invoker-remove-security-context","title":"Test Case 11: Provider receives an ACL unavailable event when invoker remove Security Context.","text":"<p>Test ID: capif_api_events-11, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered.</li> <li>API Provider had one Service API Published on CAPIF</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF.</li> <li>Publish a provider API with name service_1.</li> <li>Register Invoker and Onboard Invoker at CCF.</li> <li>Subscribe Invoker to ACCESS_CONTROL_POLICY_UNAVAILABLE event.</li> <li>Discover APIs filtered by aef_id</li> <li>Create Security Context for Invoker.</li> <li>Provider Retrieve ACL.</li> <li>Remove Security Context for Invoker.</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li>Perform invoker onboarding</li> <li>Event Subscription to ACCESS_CONTROL_POLICY_UNAVAILABLE event:<ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['ACCESS_CONTROL_POLICY_UNAVAILABLE']</li> <li>eventFilters: apiInvokerIds array with apiInvokerId of invoker</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li>Discover published APIs</li> <li>Create Security Context for Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li>Provider Retrieve ACL<ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li>Delete Security Context of Invoker by Provider:<ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Use AEF certificate</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Create security context:<ol> <li>201 Created response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}</li> </ol> </li> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>contain only one object.</li> <li>apiInvokerId must match apiInvokerId registered previously.</li> </ol> </li> </ol> </li> <li>Delete security context:<ol> <li>204 No Content response.</li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>One Event has been received.</li> <li>Validate received event follow EventNotification data structure, without eventDetail parameter.<ol> <li>One Event should be ACCESS_CONTROL_POLICY_UNAVAILABLE without eventDetail.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-12-invoker-receives-an-invoker-authorization-revoked-and-acl-unavailable-event-when-provider-revoke-invoker-authorization","title":"Test Case 12: Invoker receives an Invoker Authorization Revoked and ACL unavailable event when Provider revoke Invoker Authorization.","text":"<p>Test ID: capif_api_events-12, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered.</li> <li>API Provider had one Service API Published on CAPIF</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF.</li> <li>Publish a provider API with name service_1.</li> <li>Register Invoker and Onboard Invoker at CCF.</li> <li>Subscribe Invoker to ACCESS_CONTROL_POLICY_UNAVAILABLE and API_INVOKER_AUTHORIZATION_REVOKED events.</li> <li>Discover APIs filtered by aef_id</li> <li>Create Security Context for Invoker.</li> <li>Revoke Authorization by Provider.</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li>Perform invoker onboarding</li> <li>Event Subscription to ACCESS_CONTROL_POLICY_UNAVAILABLE and API_INVOKER_AUTHORIZATION_REVOKED event:<ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['ACCESS_CONTROL_POLICY_UNAVAILABLE','API_INVOKER_AUTHORIZATION_REVOKED']</li> <li>eventFilters: apiInvokerIds array with apiInvokerId of invoker</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li>Discover published APIs</li> <li>Create Security Context for Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li>Revoke Authorization by Provider:<ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/delete</li> <li>body security notification body</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Create security context:<ol> <li>201 Created response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}</li> </ol> </li> <li>Revoke Authorization:<ol> <li>204 No Content response.</li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>Two Events has been received.</li> <li>Validate received event follow EventNotification data structure, without eventDetail parameter.<ol> <li>One Event should be ACCESS_CONTROL_POLICY_UNAVAILABLE without eventDetail.</li> <li>One Event should be API_INVOKER_AUTHORIZATION_REVOKED without eventDetail.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/","title":"Test Plan for CAPIF Api Invoker Management","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_invoker_management/#test-case-1-onboard-netapp","title":"Test Case 1: Onboard NetApp","text":"<p>Test ID: capif_api_invoker_management-1</p> <p>Description:</p> <p>This test will try to register new NetApp at CAPIF Core.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was not registered previously</li> <li>NetApp was not onboarded previously</li> <li>Preconditions: The administrator must have previously registered the User.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at invoker</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Onboard Invoker:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers</li> <li>Reference Request Body: invoker onboarding body</li> <li>\"onboardingInformation\"->\"apiInvokerPublicKey\": must contain public key generated by Invoker.</li> <li>Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token})</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Retrieve access_token by User from register</li> <li>Onboard Invoker at CCF</li> <li>Store signed Certificate</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-2-onboard-netapp-already-onboarded","title":"Test Case 2: Onboard NetApp Already onboarded","text":"<p>Test ID: capif_api_invoker_management-2</p> <p>Description:</p> <p>This test will check second onboard of same NetApp is not allowed.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Repeat Onboard Invoker:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers</li> <li>Reference Request Body: invoker onboarding body</li> <li>\"onboardingInformation\"->\"apiInvokerPublicKey\": must contain public key generated by Invoker.</li> <li>Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token})</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register NetApp at CCF</li> <li>Onboard NetApp at CCF</li> <li>Store signed Certificate at NetApp</li> <li>Onboard Again the NetApp at CCF</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Second Onboard of NetApp must accomplish:<ol> <li>403 Forbidden</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 403</li> <li>title with message \"Forbidden\"</li> <li>detail with message \"Invoker Already registered\".</li> <li>cause with message \"Identical invoker public key\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-3-update-onboarded-netapp","title":"Test Case 3: Update Onboarded NetApp","text":"<p>Test ID: capif_api_invoker_management-3</p> <p>Description:</p> <p>This test will try to update information of previous onboard NetApp at CAPIF Core.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Update information of previously onboarded Invoker:</p> <ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> <li>\"notificationDestination\": \"http://host.docker.internal:8086/netapp_new_callback\",</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Update Onboarding Information at CCF with a minor change on \"notificationDestination\"</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Update Request (PUT) with minor change must contain:<ol> <li>200 OK response.</li> <li>notificationDestination on response must contain the new value</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-4-update-not-onboarded-netapp","title":"Test Case 4: Update Not Onboarded NetApp","text":"<p>Test ID: capif_api_invoker_management-4</p> <p>Description:</p> <p>This test will try to update information of not onboarded NetApp at CAPIF Core.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was not onboarded previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Update information of not onboarded Invoker:</p> <ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{INVOKER_NOT_REGISTERED}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Onboard Invoker at CCF</li> <li>Update Onboarding Information at CCF of not onboarded</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> </ol> </li> <li>Response to Update Request (PUT) must contain:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Please provide an existing Netapp ID\".</li> <li>cause with message \"Not exist NetappID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-5-offboard-netapp","title":"Test Case 5: Offboard NetApp","text":"<p>Test ID: capif_api_invoker_management-5</p> <p>Description:</p> <p>This test case will check that a Registered NetApp can be deleted.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Offboard:</p> <ul> <li>Send Delete to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Onboard Invoker at CCF</li> <li>Offboard Invoker at CCF</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> </ol> </li> <li>Response to Offboard Request (DELETE) must contain:<ol> <li>204 No Content</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-6-offboard-not-previsouly-onboarded-netapp","title":"Test Case 6: Offboard Not previsouly Onboarded NetApp","text":"<p>Test ID: capif_api_invoker_management-6</p> <p>Description:</p> <p>This test case will check that a Non-Registered NetApp cannot be deleted</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was not onboarded previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Offboard:</p> <ul> <li>Send Delete to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{INVOKER_NOT_REGISTERED}</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Offboard Invoker at CCF</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Offboard Request (DELETE) must contain:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Please provide an existing Netapp ID\".</li> <li>cause with message \"Not exist NetappID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-7-update-onboarded-netapp-certificate","title":"Test Case 7: Update Onboarded NetApp Certificate","text":"<p>Test ID: capif_api_invoker_management-7</p> <p>Description:</p> <p>This test will try to update public key and get a new signed certificate by CAPIF Core.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId} and {public_key_1}</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding with public_key_1.</p> </li> <li> <p>Create {public_key_2}</p> </li> <li> <p>Update information of previously onboarded Invoker:</p> <ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> <li>[\"onboardingInformation\"][\"apiInvokerPublicKey\"]: {public_key_2},</li> <li>Store new certificate.</li> </ul> </li> <li> <p>Update information of previously onboarded Invoker Using new certificate:</p> <ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> <li>\"notificationDestination\": \"http://host.docker.internal:8086/netapp_new_callback\",</li> <li>Use new invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Update Onboarding Information at CCF with new public key</li> <li>Update Onboarding Information at CCF with minor change</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Update Request (PUT) with new public key:<ol> <li>200 OK response.</li> <li>apiInvokerCertificate with new certificate on response -> store to use.</li> </ol> </li> <li>Response to Update Request (PUT) with minor change must contain:<ol> <li>200 OK response.</li> <li>notificationDestination on response must contain the new value</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_logging_service/","title":"Test Plan for CAPIF Api Logging Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_logging_service/#test-case-1-creates-a-new-individual-capif-log-entry","title":"Test Case 1: Creates a new individual CAPIF Log Entry.","text":"<p>Test ID: capif_api_logging-1</p> <p>Description:</p> <p>This test case will check that a CAPIF AEF can create log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid aefId from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow InvocationLog data structure with:<ul> <li>aefId</li> <li>apiInvokerId</li> <li>logs</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invocation-logs/v1/{aefId}/logs/{logId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_logging_service/#test-case-2-creates-a-new-individual-capif-log-entry-with-invalid-aefid","title":"Test Case 2: Creates a new individual CAPIF Log Entry with Invalid aefId","text":"<p>Test ID: capif_api_logging-2</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (AEF) cannot create Log Entry without valid aefId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is not pre-authorised (has not valid aefId from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{not-valid-aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Exposer not exist\".</li> <li>cause with message \"Exposer id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_logging_service/#test-case-3-creates-a-new-individual-capif-log-entry-with-invalid-serviceapi","title":"Test Case 3: Creates a new individual CAPIF Log Entry with Invalid serviceAPI","text":"<p>Test ID: capif_api_logging-3</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (AEF) cannot create Log Entry without valid aefId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid aefId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body [log entry request body with serviceAPI apiName apiId not valid]</li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not exist\".</li> <li>cause with message \"Invoker id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_logging_service/#test-case-4-creates-a-new-individual-capif-log-entry-with-invalid-apiinvokerid","title":"Test Case 4: Creates a new individual CAPIF Log Entry with Invalid apiInvokerId","text":"<p>Test ID: capif_api_logging-4</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (AEF) cannot create Log Entry without valid aefId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid aefId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body [log entry request body with invokerId not valid]</li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure.</li> <li>For each apiProvFuncs, we must check:<ol> <li>apiProvFuncId is set</li> <li>apiProvCert under regInfo is set properly</li> </ol> </li> <li>Location Header must contain the new resource URL {apiRoot}/api-provider-management/v1/registrations/{registrationId}</li> </ol> </li> <li> <p>Response to Logging Service must accomplish:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not exist\".</li> <li>cause with message \"Invoker id not found\".</li> </ul> </li> </ol> </li> <li> <p>Log Entry are not stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_logging_service/#test-case-5-creates-a-new-individual-capif-log-entry-with-invalid-aefid-in-body","title":"Test Case 5: Creates a new individual CAPIF Log Entry with Invalid aefId in body","text":"<p>Test ID: capif_api_logging-5</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (AEF) cannot create Log Entry without valid aefId in body</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body [log entry request body with bad aefId] </li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"AEF id not matching in request and body\".</li> <li>cause with message \"Not identical AEF id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/","title":"Test Plan for CAPIF Api Provider Management","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_provider_management/#test-case-1-register-api-provider","title":"Test Case 1: Register Api Provider","text":"<p>Test ID: capif_api_provider_management-1</p> <p>Description:</p> <p>This test case will check that Api Provider can be registered con CCF</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid certificate from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Create private and public key for provider and each function to register.</li> <li>Register Provider.</li> </ol> <p>Expected Result:</p> <ol> <li>Register Provider at Provider Management:<ol> <li>201 Created response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure.</li> <li>For each apiProvFuncs, we must check:<ol> <li>apiProvFuncId is set</li> <li>apiProvCert under regInfo is set properly</li> </ol> </li> <li>Location Header must contain the new resource URL {apiRoot}/api-provider-management/v1/registrations/{registrationId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-2-register-api-provider-already-registered","title":"Test Case 2: Register Api Provider Already registered","text":"<p>Test ID: capif_api_provider_management-2</p> <p>Description:</p> <p>This test case will check that a Api Provider previously registered cannot be re-registered</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was registered previously and there is a {registerId} for his Api Provider in the DB</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Re-Register Provider:</p> <ul> <li>Same regSec than Previous registration</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Create private and public key for provider and each function to register.</li> <li>Register Provider.</li> <li>Re-Register Provider.</li> </ol> <p>Expected Result:</p> <ol> <li>Re-Register Provider:<ol> <li>403 Forbidden response.</li> <li> <p>body returned must accomplish ProblemDetails data structure, with:</p> <ul> <li>status 403</li> <li>title with message \"Forbidden\"</li> <li>detail with message \"Provider already registered\".</li> <li>cause with message \"Identical provider reg sec\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-3-update-registered-api-provider","title":"Test Case 3: Update Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-3</p> <p>Description:</p> <p>This test case will check that a Registered Api Provider can be updated</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was registered previously and there is a {registerId} for his Api Provider in the DB</li> </ul> <p>Information of Test:</p> <ol> <li>Create public and private key at provider for provider itself and each function (apf, aef and amf)</li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Get Resource URL from Location</li> </ul> </li> <li> <p>Update Provider:</p> <ul> <li>Send PUT to Resource URL returned at registration https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{registrationId}</li> <li>body provider request body with apiProvDomInfo set to ROBOT_TESTING_MOD</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Create private and public key for provider and each function to register.</li> <li>Register Provider</li> <li>Update Provider</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Register Provider:</p> <ol> <li>201 Created response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/api-provider-management/v1/registrations/{registrationId}</li> </ol> </li> <li> <p>Update Provider:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure, with:<ul> <li>apiProvDomInfo set to ROBOT_TESTING_MOD</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-4-update-not-registered-api-provider","title":"Test Case 4: Update Not Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-4</p> <p>Description:</p> <p>This test case will check that a Non-Registered Api Provider cannot be updated</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was not registered previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Update Not Registered Provider:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{API_PROVIDER_NOT_REGISTERED}</li> <li>body provider request body</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Update Not Registered Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Update Not Registered Provider:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Not Exist Provider Enrolment Details\".</li> <li>cause with message \"Not found registrations to send this api provider details\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-5-partially-update-registered-api-provider","title":"Test Case 5: Partially Update Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-5</p> <p>Description:</p> <p>This test case will check that a Registered Api Provider can be partially updated</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was registered previously and there is a {registerId} for his Api Provider in the DB</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Partial update provider:</p> <ul> <li>Send PATCH https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{registrationId}</li> <li>body provider request patch body</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Register Provider</li> <li>Partial update provider</li> </ol> <p>Expected Result:</p> <ol> <li>Partial update provider at Provider Management:<ol> <li>200 OK response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure, with:<ul> <li>apiProvDomInfo with \"ROBOT_TESTING_MOD\"</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-6-partially-update-not-registered-api-provider","title":"Test Case 6: Partially Update Not Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-6</p> <p>Description:</p> <p>This test case will check that a Non-Registered Api Provider cannot be partially updated </p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was not registered previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Partial update Provider:</p> <ul> <li>Send PATCH https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{API_API_PROVIDER_NOT_REGISTERED}</li> <li>body provider request patch body</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Register Provider</li> <li>Partial update provider</li> </ol> <p>Expected Result:</p> <ol> <li>Partial update provider:<ol> <li>404 Not Found response.</li> <li> <p>body returned must accomplish ProblemDetails data structure, with:</p> <ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Not Exist Provider Enrolment Details\".</li> <li>cause with message \"Not found registrations to send this api provider details\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-7-delete-registered-api-provider","title":"Test Case 7: Delete Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-7</p> <p>Description:</p> <p>This test case will check that a Registered Api Provider can be deleted</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was registered previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Delete registered provider:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{registrationId}</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Register Provider</li> <li>Delete Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Delete Provider:<ol> <li>204 No Content response.</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-8-delete-not-registered-api-provider","title":"Test Case 8: Delete Not Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-8</p> <p>Description:</p> <p>This test case will check that a Non-Registered Api Provider cannot be deleted</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was not registered previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Delete registered provider at Provider Management:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{API_PROVIDER_NOT_REGISTERED}</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Delete Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Delete Provider:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Not Exist Provider Enrolment Details\".</li> <li>cause with message \"Not found registrations to send this api provider details\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/","title":"Test Plan for CAPIF Api Publish Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_publish_service/#test-case-1-publish-api-by-authorised-api-publisher","title":"Test Case 1: Publish API by Authorised API Publisher","text":"<p>Test ID: capif_api_publish_service-1</p> <p>Description:</p> <p>This test case will check that an API Publisher can Publish an API</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> </li> <li> <p>Send Post to ccf_publish_url: https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</p> </li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ol> <p>Execution Steps:</p> <ol> <li> <p>Register Provider at CCF and store certificates.</p> </li> <li> <p>Publish Service API</p> </li> <li> <p>Retrieve {apiId} from body and Location header with new resource created from response</p> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Published Service API is stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-2-publish-api-by-non-authorised-api-publisher","title":"Test Case 2: Publish API by NON Authorised API Publisher","text":"<p>Test ID: capif_api_publish_service-2</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Publish an API withot valid apfId </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is NOT pre-authorised (has invalid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API with invalid APF ID at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{APF_ID_NOT_VALID}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API with invalid APF ID</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Publisher not existing\".</li> <li>cause with message \"Publisher id not found\".</li> </ul> </li> </ol> </li> <li> <p>Service API is NOT stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-3-retrieve-all-apis-published-by-authorised-apfid","title":"Test Case 3: Retrieve all APIs Published by Authorised apfId","text":"<p>Test ID: capif_api_publish_service-3</p> <p>Description:</p> <p>This test case will check that an API Publisher can Retrieve all API published</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>At least 2 service APIs are published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Publish Other Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve all published APIs:</p> <ul> <li>Send Get to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API service_1</li> <li>Retrieve {apiId1} from body and Location header with new resource created from response</li> <li>Publish Service API service_2</li> <li>Retrieve {apiId2} from body and Location header with new resource created from response</li> <li>Retrieve All published APIs and check if both are present.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to service 1 Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId1}</li> </ol> </li> <li> <p>Response to service 2 Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId2}</li> </ol> </li> <li> <p>Published Service APIs are stored in CAPIF Database</p> </li> <li> <p>Response to Retrieve all published APIs:</p> <ol> <li>200 OK</li> <li>Response body must return an array of ServiceAPIDescription data.</li> <li>Array must contain all previously published APIs.</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-4-retrieve-all-apis-published-by-non-authorised-apfid","title":"Test Case 4: Retrieve all APIs Published by NON Authorised apfId","text":"<p>Test ID: capif_api_publish_service-4</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Retrieve API published when apfId is not authorised </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is NOT pre-authorised (has invalid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Retrieve all published APIs:</p> <ul> <li>Send Get to https://{CAPIF_HOSTNAME}/published-apis/v1/{APF_ID_NOT_VALID}/service-apis</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Retrieve All published APIs</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>401 Non Authorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Provider not existing\".</li> <li>cause with message \"Provider id not found\".</li> </ul> </li> </ol> </li> <li> <p>Service API is NOT stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-5-retrieve-single-apis-published-by-authorised-apfid","title":"Test Case 5: Retrieve single APIs Published by Authorised apfId","text":"<p>Test ID: capif_api_publish_service-5</p> <p>Description:</p> <p>This test case will check that an API Publisher can Retrieve API published one by one</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>At least 2 service APIs are published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Publish Other Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve service_1 published APIs detail:</p> <ul> <li>Send Get to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{apiId1}</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve service_2 published APIs detail:</p> <ul> <li>Send Get to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{apiId2}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API service_1.</li> <li>Retrieve {apiId1} from body and Location header with new resource created from response.</li> <li>Publish Service API service_2.</li> <li>Retrieve {apiId2} from body and Location header with new resource created from response.</li> <li>Retrieve service_1 API Detail.</li> <li>Retrieve service_2 API Detail.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to service 1 Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId1}</li> </ol> </li> <li> <p>Response to service 2 Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId2}</li> </ol> </li> <li> <p>Published Service APIs are stored in CAPIF Database</p> </li> <li> <p>Response to Retrieve service_1 published API using apiId1:</p> <ol> <li>200 OK</li> <li>Response body must return a ServiceAPIDescription data.</li> <li>Array must contain same information than service_1 published registration response.</li> </ol> </li> <li> <p>Response to Retrieve service_2 published API using apiId2:</p> <ol> <li>200 OK</li> <li>Response body must return a ServiceAPIDescription data.</li> <li>Array must contain same information than service_2 published registration response.</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-6-retrieve-single-apis-non-published-by-authorised-apfid","title":"Test Case 6: Retrieve single APIs non Published by Authorised apfId","text":"<p>Test ID: capif_api_publish_service-6</p> <p>Description:</p> <p>This test case will check that an API Publisher try to get detail of not published api.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>No published api</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration</li> <li>Retrieve not published APIs detail:<ul> <li>Send Get to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID_NOT_VALID}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Retrieve not published API Detail.</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Retrieve for NOT published API must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Service API not found\".</li> <li>cause with message \"No Service with specific credentials exists\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-7-retrieve-single-apis-published-by-non-authorised-apfid","title":"Test Case 7: Retrieve single APIs Published by NON Authorised apfId","text":"<p>Test ID: capif_api_publish_service-7</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Retrieve detailed API published when apfId is not authorised </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is NOT pre-authorised (has invalid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve detailed published APIs:</p> <ul> <li>Send Get to https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/${apiId}</li> <li>Use Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API at CCF</li> <li>Retrieve {apiId} from body and Location header with new resource created from response.</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Invoker Certificate</li> <li>Retrieve detailed published API acting as Invoker</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Retrieve Detailed published API acting as Invoker must accomplish:</p> <ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"User not authorized\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> <li> <p>Service API is NOT stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-8-update-api-published-by-authorised-apfid-with-valid-serviceapiid","title":"Test Case 8: Update API Published by Authorised apfId with valid serviceApiId","text":"<p>Test ID: capif_api_publish_service-8</p> <p>Description:</p> <p>This test case will check that an API Publisher can Update published API with a valid serviceApiId </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>A service APIs is published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>get resource url from location Header.</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Update published API at CCF:</p> <ul> <li>Send PUT to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>body service api description with overrided apiName to service_1_modified</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve detail of service API:</p> <ul> <li>Send Get to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>check apiName is service_1_modified</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API</li> <li>Retrieve {apiId} from body and Location header with new resource url created from response</li> <li>Update published Service API.</li> <li>Retrieve detail of Service API</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Update Published Service API:</p> <ol> <li>200 OK</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiName service_1_modified</li> </ul> </li> </ol> </li> <li> <p>Response to Retrieve detail of Service API:</p> <ol> <li>200 OK</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiName service_1_modified.</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-9-update-apis-published-by-authorised-apfid-with-invalid-serviceapiid","title":"Test Case 9: Update APIs Published by Authorised apfId with invalid serviceApiId","text":"<p>Test ID: capif_api_publish_service-9</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Update published API with a invalid serviceApiId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Update published API at CCF:</p> <ul> <li>Send PUT to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID_NOT_VALID}</li> <li>body service api description with overrided apiName to service_1_modified</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Update published Service API.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Update Published Service API:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Service API not found\".</li> <li>cause with message \"Service API id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-10-update-apis-published-by-non-authorised-apfid","title":"Test Case 10: Update APIs Published by NON Authorised apfId","text":"<p>Test ID: capif_api_publish_service-10</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Update API published when apfId is not authorised</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is NOT pre-authorised (has invalid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Update published API at CCF:</p> <ul> <li>Send PUT to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> <li>body service api description with overrided apiName to service_1_modified</li> <li>Use invoker certificate</li> </ul> </li> <li> <p>Retrieve detail of service API:</p> <ul> <li>Send Get to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>check apiName is service_1</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API at CCF</li> <li>Retrieve {apiId} from body and Location header with new resource created from response.</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Invoker Certificate</li> <li>Update published API at CCF as Invoker</li> <li>Retrieve detail of Service API as publisher</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Update published API acting as Invoker must accomplish:</p> <ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"User not authorized\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> <li> <p>Response to Retrieve Detail of Service API:</p> <ol> <li>200 OK</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiName service_1.</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-11-delete-api-published-by-authorised-apfid-with-valid-serviceapiid","title":"Test Case 11: Delete API Published by Authorised apfId with valid serviceApiId","text":"<p>Test ID: capif_api_publish_service-11</p> <p>Description:</p> <p>This test case will check that an API Publisher can Delete published API with a valid serviceApiId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority).</li> <li>A service APIs is published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Remove published Service API at CCF:</p> <ul> <li>Send DELETE to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> <li>Use APF Certificate</li> </ul> </li> <li>Retrieve detail of service API:<ul> <li>Send Get to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API</li> <li>Retrieve {apiId} from body and Location header with new resource created from response</li> <li>Remove published API at CCF</li> <li>Try to retreive deleted service API from CCF</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Published Service API is stored in CAPIF Database</p> </li> <li> <p>Response to Remove published Service API at CCF:</p> <ol> <li>204 No Content</li> </ol> </li> <li> <p>Response to Retrieve for DELETED published API must accomplish:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Service API not found\".</li> <li>cause with message \"No Service with specific credentials exists\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-12-delete-apis-published-by-authorised-apfid-with-invalid-serviceapiid","title":"Test Case 12: Delete APIs Published by Authorised apfId with invalid serviceApiId","text":"<p>Test ID: capif_api_publish_service-12</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Delete with invalid serviceApiId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority).</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Remove published Service API at CCF with invalid serviceId:</p> <ul> <li>Send DELETE to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID_NOT_VALID}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Remove published API at CCF with invalid serviceId</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Remove published Service API at CCF:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Service API not found\".</li> <li>cause with message \"Service API id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-13-delete-apis-published-by-non-authorised-apfid","title":"Test Case 13: Delete APIs Published by NON Authorised apfId","text":"<p>Test ID: capif_api_publish_service-12</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Delete API published when apfId is not authorised</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority).</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Remove published Service API at CCF with invalid serviceId as Invoker:</p> <ul> <li>Send DELETE to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID_NOT_VALID}</li> <li>Use invoker certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Register Invoker and onboard Invoker at CCF</li> <li>Remove published API at CCF with invalid serviceId as Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Remove published Service API at CCF:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"User not authorized\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/","title":"Test Plan for CAPIF Api Security Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_security_service/#test-case-1-create-a-security-context-for-an-api-invoker","title":"Test Case 1: Create a security context for an API invoker","text":"<p>Test ID: capif_security_api-1</p> <p>Description:</p> <p>This test case will check that an API Invoker can create a Security context</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Invoker Onboarding</li> <li>Create Security Context for this Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> </ol> <p>Expected Result:</p> <ol> <li>Create security context:<ol> <li>201 Created response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-2-create-a-security-context-for-an-api-invoker-with-provider-role","title":"Test Case 2: Create a security context for an API invoker with Provider role","text":"<p>Test ID:: capif_security_api-2</p> <p>Description:</p> <p>This test case will check that an Provider cannot create a Security context with valid apiInvokerId.</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID), but user that create Security Context with Provider role</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker but using Provider certificate.</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Create security context using Provider certificate:</p> <ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be invoker\".</li> </ul> </li> </ol> </li> <li> <p>No context stored at DB</p> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-3-create-a-security-context-for-an-api-invoker-with-provider-entity-role-and-invalid-apiinvokerid","title":"Test Case 3: Create a security context for an API invoker with Provider entity role and invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-3</p> <p>Description:</p> <p>This test case will check that an Provider cannot create a Security context with invalid apiInvokerID.</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID), but user that create Security Context with Provider role</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Create Security Context for this not valid apiInvokerId and using Provider certificate.</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>body service security body</li> <li>Using AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> </ol> <p>Expected Result:</p> <ol> <li>Create security context using Provider certificate:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be invoker\".</li> </ul> </li> </ol> </li> <li>No context stored at DB</li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-4-create-a-security-context-for-an-api-invoker-with-invoker-entity-role-and-invalid-apiinvokerid","title":"Test Case 4: Create a security context for an API invoker with Invoker entity role and invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-4</p> <p>Description:</p> <p>This test case will check that an Invoker cannot create a Security context with valid apiInvokerId.</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID), but user that create Security Context with invalid apiInvokerId</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Create Security Context using Provider certificate</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Create security context using Provider certificate:</p> <ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> <li> <p>No context stored at DB</p> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-5-retrieve-the-security-context-of-an-api-invoker","title":"Test Case 5: Retrieve the Security Context of an API Invoker","text":"<p>Test ID:: capif_security_api-5</p> <p>Description:</p> <p>This test case will check that an provider can retrieve the Security context of an API Invoker</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority) and API Invoker has created a valid Security Context</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker.</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker certificate</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using AEF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> <li>Retrieve Security Context by Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Retrieve security context:<ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-6-retrieve-the-security-context-of-an-api-invoker-with-invalid-apiinvokerid","title":"Test Case 6: Retrieve the Security Context of an API Invoker with invalid apiInvokerID","text":"<p>Test ID:: capif_security_api-6</p> <p>Description:</p> <p>This test case will check that an provider can retrieve the Security context of an API Invoker</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority) and API Invoker has created a valid Security Context</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Retrieve Security Context of invalid Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> <li>Retrieve Security Context by Provider of invalid invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Retrieve security context:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-7-retrieve-the-security-context-of-an-api-invoker-with-invalid-apfid","title":"Test Case 7: Retrieve the Security Context of an API Invoker with invalid apfId","text":"<p>Test ID:: capif_security_api-7</p> <p>Description:</p> <p>This test case will check that an Provider cannot retrieve the Security context of an API Invoker without valid apfId</p> <p>Pre-Conditions:</p> <ul> <li>API Exposure Function is not pre-authorised (has invalid apfId)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate</li> </ul> </li> <li> <p>Retrieve Security Context as Invoker role:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Retrieve Security Context as Provider.</li> </ol> <p>Expected Result:</p> <ol> <li>Create security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be aef\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-8-delete-the-security-context-of-an-api-invoker","title":"Test Case 8: Delete the Security Context of an API Invoker","text":"<p>Test ID:: capif_security_api-8</p> <p>Description:</p> <p>This test case will check that an Provider can delete a Security context</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority) and API Invoker has created a valid Security Context</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker but using Provider certificate.</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using AEF certificate</li> </ul> </li> <li> <p>Delete Security Context of Invoker by Provider:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Use AEF certificate</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using AEF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> <li>Delete Security Context by Provider</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Delete security context:</p> <ol> <li>204 No Content response.</li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Security context not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-9-delete-the-security-context-of-an-api-invoker-with-invoker-entity-role","title":"Test Case 9: Delete the Security Context of an API Invoker with Invoker entity role","text":"<p>Test ID:: capif_security_api-9</p> <p>Description:</p> <p>This test case will check that an Invoker cannot delete a Security context</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority) and API Invoker has created a valid Security Context</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker certificate</li> </ul> </li> <li> <p>Delete Security Context of Invoker:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Use Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> <li>Delete Security Context by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Delete security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be aef\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-10-delete-the-security-context-of-an-api-invoker-with-invoker-entity-role-and-invalid-apiinvokerid","title":"Test Case 10: Delete the Security Context of an API Invoker with Invoker entity role and invalid apiInvokerID","text":"<p>Test ID:: capif_security_api-10</p> <p>Description:</p> <p>This test case will check that an Invoker cannot delete a Security context with invalid </p> <p>Pre-Conditions:</p> <ul> <li>Invoker is pre-authorised.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Delete Security Context of Invoker:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>Use Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Delete Security Context by invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Delete security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be aef\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-11-delete-the-security-context-of-an-api-invoker-with-invalid-apiinvokerid","title":"Test Case 11: Delete the Security Context of an API Invoker with invalid apiInvokerID","text":"<p>Test ID:: capif_security_api-11</p> <p>Description:</p> <p>This test case will check that an Provider cannot delete a Security context of invalid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority).</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Delete Security Context of Invoker by Provider:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>Use AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Delete Security Context by provider</li> </ol> <p>Expected Result:</p> <ol> <li>Retrieve security context:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-12-update-the-security-context-of-an-api-invoker","title":"Test Case 12: Update the Security Context of an API Invoker","text":"<p>Test ID:: capif_security_api-12</p> <p>Description:</p> <p>This test case will check that an API Invoker can update a Security context</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Update Security Context of Invoker:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/update</li> <li>body service security body but with notification destination modified to http://robot.testing2</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context By Invoker</li> <li>Update Security Context By Invoker</li> <li>Retrieve Security Context By Provider</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Update security context:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.<ol> <li>Check is this returned object match with modified one.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-13-update-the-security-context-of-an-api-invoker-with-provider-entity-role","title":"Test Case 13: Update the Security Context of an API Invoker with Provider entity role","text":"<p>Test ID:: capif_security_api-13</p> <p>Description:</p> <p>This test case will check that an Provider cannot update a Security context</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized.</li> <li>Invoker has created the Security Context previously.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Update Security Context of Invoker by Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/update</li> <li>body service security body but with notification destination modified to http://robot.testing2</li> <li>Using AEF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context</li> <li>Update Security Context as Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Update security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be invoker\". </li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-14-update-the-security-context-of-an-api-invoker-with-aef-entity-role-and-invalid-apiinvokerid","title":"Test Case 14: Update the Security Context of an API Invoker with AEF entity role and invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-14</p> <p>Description:</p> <p>This test case will check that an Provider cannot update a Security context of invalid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized.</li> <li>Invoker has created the Security Context previously.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Update Security Context of Invoker by Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}/update</li> <li>body service security body</li> <li>Using AEF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Update Security Context as Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Update security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be invoker\". </li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-15-update-the-security-context-of-an-api-invoker-with-invalid-apiinvokerid","title":"Test Case 15: Update the Security Context of an API Invoker with invalid apiInvokerID","text":"<p>Test ID:: capif_security_api-15</p> <p>Description:</p> <p>This test case will check that an API Invoker cannot update a Security context not valid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Update Security Context of Invoker:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}/update</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Update Security Context</li> </ol> <p>Expected Result:</p> <ol> <li>Retrieve security context:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-16-revoke-the-authorization-of-the-api-invoker-for-apis","title":"Test Case 16: Revoke the authorization of the API invoker for APIs.","text":"<p>Test ID:: capif_security_api-16</p> <p>Description:</p> <p>This test case will check that a Provider can revoke the authorization for APIs</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context By Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate</li> </ul> </li> <li> <p>Revoke Authorization by Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/delete</li> <li>body security notification body</li> <li>Using AEF Certificate.</li> </ul> </li> <li> <p>Retrieve Security Context by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context by Invoker</li> <li>Revoke Security Context by Provider</li> <li>Retrieve Security Context by Provider</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Revoke Authorization:</p> <ol> <li>204 No Content response.</li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Security context not found\".</li> <li>cause with message \"API Invoker has no security context\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-17-revoke-the-authorization-of-the-api-invoker-for-apis-without-valid-apfid","title":"Test Case 17: Revoke the authorization of the API invoker for APIs without valid apfID.","text":"<p>Test ID:: capif_security_api-17</p> <p>Description:</p> <p>This test case will check that an Invoker can't revoke the authorization for APIs</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Revoke Authorization by invoker:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/delete</li> <li>body security notification body</li> <li>Using Invoker Certificate</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context</li> <li>Revoke Security Context by invoker</li> <li>Retrieve Security Context</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Revoke Security Context by invoker:</p> <ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be provider\". </li> </ul> </li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.<ol> <li>Check is this returned object match with created one.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-18-revoke-the-authorization-of-the-api-invoker-for-apis-with-invalid-apiinvokerid","title":"Test Case 18: Revoke the authorization of the API invoker for APIs with invalid apiInvokerId.","text":"<p>Test ID:: capif_security_api-18</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot revoke the authorization for APIs for invalid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Revoke Authorization by Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}/delete</li> <li>body security notification body</li> <li>Using AEF Certificate.</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}?authenticationInfo=true&authorizationInfo=true</li> <li>This request will ask with parameter to retrieve authenticationInfo and authorizationInfo</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context</li> <li>Revoke Security Context by Provider</li> <li>Retrieve Security Context</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Revoke Security Context by invoker:</p> <ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.<ol> <li>Check is this return one object that match with created one.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-19-retrieve-access-token","title":"Test Case 19: Retrieve access token","text":"<p>Test ID:: capif_security_api-19</p> <p>Description:</p> <p>This test case will check that an API Invoker can retrieve a security access token OAuth 2.0.</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerId)</li> <li>Service API of Provider is published</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token:</li> <li>body access token req body and example example</li> <li>securityId is apiInvokerId.</li> <li>grant_type=client_credentials.</li> <li>Create Scope properly for request: 3gpp#{aef_id}:{api_name}</li> <li>Using Invoker Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>200 OK</li> <li>body must follow AccessTokenRsp with:<ol> <li>access_token present</li> <li>token_type=Bearer</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-20-retrieve-access-token-by-provider","title":"Test Case 20: Retrieve access token by Provider","text":"<p>Test ID:: capif_security_api-20</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot revoke the authorization for APIs for invalid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by provider:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token:</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>Using AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error unauthorized_client</li> <li>error_description=Role not authorized for this API route</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-21-retrieve-access-token-by-provider-with-invalid-apiinvokerid","title":"Test Case 21: Retrieve access token by Provider with invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-21</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token without valid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by provider:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{API_INVOKER_NOT_VALID}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>Using AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error unauthorized_client</li> <li>error_description=Role not authorized for this API route</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-22-retrieve-access-token-with-invalid-apiinvokerid","title":"Test Case 22: Retrieve access token with invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-22</p> <p>Description:</p> <p>This test case will check that an API Invoker can't retrieve a security access token without valid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerId)</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs not filtered:<ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li>Create Security Context for this Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li>Request Access Token by invoker:<ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{API_INVOKER_NOT_VALID}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails29571 data structure, with:<ul> <li>status 404</li> <li>title Not Found</li> <li>detail Security context not found</li> <li>cause API Invoker has no security context</li> </ul> </li> </ol> </li> </ol> <p>NOTE: ProblemDetails29571 is the definition present for this request at swagger of ProblemDetails, and this is different from definition of ProblemDetails across other CAPIF Services</p>"},{"location":"testing/testplan/api_security_service/#test-case-23-retrieve-access-token-with-invalid-client_id","title":"Test Case 23: Retrieve access token with invalid client_id","text":"<p>Test ID:: capif_security_api-23</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token without valid client_id at body</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>client_id is not-valid </li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error invalid_client</li> <li>error_description=Client Id not found</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-24-retrieve-access-token-with-unsupported-grant_type","title":"Test Case 24: Retrieve access token with unsupported grant_type","text":"<p>Test ID:: capif_security_api-24</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token with unsupported grant_type</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=not_valid</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error unsupported_grant_type</li> <li>error_description=Invalid value for <code>grant_type</code> \\(${grant_type}\\), must be one of \\['client_credentials'\\] - 'grant_type'</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-25-retrieve-access-token-with-invalid-scope","title":"Test Case 25: Retrieve access token with invalid scope","text":"<p>Test ID:: capif_security_api-25</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token with complete invalid scope</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>scope=not-valid-scope</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error invalid_scope</li> <li>error_description=The first characters must be '3gpp'</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-26-retrieve-access-token-with-invalid-aefid-at-scope","title":"Test Case 26: Retrieve access token with invalid aefid at scope","text":"<p>Test ID:: capif_security_api-26</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token with invalid aefId at scope</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>scope=3gpp#1234:service_1</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error invalid_scope</li> <li>error_description=One of aef_id not belongs of your security context</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-27-retrieve-access-token-with-invalid-apiname-at-scope","title":"Test Case 27: Retrieve access token with invalid apiName at scope","text":"<p>Test ID:: capif_security_api-27</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token with invalid apiName at scope</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>scope=3gpp#{aef_id}:not-valid</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error invalid_scope</li> <li>error_description=One of the api names does not exist or is not associated with the aef id provided</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/common_operations/","title":"Common Operations","text":""},{"location":"testing/testplan/common_operations/#register-new-user","title":"Register new user","text":"<p>In order to use OpenCAPIF we must add a new user. This new user can onboard/register any Invokers or Providers.</p> <p>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.</p> <p>The steps to register a new user at Register Service are:</p>"},{"location":"testing/testplan/common_operations/#admin-create-user","title":"Admin create User","text":"<p>1) Login as Admin to get access_token:</p> <ul> <li>Send POST to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/login<ul> <li>Include basic Auth Header with Admin credentials</li> </ul> </li> <li>Get access_token and refresh_token from response</li> </ul> <p></p> <p>2) Create User:</p> <ul> <li>Send POST to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/createUser<ul> <li>Include Admin access_token in Authorization Bearer Header</li> <li>Body user_registration_body</li> </ul> </li> </ul> <p></p>"},{"location":"testing/testplan/common_operations/#user-retrieve-access-token-and-other-information","title":"User Retrieve access token and other information","text":"<p>1) Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth<ul> <li>Include basic Auth Header with User credentials</li> </ul> </li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> <p></p>"},{"location":"testing/testplan/common_operations/#onboard-an-invoker","title":"Onboard an Invoker","text":""},{"location":"testing/testplan/common_operations/#steps-to-perform-operation","title":"Steps to perform operation","text":"<p>Preconditions: The administrator must have previously registered the User.</p> <ol> <li>Create public and private key at invoker</li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Onboard Invoker: </p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers</li> <li>Reference Request Body: invoker onboarding body</li> <li>\"onboardingInformation\"->\"apiInvokerPublicKey\": must contain public key generated by Invoker.</li> <li>Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token})</li> </ul> </li> </ol>"},{"location":"testing/testplan/common_operations/#checks-to-ensure-onboarding","title":"Checks to ensure onboarding","text":"<ol> <li> <p>Response to Get Auth:</p> <ol> <li>200 OK</li> <li>access_token returned.</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/common_operations/#example-flow","title":"Example Flow","text":""},{"location":"testing/testplan/common_operations/#register-a-provider","title":"Register a Provider","text":""},{"location":"testing/testplan/common_operations/#steps-to-perform-operation_1","title":"Steps to Perform operation","text":"<ol> <li>Create public and private key at provider for provider itself and each function (apf, aef and amf)</li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token})</li> <li>Store each cert in a file with according name.</li> </ul> </li> </ol>"},{"location":"testing/testplan/common_operations/#checks-to-ensure-provider-registration","title":"Checks to ensure provider registration","text":"<ol> <li> <p>Response to Register:</p> <ol> <li>201 Created</li> </ol> </li> <li> <p>Response to Get Auth:</p> <ol> <li>200 OK</li> <li>access_token returned.</li> </ol> </li> <li> <p>Register Provider at Provider Management:</p> <ol> <li>201 Created response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure.</li> <li>For each apiProvFuncs, we must check:<ol> <li>apiProvFuncId is set</li> <li>apiProvCert under regInfo is set properly</li> </ol> </li> <li>Location Header must contain the new resource URL {apiRoot}/api-provider-management/v1/registrations/{registrationId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/common_operations/#example-flow_1","title":"Example Flow","text":""}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Introduction","text":""},{"location":"#what-is-opencapif","title":"What is OpenCAPIF?","text":"<p>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.</p> <p>CAPIF is the \u201cCommon API Framework\u201d defined by 3GPP to manage the APIs exposed by 3GPP networks in their northbound interfaces.</p> <p>3GPP specifications for CAPIF defines three types of entities:</p> <ul> <li>API Providers, which are the entities exposing APIs (e.g., NEF)</li> <li>API Invokers, which are the entities that consume APIs (e.g., Applications), and</li> <li>the CAPIF Core Function, which manages the relationships between the other two.</li> </ul> <p></p> <p>The CAPIF Core Function is the cornerstone of the Common API Framework and provides the following capabilities:</p> <ul> <li>Authenticating the API Invoker based on its identity and other information;</li> <li>Supporting mutual authentication with the API Invoker;</li> <li>Providing authorization for the API Invoker prior to accessing the exposed APIs;</li> <li>Publishing, storing, and supporting the discovery of service APIs information;</li> <li>Controlling the service API access based on PLMN operator configured policies;</li> <li>Storing the logs for the service API invocations and providing the service API invocation logs to authorized entities;</li> <li>Charging based on the logs of the service API invocations;</li> <li>Monitoring the service API invocations;</li> <li>Onboarding a new API Invoker and offboarding an API Invoker;</li> <li>Storing policy configurations related to CAPIF and service APIs;</li> <li>Support for accessing the logs for auditing (e.g., detecting abuse); and</li> <li>Support for publishing and discovery of service APIs information among CAPIF Core Function (CAPIF interconnection).</li> </ul> <p>The following diagram shows how API Invokers and API Providers interact with the CAPIF Core Function to Register in CAPIF, Publish APIs, Discover APIs and Consume APIs. It is important to highlight that the CAPIF Core Function is not a classical API Gateway. The API consumption takes place directly between the API Invoker and the API Provider. Therefore, CAPIF does not impact API performance in API consumption between API Invokers and API Providers.</p> <p></p> <p>If you want to know more about OpenCAPIF check The story behind openCAPIF</p>"},{"location":"#repository-structure","title":"Repository structure","text":"<p>You can check the code at OpenCAPIF Repository</p> <pre><code>CAPIF_API_Services\n\u2514\u2500\u2500\u2500helm\n\u2514\u2500\u2500\u2500monitoring\n\u2514\u2500\u2500\u2500services\n\u2514\u2500\u2500\u2500tests\n\u2514\u2500\u2500\u2500tools\n \u2514\u2500\u2500\u2500robot\n \u2514\u2500\u2500\u2500open_api_script\n</code></pre> <ul> <li>helm: This folder contains helm files to deploy capif under k8s environment.</li> <li>monitoring: This folder contains grafana helm files to deploy capif under k8s environment.</li> <li>services: Services developed following CAPIF API specifications. Also, other complementary services (e.g., NGINX and JWTauth services for the authentication of API consuming entities).</li> <li>tools: Auxiliary tools. Robot Framework related code and OpenAPI scripts.</li> <li>test: Tests developed using Robot Framework.</li> </ul>"},{"location":"#capif_api_services","title":"CAPIF_API_Services","text":"<p>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</p>"},{"location":"#how-to-test-capif-apis","title":"How to test CAPIF APIs","text":"<p>The above APIs can be tested either with POSTMAN tool or running the developed tests with Robot Framework.</p>"},{"location":"#test-plan-documentation","title":"Test Plan Documentation","text":"<p>Complete documentation of tests is available here: Test Plan Directory</p>"},{"location":"#robot-framework","title":"Robot Framework","text":"<p>In order to ensure that modifications over CAPIF services still fulfills the required functionality, the Robot Framework Test Suite must be successfully run.</p> <p>The Robot Test Suite covers the requirements described in the test plan at Test Plan Directory folder.</p> <p>Please check the Testing with Robot Framework Section</p>"},{"location":"#using-postman","title":"Using PostMan","text":"<p>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.</p> <p>For more information on how to test the APIs with POSTMAN, go to this POSTMAN Section.</p>"},{"location":"#important-urls","title":"Important urls:","text":""},{"location":"#mongo-capifs-db-dashboard","title":"Mongo CAPIF's DB Dashboard","text":"<pre><code>http://localhost:8082/ (if accessed from localhost) \n\nor\n\nhttp://<Mongo CAPIF Express Host IP>:8082/ (if accessed from another host)\n</code></pre>"},{"location":"#mongo-registers-db-dashboard","title":"Mongo Register's DB Dashboard","text":"<pre><code>http://localhost:8083/ (if accessed from localhost) \n\nor\n\nhttp://<Mongo Register Express Host IP>:8083/ (if accessed from another host)\n</code></pre>"},{"location":"#faq-documentation","title":"FAQ Documentation","text":"<p>Frequently asked questions can be found here: FAQ Section</p>"},{"location":"FAQ/","title":"Frequently Asked Questions (FAQ)","text":""},{"location":"FAQ/#does-the-user-have-to-develop-the-3-elements-of-the-provider-aef-amf-and-apf","title":"Does the user have to develop the 3 elements of the provider (AEF, AMF and APF)?","text":"<p>No, you only have to make the request to the \"/onboarding\" endpoint. In it you must specify a CSR for the AEF, APF and AMF and you will receive the certificates for each of them in the response.</p>"},{"location":"FAQ/#there-is-one-party-that-publishes-the-api-and-another-that-exposes-it-what-is-the-difference","title":"There is one party that publishes the API and another that exposes it, what is the difference?","text":"<p>There are different services, the APF, intended for publishing the APIs, and the AEF, intended so that the invoker can call it. The APF is what connects to the Capif Core Function to publish the service and when the service is up, you need the AEF service so that invokers can connect to it.</p>"},{"location":"FAQ/#before-publishing-an-api-do-you-have-to-be-registered-in-capif","title":"Before publishing an API, do you have to be registered in CAPIF?","text":"<p>Yes, before publishing an API you must register using the POST /register endpoint.</p>"},{"location":"FAQ/#where-is-the-registration-done","title":"Where is the registration done?","text":"<p>Registration is done in a REST API outside of the CAPIF specification taht we have implemented.</p>"},{"location":"FAQ/#is-the-username-and-password-chosen-by-the-user-when-registering-or-is-it-assigned-when-requesting-registration-to-capif-public-instance","title":"Is the username and password chosen by the user when registering or is it assigned when requesting registration to CAPIF public instance?","text":"<p>When you make the request to the \"/endpoint\" of register, you will be returned a username and a password determined by CAPIF.</p>"},{"location":"FAQ/#what-is-a-csr","title":"What is a CSR?","text":"<p>A CSR is a Certificate Signing Request. It is a generated data block where the certificate is planned to be installed and contains key information such as public key, organization, and location, and is used to request a certificate from a certificate authority (CA). In CAPIF, 3 CSRs are necessary to register a provider, for AEF, APF and AMF.</p>"},{"location":"FAQ/#when-doing-the-register_provider-where-can-i-find-the-csrs-that-are-generated","title":"When doing the register_provider where can I find the CSRs that are generated?","text":"<p>When using the \"register_provider\" command, if you add the \"debug\" option, it shows you a json with the data used to register the provider. There we can find in the body a list of 3 elements corresponding to AEF, APF and AMF. IN each of them, the apiProbPubKey field corresponds to the CSR.</p>"},{"location":"FAQ/#how-to-use-the-example-client-capif_invoker_gui","title":"How to use the example client (CAPIF_INVOKER_GUI)?","text":"<p>First you have to make a \"./run.sh host:port\" indicating the address of the public CAPIF. Once the Docker containers are up, you have to do a \"./terminal_to_py_netapp.sh\" and then a \"python main.py\". At this point we will find ourselves in a console with some predefined commands to use the Client. If we press tab twice it will bring up the list of available commands.</p>"},{"location":"FAQ/#where-is-the-capif-public-instance-located","title":"Where is the CAPIF public instance located?","text":"<p>The CAPIF public instance can be found at the following URLs: - capif.mobilesandbox.cloud:37211 (HTTPS) - capif.mobilesandbox.cloud:37212 (HTTP)</p>"},{"location":"FAQ/#do-you-have-to-publish-3-apis-one-for-each-instance","title":"Do you have to publish 3 APIs? one for each instance?","text":"<p>No, you only have to publish a single API but each component is responsible for a specific service, whether publishing or exposing.</p>"},{"location":"FAQ/#once-the-api-is-published-is-it-always-active-or-do-you-have-to-republish-it-every-time-you-want-to-use-it","title":"Once the API is published, is it always active? Or do you have to republish it every time you want to use it?","text":"<p>It is better to unsubscribe the API every time you exit the application since otherwise it could be republished and it would be double.</p>"},{"location":"FAQ/#would-the-same-username-and-password-be-valid-for-different-invokers","title":"Would the same username and password be valid for different invokers?","text":"<p>Yes, a user can have multiple invokers at the same time, and as such, the username and password would be the same.</p>"},{"location":"FAQ/#what-is-the-notfication-destination-field-in-the-register_invoker-request","title":"What is the notfication destination field in the register_invoker request?","text":"<p>This is the callback URL used to notify events. CAPIF has an Event service to subscribe to that notifies actions such as a subscription to an API, a change in the state of an API...</p>"},{"location":"FAQ/#is-the-notification_destination-a-required-field-in-the-register_invoker","title":"Is the notification_destination a required field in the register_invoker","text":"<p>No, it is not mandatory, but if you do not enter it you will not receive any CAPIF events. For example, the APF may delete the API, you will not be notified that the API is no longer available.</p>"},{"location":"FAQ/#what-is-the-purpose-of-the-discover_service-function-in-the-invoker-client","title":"What is the purpose of the \"discover_service\" function in the invoker client?","text":"<p>The discover_service returns a json with all the services that exist exposed in CAPIF at that moment.</p>"},{"location":"FAQ/#what-is-the-purpose-of-the-get_security_auth-function-in-the-invoker-client","title":"What is the purpose of the \"get_security_auth\" function in the invoker client?","text":"<p>Sirve para pedir el token o para refrescarlo en caso de que haya caducado. You have to use that token to call the API from the invoker.</p>"},{"location":"FAQ/#what-is-the-purpose-of-the-register_security_context-function-in-the-invoker-client","title":"What is the purpose of the \"register_security_context\" function in the invoker client?","text":"<p>To consume the API it is necessary to have a Security Context registered with the data and the authentication method.</p>"},{"location":"FAQ/#is-a-user-the-same-as-an-exposer","title":"Is a user the same as an exposer?","text":"<p>No, a user registers in CAPIF and once done can have the role of invoker, provider or both.</p>"},{"location":"FAQ/#where-can-i-put-my-endpoint","title":"Where can I put my endpoint?","text":"<p>You have to set your endpoint when doing the \"publish_service\" functionality: <code>publish_service capif_ops/config_files/service_api_description_hello.json</code></p> <p>In the file \"service_api_description_hello.json\" you configure the service that is going to be exposed and by developing one to suit you, you expose your API.</p>"},{"location":"architecture/","title":"Architecture","text":""},{"location":"architecture/#architecture","title":"Architecture","text":"<p>The CAPIF architecture has three main components, Register Service, Vault and CCF, which are represented in the following image:</p> <p></p> <p>Each component is separated into different namespaces and all communications between them use Rest APIs.</p> <p>Apart from the communication between components, there are 2 other entities that can use them:</p> <ul> <li>Admin/superadmin: Responsible for managing users with the Register or carrying out special operations in the CCF.</li> <li>Users: They are those who want to use CAPIF, registering as a user in the Register and as Invoker or Provider in the CCF.</li> </ul>"},{"location":"architecture/#register-ns","title":"Register NS","text":"<p>This namespace belongs to the Register service, and we find 2 components:</p> <ul> <li>Register Service: It is responsible for managing all users who use CAPIF, in addition to providing the necessary information for its use.</li> <li>Register MONGO DATABASE: It is the Register database, in it we store all the information about registered users.</li> </ul>"},{"location":"architecture/#vault-ns","title":"Vault NS","text":"<p>This namespace belongs to Vault. </p> <p>This component is responsible for managing all CAPIF certificates, so other components such as the Register or the CCF communicate with it to create new certificates or request keys.</p>"},{"location":"architecture/#mon-ns","title":"Mon NS","text":"<p>This is the main namespace of CAPIF, since it contains all the CCF services in addition to other components:</p> <ul> <li>NGINX: Responsible for acting as a reverse proxy to distribute CAPIF requests to the different services, controlling whether or not they are authorized to access them.</li> <li>REDIS: Used for internal communication of services.</li> <li>CAPIF MONGO DATABASE: CAPIF database, where all information related to CAPIF services such as invokers, registered providers or published services is stored.</li> <li>HELPER: Service that simplifies integration with third parties such as external management portals.</li> </ul>"},{"location":"architecture/#new-architecture","title":"New Architecture","text":"<p>You can check the details of all these changes in the conversation on the OCF wiki.</p>"},{"location":"releasenotes/","title":"Release X.X.X-rc","text":"<p>This release includes next changes:</p> <p>New register flow to ensure:</p> <ul> <li>isolation between CCF and Register services.</li> <li>Improve security, with split resposability between administrator operations and common user.</li> </ul> <p>Improved Testing with Robot in order to cover:</p> <ul> <li>New Register flows.</li> <li>Allow different URLs for register, ccf and vault services.</li> </ul> <p>Improved security on DB:</p> <ul> <li>Credentials requested to access mongo databases.</li> <li>Credentials requested also by mongo-express.</li> </ul> <p>Scripts upgraded:</p> <ul> <li>docker compose version 2 used on them.</li> <li>New cleaning script developed.</li> </ul> <p>Cleanup of capif repository:</p> <ul> <li>Documentation is now on splitted repository [OCF Documentation Repository]</li> <li>Test plan was moved to [OCF Documentation Repository]</li> <li>Obsolote data is removed.</li> </ul>"},{"location":"releasenotes/#release-00","title":"Release 0.0","text":"<p>The APIs included in Release 0.0 are:</p> <ul> <li>JWT Authentication APIs</li> <li>CAPIF Invoker Management API</li> <li>CAPIF Publish API</li> <li>CAPIF Discover API</li> <li>CAPIF Security API</li> <li>CAPIF Events API</li> <li>CAPIF Provider Management API</li> </ul> <p>This Release also includes a Robot Test Suite for all those services and a Postman Test Suite for simple testing.</p>"},{"location":"gettingstarted/howtorun/","title":"How to Run","text":"<ul> <li>Downloading the project</li> <li>1. Create a folder to download the project</li> <li>2. Download the deployment script</li> <li>3. Run the deployment script</li> <li>Run All CAPIF Services locally with Docker images</li> <li>Run All CAPIF Services locally with Docker images and deploy monitoring stack</li> <li>Run each service using Docker</li> <li>Run each service using Python</li> <li>Start Your Testing with OpenCAPIF</li> </ul> <p>Capif services are developed under services folder.</p>"},{"location":"gettingstarted/howtorun/#downloading-the-project","title":"Downloading the project","text":"<p>You can easily download CAPIF to run in local environment following next steps:</p>"},{"location":"gettingstarted/howtorun/#1-create-a-folder-to-download-the-project","title":"1. Create a folder to download the project","text":"<pre><code>mkdir OpenCAPIF\n\ncd OpenCAPIF\n</code></pre>"},{"location":"gettingstarted/howtorun/#2-download-the-deployment-script","title":"2. Download the deployment script","text":"<p>Download the deployment / environment preparation script (press here to directly download script):</p> <pre><code>wget https://labs.etsi.org/rep/ocf/capif/-/raw/staging/deploy.sh\n</code></pre> <p>Make it executable:</p> <pre><code>chmod +x deploy.sh\n</code></pre>"},{"location":"gettingstarted/howtorun/#3-run-the-deployment-script","title":"3. Run the deployment script","text":"<p>This script selects the branch for capif repository project to pull from.</p> <p>If you run the script without selecting a branch the the main branch is going to be selected.</p> <p>We recommend:</p> <ul> <li>main branch for the most stable experience and staging branch for an experience with the latest features (for staging branch installation, it is strongly advisable that you may as well follow the staging documentation)</li> </ul> <pre><code># ./deploy.sh [branch to fetch] [true or false (default) to install monitoring stack or not]\n\nsudo ./deploy.sh staging\n</code></pre> <p>We recommend running the deploy.sh script with root permissions! In other case, some directories may not be accessible by the project building tools and hinder the smooth installation.</p>"},{"location":"gettingstarted/howtorun/#run-all-capif-services-locally-with-docker-images","title":"Run All CAPIF Services locally with Docker images","text":"<p>To run using docker and docker compose, version 2.10 or higher, you must ensure you have those tools installed in your machine. Also to simplify the process, we have 3 scripts allowing docker images to deploy, check and cleanup.</p> <p>All these scripts are available under services directory.</p> <p>To run CAPIF APIs locally using docker and docker-compose you can use run.sh script:</p> <pre><code>./run.sh -h\n\nUsage: ./run.sh <options>\n -c : Setup different hostname for capif\n -m : Launch monitoring service\n -h : show this help\n</code></pre> <p>This script builds and runs all services using docker images, including mongodb and nginx locally and in the background, and imports ca.crt to nginx. By default monitoring is not activated and Nginx is deployed use capifcore as a hostname. </p> <p>Some examples of use:</p> <pre><code># Default values, No monitoring and capifcore as CAPIF_HOSTNAME\n./run.sh\n\n# opencapif.etsi.org as CAPIF_HOSTNAME\n./run.sh -c opencapif.etsi.org\n\n# opencapif.etsi.org as CAPIF_HOSTNAME and monitoring activated\n./run.sh -c opencapif.etsi.org -m \n\n</code></pre> <p>If you want to check if all CAPIF services are running properly in a local machine after executing run.sh, you can use:</p> <pre><code>./check_services_are_running.sh\n</code></pre> <p>This shell script will return 0 if all services are running properly.</p> <p>When we need to stop all CAPIF services, we can use next bash script:</p> <pre><code>./clean_capif_docker_services.sh -a\n</code></pre> <p>NOTE: You can use different flags if you only want to stop some of them, please check the help using</p> <pre><code>./clean_capif_docker_services.sh -h\n\nUsage: clean_capif_docker_services.sh <options>\n -c : clean capif services\n -v : clean vault service\n -r : clean register service\n -m : clean monitoring service\n -a : clean all services\n -h : show this help\n</code></pre> <p>This shell script will remove and clean all CAPIF services started previously with run.sh</p> <p>On the other hand you can check logs using show_logs.sh script, please check options:</p> <pre><code>./show_logs.sh\nYou must specify an option when running the script.\nUsage: ./show_logs.sh <options>\n -c : Show capif services\n -v : Show vault service\n -r : Show register service\n -m : Show monitoring service\n -a : Show all services\n -f : Follow log output\n -h : Show this help\n</code></pre> <p>You can also use option -f in order to follow log output in real time</p>"},{"location":"gettingstarted/howtorun/#run-all-capif-services-locally-with-docker-images-and-deploy-monitoring-stack","title":"Run All CAPIF Services locally with Docker images and deploy monitoring stack","text":"<p>It is now possible to deploy a monitoring stack for CAPIF with Grafana, Prometheus, FluentBit, Loki, Cadvisor, Tempo and Opentelemetry.</p> <p>To deploy CAPIF together with the monitoring stack, it is only necessary to execute the following.</p> <pre><code>./run.sh -m true\n</code></pre> <p>After they have been built, the different panels can be consulted in Grafana at the url</p> <pre><code>http://localhost:3000\n</code></pre> <p>By default, the monitoring option is set to false. Once up, all data sources and dashboards are automatically provisioned.</p>"},{"location":"gettingstarted/howtorun/#run-each-service-using-docker","title":"Run each service using Docker","text":"<p>Also you can run OpenCAPIF service by service using docker:</p> <pre><code>cd <Service>\ndocker build -t capif_security .\ndocker run -p 8080:8080 capif_security\n</code></pre>"},{"location":"gettingstarted/howtorun/#run-each-service-using-python","title":"Run each service using Python","text":"<p>Run using python</p> <pre><code>cd <Service>\npip3 install -r requirements.txt\npython3 -m <service>\n</code></pre>"},{"location":"gettingstarted/howtorun/#start-your-testing-with-opencapif","title":"Start Your Testing with OpenCAPIF","text":"<p>Related with OpenCAPIF Testing, the following sections help you to understand testing implemented and how to run it by yourself:</p> <ul> <li>Test Plan Directory: Here you can find the complete test plan definition that are accomplish by all versions released of OpenCAPIF.</li> <li>Testing with Robot Framework: At this section you can find all information about how to run the test suite implemented using Robot Framework.</li> <li>Testing with Postman: Easy way to understand the complete basic OpenCAPIF flow, acting as invoker and provider.</li> </ul>"},{"location":"testing/postman/","title":"Postman","text":"<p>In this section we can use Postman to publish an API as a provider and use it as an invoker.</p>"},{"location":"testing/postman/#requisites","title":"Requisites","text":"<ul> <li>We will need to have Node.js installed since we will use a small script to create the CSRs of the certificates.</li> <li>An instance of CAPIF (If it is not local, certain variables would have to be modified both in the Node.js script and in the Postman environment variables).</li> </ul>"},{"location":"testing/postman/#first-steps","title":"First steps","text":"<ol> <li>Install the Node dependencies package.json to run the script with:</li> </ol> <pre><code>npm i\n</code></pre> <ol> <li>Run the script.js with the following command:</li> </ol> <pre><code>node script.js\n</code></pre> <ol> <li>Import Postman collection and environment variables (CAPIF.postman_collection.json and CAPIF.postman_environment.json)</li> <li>Select CAPIF Environment before start testing.</li> </ol>"},{"location":"testing/postman/#remote-capif","title":"Remote CAPIF","text":"<p>If the CAPIF is not local, the host and port of both the CAPIF and the register would have to be specified in the variables, and the CAPIF_HOSTNAME in the script, necessary to obtain the server certificate.</p> <p>Enviroments in Postman</p> <pre><code>CAPIF_HOSTNAME capifcore\nCAPIF_PORT 8080\nREGISTER_HOSTNAME register\nREGISTER_PORT 8084\n</code></pre> <p>Const in script.js</p> <pre><code>CAPIF_HOSTNAME capifcore\n</code></pre>"},{"location":"testing/postman/#capif-flows","title":"CAPIF Flows","text":"<p>Once the first steps have been taken, we can now use Postman requests. These requests are numbered in the order that must be followed to obtain everything necessary from CAPIF.</p>"},{"location":"testing/postman/#creation-of-user-by-admin","title":"Creation of User by Admin","text":"<p>The first step would be for an administrator to create a user with which a provider and an invoker will be created. To do this, the admin must log in to obtain the token needed in admin requests.</p>"},{"location":"testing/postman/#01-login_admin","title":"01-Login_admin","text":""},{"location":"testing/postman/#02-creation-of-user","title":"02-Creation of User","text":""},{"location":"testing/postman/#publication-of-an-api","title":"Publication of an API","text":"<p>The next step is to register a provider using the user created by the administrator in order to publish an API.</p>"},{"location":"testing/postman/#03-getauth_provider","title":"03-getauth_provider","text":""},{"location":"testing/postman/#04-onboard_provider","title":"04-onboard_provider","text":"<p>At this point we move on to using certificate authentication in CAPIF. In Postman it is necessary to add the certificates manually and using more than one certificate for the same host as we do in CAPIF complicates things. For this reason, we use the script to overwrite a certificate and a key when it is necessary to have a specific one.</p> <p>To configure go to settings in Postman and open the certificates section. </p> <ul> <li>Here, activate the CA certificates option and add the ca_cert.pem file found in the Responses folder.</li> <li>Adds a client certificate specifying the CAPIF host being used and the files client_cert.crt and client_key.key in the Responses folder.</li> </ul> <p>Once this is done, the node script will be in charge of changing the certificate that is necessary in each request.</p>"},{"location":"testing/postman/#05-publish_api","title":"05-publish_api","text":"<p>Once the api is published, we can start it. In this case we have a test one created in python called hello_api.py that can be executed with the following command:</p> <pre><code>python3 hello_api.py\n</code></pre> <p>The API publication interface is set to localhost with port 8088, so the service must be set up locally. If you wanted to build it on another site, you would have to change the interface description in the body of publish_api.</p> <p>With this the provider part would be finished.</p>"},{"location":"testing/postman/#calling-the-api","title":"Calling the API","text":"<p>Finally, we will create an invoker with the user given by the administrator to be able to use the published api.</p>"},{"location":"testing/postman/#06-getauth_invoker","title":"06-getauth_invoker","text":""},{"location":"testing/postman/#07-onboard_invoker","title":"07-onboard_invoker","text":"<p>At this point we move on to using certificate authentication in CAPIF. If you did not configure the provider's certificates, you would have to do it now.</p>"},{"location":"testing/postman/#08-discover","title":"08-discover","text":""},{"location":"testing/postman/#09-security_context","title":"09-security_context","text":""},{"location":"testing/postman/#10-get_token","title":"10-get_token","text":""},{"location":"testing/postman/#11-call_service","title":"11-call_service","text":"<p>With this, we would have made the API call and finished the flow.</p>"},{"location":"testing/postman/#other-requests","title":"Other requests","text":"<p>Other requests that we have added are the following:</p> <ul> <li>offboard_provider Performs offboarding of the provider, thereby eliminating the published APIs.</li> <li>offboard_invoker Offboards the invoker, also eliminating access to the APIs of that invoker.</li> <li>remove_user Delete the user.</li> <li>refresh_admin_token Return a new access token to the admin.</li> </ul>"},{"location":"testing/postman/#notes","title":"Notes","text":"<ul> <li>This process is designed to teach how requests are made in Postman and the flow that should be followed to publish and use an API.</li> <li>It is possible that if external CAPIFs are used (Public CAPIF) the test data may already be used or the API already registered.</li> <li>It is necessary to have the Node service running to make the certificate change for the requests, otherwise it will not work.</li> <li>We are working on adding more requests to the Postman collection.</li> <li>This collection is a testing guide and is recommended for testing purposes only.</li> </ul>"},{"location":"testing/robotframework/","title":"Robot Framework","text":""},{"location":"testing/robotframework/#steps-to-test","title":"Steps to Test","text":"<p>To run any test locally you will need docker and docker-compose installed in order run services and execute test plan. Steps will be:</p> <ul> <li> <p>Run All Services: See section Run All CAPIF Services</p> </li> <li> <p>Run desired tests: At this point we have 2 options:</p> </li> <li> <p>Using helper script: Script Test Execution</p> </li> <li>Build robot docker image and execute manually robot docker: Manual Build And Test Execution</li> </ul>"},{"location":"testing/robotframework/#script-test-execution","title":"Script Test Execution","text":"<p>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.</p> <pre><code>./run_capif_tests.sh --include <TAG>\n</code></pre> <p>Results will be stored at /results <p>Please check parameters (include) under Test Execution at Manual Build And Test Execution.</p>"},{"location":"testing/robotframework/#mock-server","title":"Mock Server","text":"<p>Some tests on Test Plans require mockserver. That mock server must be deployed and reachable by Robot Framework and CCF under test.</p> <p>To run Mock Server locally you can just execute the next script:</p> <pre><code>cd services\n./run_mock_server.sh\n\nor\n./run.sh -s\n</code></pre> <p>If you want to launch only tests that not needed mockserver, just add \"--exclude mockserver\" parameter to robot execution:</p> <pre><code>./run_capif_tests.sh --include <TAG> --exclude mockserver\n</code></pre> <p>After run tests the Mock Server can be removed from local deployment:</p> <pre><code>./clean_mock_server.sh\n\nor\n./clean_capif_docker_services.sh -s\n</code></pre>"},{"location":"testing/robotframework/#manual-build-and-test-execution","title":"Manual Build And Test Execution","text":"<ul> <li>Build Robot docker image:</li> </ul> <pre><code>cd tools/robot\ndocker build . -t capif-robot-test:latest\n</code></pre> <ul> <li>Tests Execution:</li> </ul> <p>Execute all tests locally:</p> <pre><code><PATH_TO_REPOSITORY>=path in local machine to repository cloned.\n<PATH_RESULT_FOLDER>=path to a folder on local machine to store results of Robot Framework execution.\n<CAPIF_HOSTNAME>=Is the hostname set when run.sh is executed, by default it is capifcore.\n<CAPIF_HTTP_PORT>=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.\n<CAPIF_HTTPS_PORT>=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\n<CAPIF_REGISTER>=This is the hostname of register service deployed. By default it is register.\n<CAPIF_REGISTER_PORT>=This is the port to be used to reach register service deployed. By default it is 8084.\n<CAPIF_VAULT>=This is the hostname of vault service. By default it is vault.\n<CAPIF_VAULT_PORT>=This is the port to be used to reach vault service. By default it is 8200.\n<CAPIF_VAULT_TOKEN>=Vault token to be used on request through vault. By default it is \"read-ca-token\".\n<MOCK_SERVER_URL>=Setup Mock server url to be used in notifications at tests marked with mockserver tag. By default it is not set.\n\nTo execute all tests run :\ndocker run -ti --rm --network=\"host\" \\\n --add-host host.docker.internal:host-gateway \\\n --add-host vault:host-gateway \\\n --add-host register:host-gateway \\\n --add-host mock-server:host-gateway \\\n -v <PATH_TO_REPOSITORY>/tests:/opt/robot-tests/tests \\\n -v <PATH_RESULT_FOLDER>:/opt/robot-tests/results capif-robot-test:latest \\\n --variable CAPIF_HOSTNAME:$CAPIF_HOSTNAME \\\n --variable CAPIF_HTTP_PORT:$CAPIF_HTTP_PORT \\\n --variable CAPIF_HTTPS_PORT:$CAPIF_HTTPS_PORT \\\n --variable CAPIF_REGISTER:$CAPIF_REGISTER \\\n --variable CAPIF_REGISTER_PORT:$CAPIF_REGISTER_PORT \\\n --variable CAPIF_VAULT:$CAPIF_VAULT \\\n --variable CAPIF_VAULT_PORT:$CAPIF_VAULT_PORT \\\n --variable CAPIF_VAULT_TOKEN:$CAPIF_VAULT_TOKEN \\\n --variable MOCK_SERVER_URL:$MOCK_SERVER_URL \\\n --include all\n</code></pre> <p>Execute specific tests locally:</p> <pre><code>To run more specific tests, for example, only one functionality:\n<TAG>=Select one from list:\n \"capif_api_acl\",\n \"capif_api_auditing_service\",\n \"capif_api_discover_service\",\n \"capif_api_events\",\n \"capif_api_invoker_management\",\n \"capif_api_logging_service\",\n \"capif_api_provider_management\",\n \"capif_api_publish_service\",\n \"capif_security_api\n\nAnd Run:\ndocker run -ti --rm --network=\"host\" \\\n --add-host host.docker.internal:host-gateway \\\n --add-host vault:host-gateway \\\n --add-host register:host-gateway \\\n --add-host mock-server:host-gateway \\\n -v <PATH_TO_REPOSITORY>/tests:/opt/robot-tests/tests \\\n -v <PATH_RESULT_FOLDER>:/opt/robot-tests/results capif-robot-test:latest \\\n --variable CAPIF_HOSTNAME:$CAPIF_HOSTNAME \\\n --variable CAPIF_HTTP_PORT:$CAPIF_HTTP_PORT \\\n --variable CAPIF_HTTPS_PORT:$CAPIF_HTTPS_PORT \\\n --variable CAPIF_REGISTER:$CAPIF_REGISTER \\\n --variable CAPIF_REGISTER_PORT:$CAPIF_REGISTER_PORT \\\n --variable CAPIF_VAULT:$CAPIF_VAULT \\\n --variable CAPIF_VAULT_PORT:$CAPIF_VAULT_PORT \\\n --variable CAPIF_VAULT_TOKEN:$CAPIF_VAULT_TOKEN \\\n --variable MOCK_SERVER_URL:$MOCK_SERVER_URL \\\n --include <TAG>\n</code></pre>"},{"location":"testing/robotframework/#test-result-review","title":"Test result review","text":"<p>In order to Review results after tests, you can check general report at /report.html or if you need more detailed information /log.html, example: <ul> <li> <p>Report: </p> </li> <li> <p>Detailed information: </p> </li> </ul> <p>NOTE: If you need more detail at Robot Framework Logs you can set log level option just adding to command --loglevel DEBUG</p>"},{"location":"testing/testplan/","title":"Test Plan Index","text":"<p>List of Common API Services implemented:</p> <ul> <li>Common Operations</li> <li>Api Invoker Management</li> <li>Api Provider Management</li> <li>Api Publish Service</li> <li>Api Discover Service</li> <li>Api Events Service</li> <li>Api Security Service</li> <li>Api Logging Service</li> <li>Api Auditing Service</li> <li>Api Access Control Policy</li> </ul>"},{"location":"testing/testplan/api_access_control_policy/","title":"Test Plan for CAPIF Api Access Control Policy","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_access_control_policy/#test-case-1-retrieve-acl","title":"Test Case 1: Retrieve ACL","text":"<p>Test ID: capif_api_acl-1</p> <p>Description:</p> <p>This test case will check that an API Provider can retrieve ACL from CAPIF</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>contain only one object.</li> <li>apiInvokerId must match apiInvokerId registered previously.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-2-retrieve-acl-with-2-service-apis-published","title":"Test Case 2: Retrieve ACL with 2 Service APIs published","text":"<p>Test ID: capif_api_acl-2</p> <p>Description:</p> <p>This test case will check that an API Provider can retrieve ACL from CAPIF for 2 different serviceApis published.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had two Service API Published on CAPIF</li> <li>API Invoker had a Security Context for both Service APIs published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker for both published APIs</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL for serviceApiId1</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL for serviceApiId2</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId2}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1 and service_2</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information for service_1.</li> <li>Provider Get ACL information for service_2.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>contain one object.</li> <li>apiInvokerId must match apiInvokerId registered previously.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-3-retrieve-acl-with-security-context-created-by-two-different-invokers","title":"Test Case 3: Retrieve ACL with security context created by two different Invokers","text":"<p>Test ID: capif_api_acl-3</p> <p>Description:</p> <p>This test case will check that an API Provider can retrieve ACL from CAPIF containing 2 objects.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>Two API Invokers had a Security Context for same Service API published by provider.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker for both published APIs</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Repeat previous 3 steps in order to have a new Invoker.</p> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1 and service_2</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain two objects.</li> <li>One object must match with apiInvokerId1 and the other one with apiInvokerId2 an registered previously.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-4-retrieve-acl-filtered-by-api-invoker-id","title":"Test Case 4: Retrieve ACL filtered by api-invoker-id","text":"<p>Test ID: capif_api_acl-4</p> <p>Description:</p> <p>This test case will check that an API Provider can retrieve ACL filtering by apiInvokerId from CAPIF containing 1 objects.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>Two API Invokers had a Security Context for same Service API published by provider.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker for both published APIs</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Repeat previous 3 steps in order to have a new Invoker.</p> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}&api-invoker-id={apiInvokerId1}</li> <li>Use serviceApiId, aefId and apiInvokerId1</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}&api-invoker-id={apiInvokerId2}</li> <li>Use serviceApiId, aefId and apiInvokerId2</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1 and service_2</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information with query parameter indicating first api-invoker-id.</li> <li>Provider Get ACL information with query parameter indicating second api-invoker-id.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>ACL Response:</p> <ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain one objects.</li> <li>Object must match with apiInvokerId1.</li> </ol> </li> </ol> </li> <li> <p>ACL Response:</p> <ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain one objects.</li> <li>Object must match with apiInvokerId2.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-5-retrieve-acl-filtered-by-supported-features","title":"Test Case 5: Retrieve ACL filtered by supported-features","text":"<p>Test ID: capif_api_acl-5</p> <p>Description:</p> <p>CURRENTLY NOT SUPPORTED FEATURE</p> <p>This test case will check that an API Provider can retrieve ACL filtering by supportedFeatures from CAPIF containing 1 objects.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>Two API Invokers had a Security Context for same Service API published by provider.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker for both published APIs</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Repeat previous 3 steps in order to have a new Invoker.</p> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}&supported-features={apiInvokerId1}</li> <li>Use serviceApiId, aefId and apiInvokerId1</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL for serviceApiId</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId1}?aef-id=${aef_id}&supported-features={apiInvokerId2}</li> <li>Use serviceApiId, aefId and apiInvokerId2</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1 and service_2</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information with query parameter indicating first supported-features.</li> <li>Provider Get ACL information with query parameter indicating second supported-features.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>ACL Response:</p> <ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain one objects.</li> <li>Object must match with supportedFeatures1.</li> </ol> </li> </ol> </li> <li> <p>ACL Response:</p> <ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>Contain one objects.</li> <li>Object must match with supportedFeatures1.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-6-retrieve-acl-with-aef-id-not-valid","title":"Test Case 6: Retrieve ACL with aef-id not valid","text":"<p>Test ID: capif_api_acl-6</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF if aef-id is not valid</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${AEF_ID_NOT_VALID}</li> <li>Use serviceApiId and AEF_ID_NOT_VALID</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {service_api_id}, aef_id: {aef_id}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-7-retrieve-acl-with-service-id-not-valid","title":"Test Case 7: Retrieve ACL with service-id not valid","text":"<p>Test ID: capif_api_acl-7</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF if service-api-id is not valid</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${NOT_VALID_SERVICE_API_ID}?aef-id=${aef_id}</li> <li>Use NOT_VALID_SERVICE_API_ID and aef_id</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {service_api_id}, aef_id: {aef_id}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-8-retrieve-acl-with-service-api-id-and-aef-id-not-valid","title":"Test Case 8: Retrieve ACL with service-api-id and aef-id not valid","text":"<p>Test ID: capif_api_acl-8</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF if service-api-id and aef-id are not valid</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${NOT_VALID_SERVICE_API_ID}?aef-id=${AEF_ID_NOT_VALID}</li> <li>Use NOT_VALID_SERVICE_API_ID and aef_id</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {NOT_VALID_SERVICE_API_ID}, aef_id: {AEF_ID_NOT_VALID}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-9-retrieve-acl-without-securitycontext-created-previously-by-invoker","title":"Test Case 9: Retrieve ACL without SecurityContext created previously by Invoker","text":"<p>Test ID: capif_api_acl-9</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL if no invoker had requested Security Context to CAPIF</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker created but no Security Context for Service API published had been requested.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li> <p>Discover published APIs</p> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {NOT_VALID_SERVICE_API_ID}, aef_id: {AEF_ID_NOT_VALID}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-10-retrieve-acl-filtered-by-api-invoker-id-not-present","title":"Test Case 10: Retrieve ACL filtered by api-invoker-id not present","text":"<p>Test ID: capif_api_acl-10</p> <p>Description:</p> <p>This test case will check that an API Provider get not found response if filter by not valid api-invoker-id doesn't match any registered ACL.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}&api-invoker-id={NOT_VALID_API_INVOKER_ID}</li> <li>Use serviceApiId, aefId and NOT_VALID_API_INVOKER_ID</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {NOT_VALID_SERVICE_API_ID}, aef_id: {AEF_ID_NOT_VALID}, invoker: {api_invoker_id} and supportedFeatures: {supported_features}\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-11-retrieve-acl-with-apf-certificate","title":"Test Case 11: Retrieve ACL with APF Certificate","text":"<p>Test ID: capif_api_acl-11</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF using APF Certificate</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use APF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-12-retrieve-acl-with-amf-certificate","title":"Test Case 12: Retrieve ACL with AMF Certificate","text":"<p>Test ID: capif_api_acl-12</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF using AMF Certificate</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AMF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-13-retrieve-acl-with-invoker-certificate","title":"Test Case 13: Retrieve ACL with Invoker Certificate","text":"<p>Test ID: capif_api_acl-13</p> <p>Description:</p> <p>This test case will check that an API Provider can't retrieve ACL from CAPIF using Invoker Certificate</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information.</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_access_control_policy/#test-case-14-no-acl-for-invoker-after-be-removed","title":"Test Case 14: No ACL for invoker after be removed","text":"<p>Test ID: capif_api_acl-14</p> <p>Description:</p> <p>This test case will check that ACLs are removed after invoker is removed.</p> <p>Pre-Conditions:</p> <ul> <li>API Provider had a Service API Published on CAPIF</li> <li>API Invoker had a Security Context for Service API published and ACL is present</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform Invoker Onboarding store apiInvokerId</p> </li> <li>Discover published APIs</li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li> <p>Provider Retrieve ACL</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}&api-invoker-id={api-invoker-id}</li> <li>Use serviceApiId, aefId and api-invoker-id</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li>Remove Invoker from CAPIF</li> <li>Provider Retrieve ACL<ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}&api-invoker-id={api-invoker-id}</li> <li>Use serviceApiId, aefId and api-invoker-id</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Provider at CCF.</li> <li>Publish a provider API with name service_1</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Provider Get ACL information of invoker.</li> <li>Remove Invoker from CAPIF.</li> <li>Provider Get ACL information of invoker.</li> </ol> <p>Expected Result: 1. 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.</p> <ol> <li>ACL Response:<ol> <li>404 Not Found Response.</li> <li>body returned must accomplish Problem Details data structure.</li> <li>apiInvokerPolicies must:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"No ACLs found for the requested service: {NOT_VALID_SERVICE_API_ID}, aef_id: {AEF_ID_NOT_VALID}, invoker: None and supportedFeatures: None\".</li> <li>cause with message \"Wrong id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/","title":"Test Plan for CAPIF Api Auditing Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_auditing_service/#test-case-1-get-capif-log-entry","title":"Test Case 1: Get CAPIF Log Entry.","text":"<p>Test ID: capif_api_auditing-1</p> <p>Description:</p> <p>This test case will check that a CAPIF AMF can get log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> <li>Log Entry exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Create Log Entry:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs?aef-id={aefId}&api-invoker-id={api-invoker-id}</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry 4. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>200 OK</li> <li>Response Body must follow InvocationLog data structure with:<ul> <li>aefId</li> <li>apiInvokerId</li> <li>logs</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/#test-case-2-get-capif-log-entry-with-no-log-entry-in-capif","title":"Test Case 2: Get CAPIF Log Entry With no Log entry in CAPIF.","text":"<p>Test ID: capif_api_auditing-2</p> <p>Description:</p> <p>This test case will check that a CAPIF AEF can create log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs?aef-id={aefId}&api-invoker-id={api-invoker-id}</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found Log Entry in CAPIF\".</li> <li>cause with message \"Not Exist Logs with the filters applied\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/#test-case-3-get-capif-log-entry-without-aef-id-and-api-invoker-id","title":"Test Case 3: Get CAPIF Log Entry without aef-id and api-invoker-id.","text":"<p>Test ID: capif_api_auditing-3</p> <p>Description:</p> <p>This test case will check that a CAPIF AEF can create log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is no pre-authorised (has no valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> <li>Log Entry exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Create Log Entry:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to *https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry 4. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>400 Bad Request</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 400</li> <li>title with message \"Bad Request\"</li> <li>detail with message \"aef_id and api_invoker_id parameters are mandatory\".</li> <li>cause with message \"Mandatory parameters missing\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/#test-case-4-get-capif-log-entry-with-filtter-api-version","title":"Test Case 4: Get CAPIF Log Entry with filtter api-version.","text":"<p>Test ID: capif_api_auditing-4</p> <p>Description:</p> <p>This test case will check that a CAPIF AMF can get log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> <li>Log Entry exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Create Log Entry:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs?aef-id={aefId}&api-invoker-id={api-invoker-id}&api-version={v1}</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry 4. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>200 OK</li> <li>Response Body must follow InvocationLog data structure with:<ul> <li>aefId</li> <li>apiInvokerId</li> <li>logs</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_auditing_service/#test-case-5-get-capif-log-entry-with-filter-api-version-but-not-exist-in-log-entry","title":"Test Case 5: Get CAPIF Log Entry with filter api-version but not exist in log entry.","text":"<p>Test ID: capif_api_auditing-4</p> <p>Description:</p> <p>This test case will check that a CAPIF AMF can get log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid AMF cert from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> <li>Log Entry exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding, invoker onboarding </p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Create Log Entry:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ul> </li> <li> <p>Get Log:</p> <ol> <li>Send GET to https://{CAPIF_HOSTNAME}/logs/v1/apiInvocationLogs?aef-id={aefId}&api-invoker-id={api-invoker-id}&api-version={v58}</li> <li>Use AMF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry 4. Get Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>detail with message \"Parameters do not match any log entry\"</li> <li>cause with message \"No logs found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/","title":"Test Plan for CAPIF Discover Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_discover_service/#test-case-1-discover-published-service-apis-by-authorised-api-invoker","title":"Test Case 1: Discover Published service APIs by Authorised API Invoker","text":"<p>Test ID: capif_api_discover_service-1</p> <p>Description:</p> <p>This test case will check if NetApp (Invoker) can discover published service APIs.</p> <p>Pre-Conditions:</p> <ul> <li>Service APIs are published.</li> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs:<ul> <li>Send GET to https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Use Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains the API Published previously</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-2-discover-published-service-apis-by-non-authorised-api-invoker","title":"Test Case 2: Discover Published service APIs by Non Authorised API Invoker","text":"<p>Test ID: capif_api_discover_service-2</p> <p>Description:</p> <p>This test case will check that an API Publisher can't discover published APIs because is not authorized.</p> <p>Pre-Conditions:</p> <ul> <li>Service APIs are published.</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs by no invoker entity:<ul> <li>Send GET to https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Use not Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by no invoker entity</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Discover Request By no invoker entity:</p> <ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"User not authorized\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-3-discover-published-service-apis-by-not-registered-api-invoker","title":"Test Case 3: Discover Published service APIs by not registered API Invoker","text":"<p>Test ID: capif_api_discover_service-3</p> <p>Description:</p> <p>This test case will check that a not registered invoker is forbidden to discover published APIs.</p> <p>Pre-Conditions:</p> <ul> <li>Service APIs are published.</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs with not valid apiInvoker:<ul> <li>Send GET to https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={INVOKER_NOT_REGISTERED}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Publisher</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Discover Request By Invoker:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"API Invoker does not exist\".</li> <li>cause with message \"API Invoker id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-4-discover-published-service-apis-by-registered-api-invoker-with-1-result-filtered","title":"Test Case 4: Discover Published service APIs by registered API Invoker with 1 result filtered","text":"<p>Test ID: capif_api_discover_service-4</p> <p>Description:</p> <p>This test case will check if NetApp (Invoker) can discover published service APIs.</p> <p>Pre-Conditions:</p> <ul> <li>At least 2 Service APIs are published.</li> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs filtering by api-name:<ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}&api-name=service_1</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> <li>filter by api-name service_1</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 and service_2 at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Discover filtered by api-name service_1 Service APIs by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Publish request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains previously registered Service APIs published.</li> </ul> </li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains only Service API published with api-name service_1</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-5-discover-published-service-apis-by-registered-api-invoker-filtered-with-no-match","title":"Test Case 5: Discover Published service APIs by registered API Invoker filtered with no match","text":"<p>Test ID: capif_api_discover_service-5</p> <p>Description:</p> <p>This test case will check if NetApp (Invoker) can discover published service APIs.</p> <p>Pre-Conditions:</p> <ul> <li>At least 2 Service APIs are published.</li> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs filtering by api-name not published:<ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}&api-name=NOT_VALID_NAME</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> <li>filter by api-name NOT_VALID_NAME</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 and service_2 at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Discover filtered by api-name not published Service APIs by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Publish request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains previously registered Service APIs published.</li> </ul> </li> </ol> </li> <li>Response to Discover Request By Invoker:<ol> <li>404 Not Found response.</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"API Invoker {api_invoker_id} has no API Published that accomplish filter conditions\".</li> <li>cause with message \"No API Published accomplish filter conditions\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_discover_service/#test-case-6-discover-published-service-apis-by-registered-api-invoker-not-filtered","title":"Test Case 6: Discover Published service APIs by registered API Invoker not filtered","text":"<p>Test ID: capif_api_discover_service-6</p> <p>Description:</p> <p>This test case will check if NetApp (Invoker) can discover published service APIs.</p> <p>Pre-Conditions:</p> <ul> <li>2 Service APIs are published.</li> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs not filtered:<ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 and service_2 at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Discover without filter by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Discover Request By Invoker:</p> <ol> <li>200 OK response.</li> <li>Response body must follow DiscoveredAPIs data structure:<ul> <li>Check if DiscoveredAPIs contains the 2 previously registered Service APIs published.</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/","title":"Test Plan for CAPIF Api Events Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_events_service/#test-case-1-creates-a-new-individual-capif-event-subscription","title":"Test Case 1: Creates a new individual CAPIF Event Subscription.","text":"<p>Test ID: capif_api_events-1</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) can Subscribe to Events Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Event Subscriptions are stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-2-creates-a-new-individual-capif-event-subscription-with-invalid-subscriberid","title":"Test Case 2: Creates a new individual CAPIF Event Subscription with Invalid SubscriberId","text":"<p>Test ID: capif_api_events-2</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) cannot Subscribe to Events without valid SubcriberId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is not pre-authorised (has invalid InvokerId or apfId)</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_NOT_REGISTERED}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker or APF or AEF or AMF Not found\".</li> <li>cause with message \"Subscriber Not Found\".</li> </ul> </li> </ol> </li> <li> <p>Event Subscriptions are not stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-3-deletes-an-individual-capif-event-subscription","title":"Test Case 3: Deletes an individual CAPIF Event Subscription","text":"<p>Test ID: capif_api_events-3</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) can Delete an Event Subscription</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header</li> <li>Remove Event Subscription</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Remove Event Subscription:</p> <ol> <li>Send DELETE to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Event Subscriptions are stored in CAPIF Database</p> </li> <li> <p>Remove Event Subscription:</p> <ol> <li>204 No Content</li> </ol> </li> <li> <p>Event Subscription is not present at CAPIF Database.</p> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-4-deletes-an-individual-capif-event-subscription-with-invalid-subscriberid","title":"Test Case 4: Deletes an individual CAPIF Event Subscription with invalid SubscriberId","text":"<p>Test ID: capif_api_events-4</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) cannot Delete to Events without valid SubcriberId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId).</li> <li>CAPIF subscriber is subscribed to Events.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> <li>Retrieve Location Header with subscriptionId.</li> <li>Remove Event Subscribed with not valid Subscriber.</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Remove Event Subcription with not valid subscriber:</p> <ol> <li>Send DELETE to https://{CAPIF_HOSTNAME}/capif-events/v1/{SUBSCRIBER_ID_NOT_VALID}/subscriptions/{subcriptionId}</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: *{apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Event Subscriptions are stored in CAPIF Database</p> </li> <li> <p>Error Response Body must accomplish with ProblemDetails data structure with:</p> <ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker or APF or AEF or AMF Not found\".</li> <li>cause with message \"Subscriber Not Found\".</li> </ul> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-5-deletes-an-individual-capif-event-subscription-with-invalid-subscriptionid","title":"Test Case 5: Deletes an individual CAPIF Event Subscription with invalid SubscriptionId","text":"<p>Test ID: capif_api_events-5</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (Invoker or Publisher) cannot Delete an Event Subscription without valid SubscriptionId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has invalid InvokerId or apfId).</li> <li>CAPIF subscriber is subscribed to Events.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Subscribe to Events</li> <li>Retrieve Location Header with subscriptionId.</li> <li>Remove Event Subscribed with not valid Subscriber.</li> </ol> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Event Subscription:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body</li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Remove Event Subcription with not valid subscriber:</p> <ol> <li>Send DELETE to to https://{CAPIF_HOSTNAME}/capif-events/v1/{subcriberId}/subscriptions/{SUBSCRIPTION_ID_NOT_VALID}</li> <li>Use Invoker Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Event Subscriptions are stored in CAPIF Database</p> </li> <li>Remove Event Subscription with not valid subscriber:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>detail with message \"Service API not existing\".</li> <li>cause with message \"Event API subscription id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-6-invoker-receives-service-api-invocation-events","title":"Test Case 6: Invoker receives Service API Invocation events","text":"<p>Test ID: capif_api_events-6, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered and published APIs.</li> <li>API Provider had a Service API Published on CAPIF</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register provider and publish one API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover published APIs and extract apiIds and apiNames</li> <li>Subscribe to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE event filtering by aefId.</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header</li> <li>Emulate Success and Failure on API invocation of provider by Invoker, using Invocation Logs API.</li> </ol> <p>Information of Test:</p> <ol> <li>Perform provider registration</li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send POST to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform invoker onboarding</p> </li> <li> <p>Discover published APIs:</p> <ul> <li>Get Api Ids And Api Names from response.</li> </ul> </li> <li> <p>Event Subscription to SERVICE_API_INVOCATION_SUCCESS and SERVICE_API_INVOCATION_FAILURE of provider previously registered:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['SERVICE_API_INVOCATION_SUCCESS','SERVICE_API_INVOCATION_FAILURE']</li> <li>eventFilter: only receive events from provider's aefId.</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Create Log Entry emulating provider receive Success and Failure api invocation from invoker:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body with:<ol> <li>aefId from provider published.</li> <li>apiInvokerId from invoker onboarded.</li> <li>apiId of published API</li> <li>apiName of published API</li> <li>200 and 400 results in two logs.</li> </ol> </li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Response to creation of log entry on CCF must accomplish:<ol> <li>201 Created</li> <li>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}</li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>Two Events have been received.</li> <li>Validate received events follow EventNotification data structure, with invocationLog in eventDetail parameter.<ol> <li>One should be SERVICE_API_INVOCATION_SUCCESS related with 200 result at Log.</li> <li>The other one must be SERVICE_API_INVOCATION_FAILURE related with 400 result at Log.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-7-invoker-subscribe-to-service-api-available-and-unavailable-events","title":"Test Case 7: Invoker subscribe to Service API Available and Unavailable events","text":"<p>Test ID: capif_api_events-7, mockserver</p> <p>Description:</p> <p>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. </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered and published APIs.</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register provider and publish one API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover published APIs and extract apiIds and apiNames</li> <li>Subscribe to SERVICE_API_AVAILABLE and SERVICE_API_UNAVAILABLE event filtering by aefId.</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header</li> <li>Provider publish new API.</li> <li>Provider remove published API.</li> </ol> <p>Information of Test:</p> <ol> <li>Perform provider registration</li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send POST to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Perform invoker onboarding</p> </li> <li> <p>Discover published APIs:</p> <ul> <li>Get Api Ids And Api Names from response.</li> </ul> </li> <li> <p>Event Subscription to SERVICE_API_AVAILABLE and SERVICE_API_UNAVAILABLE of provider previously registered:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['SERVICE_API_AVAILABLE','SERVICE_API_UNAVAILABLE']</li> <li>eventFilter: only receive events from provider's aefId.</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Publish new Service API at CCF:</p> <ul> <li>Send POST to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_2</li> <li>Store serviceApiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Remove published Service API at CCF:</p> <ul> <li>Send DELETE to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Event Subscription must accomplish:</p> <ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li> <p>Mock Server received messages must accomplish:</p> <ol> <li>Two Events have been received.</li> <li>Validate received events follow EventNotification data structure, with apiIds in eventDetail parameter.<ol> <li>One should be SERVICE_API_AVAILABLE apiId of service_2 published API.</li> <li>The other one must be SERVICE_API_UNAVAILABLE apiId of service_1 published API.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-8-invoker-subscribe-to-service-api-update","title":"Test Case 8: Invoker subscribe to Service API Update","text":"<p>Test ID: capif_api_events-8, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered and published APIs.</li> <li>API Provider had a Service API Published on CAPIF</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider and publish one API at CCF</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Discover published APIs and extract apiIds and apiNames</li> <li>Subscribe to SERVICE_API_UPDATE event filtering by aefId.</li> <li>Retrieve {subscriberId} and {subscriptionId} from Location Header at event subscription</li> <li>Provider update information of Service API Published.</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send POST to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> <li>Store serviceApiId</li> </ul> </li> <li> <p>Perform invoker onboarding</p> </li> <li> <p>Discover published APIs:</p> <ul> <li>Get Api Ids And Api Names from response.</li> </ul> </li> <li> <p>Event Subscription to SERVICE_API_UPDATE of provider previously registered:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['SERVICE_API_UPDATE']</li> <li>eventFilter: only receive events from provider's aefId.</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li> <p>Update published API at CCF:</p> <ul> <li>Send PUT to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>body [service api description] with overrided apiName to service_1_modified</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Response to Update Published Service API:<ol> <li>200 OK</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiName service_1_modified</li> </ul> </li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>One Event has been received.</li> <li>Validate received events follow EventNotification data structure, with serviceAPIDescriptions in eventDetail parameter.<ol> <li>Event should be SERVICE_API_UPDATE with eventDetail with modified apiName.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-9-provider-subscribe-to-api-invoker-events","title":"Test Case 9: Provider subscribe to API Invoker events","text":"<p>Test ID: capif_api_events-9, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered.</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Subscribe Provider to API_INVOKER_ONBOARDED, API_INVOKER_UPDATED and API_INVOKER_OFFBOARDED events.</li> <li>Register Invoker and Onboard Invoker at CCF</li> <li>Update Onboarding Information at CCF with a minor change on \"notificationDestination\"</li> <li>Offboard Invoker</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li>Event Subscription to API_INVOKER_ONBOARDED, API_INVOKER_UPDATED and API_INVOKER_OFFBOARDED events:<ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['API_INVOKER_ONBOARDED', 'API_INVOKER_UPDATED', 'API_INVOKER_OFFBOARDED']</li> </ol> </li> <li>Use Provider AMF Certificate</li> </ol> </li> <li>Perform invoker onboarding</li> <li>Update information of previously onboarded Invoker:<ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> <li>\"notificationDestination\": \"http://host.docker.internal:8086/netapp_new_callback\",</li> </ul> </li> <li>Offboard:<ul> <li>Send DELETE to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Update Request (PUT) with minor change must contain:<ol> <li>200 OK response.</li> <li>notificationDestination on response must contain the new value</li> </ol> </li> <li>Response to Offboard Request (DELETE) must contain:<ol> <li>204 No Content</li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>Three Events have been received.</li> <li>Validate received events follow EventNotification data structure, with apiInvokerIds in eventDetail parameter.<ol> <li>One Event should be API_INVOKER_ONBOARDED with eventDetail with modified apiInvokerId.</li> <li>One Event should be API_INVOKER_UPDATED with eventDetail with modified apiInvokerId.</li> <li>One Event should be API_INVOKER_OFFBOARDED with eventDetail with modified apiInvokerId.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-10-provider-subscribed-to-acl-update-event","title":"Test Case 10: Provider subscribed to ACL update event","text":"<p>Test ID: capif_api_events-10, mockserver</p> <p>Description:</p> <p>This test case will check that a CAPIF Provider subscribed to ACCESS_CONTROL_POLICY_UPDATE receive a notification when ACL Changes.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered.</li> <li>API Provider had one Service API Published on CAPIF</li> <li>API Invoker had a Security Context for the Service API published by provider.</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF.</li> <li>Publish a provider API with name service_1.</li> <li>Register Invoker and Onboard Invoker at CCF.</li> <li>Subscribe Provider to ACCESS_CONTROL_POLICY_UPDATE event.</li> <li>Discover APIs filtered by aef_id</li> <li>Create Security Context for Invoker.</li> <li>Provider Retrieve ACL</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li>Perform invoker onboarding</li> <li>Event Subscription to ACCESS_CONTROL_POLICY_UPDATE event:<ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['ACCESS_CONTROL_POLICY_UPDATE']</li> <li>eventFilters: apiInvokerIds array with apiInvokerId of invoker</li> </ol> </li> <li>Use Provider AMF Certificate</li> </ol> </li> <li>Discover published APIs</li> <li>Create Security Context for Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li>Provider Retrieve ACL<ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Create security context:<ol> <li>201 Created response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}</li> </ol> </li> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>contain only one object.</li> <li>apiInvokerId must match apiInvokerId registered previously.</li> </ol> </li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>One Event has been received.</li> <li>Validate received event follow EventNotification data structure, with accCtrlPolListExt in eventDetail parameter.<ol> <li>One Event should be ACCESS_CONTROL_POLICY_UPDATE with eventDetail with accCtrlPolListExt including the apiId and apiInvokerPolicies.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-11-provider-receives-an-acl-unavailable-event-when-invoker-remove-security-context","title":"Test Case 11: Provider receives an ACL unavailable event when invoker remove Security Context.","text":"<p>Test ID: capif_api_events-11, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered.</li> <li>API Provider had one Service API Published on CAPIF</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF.</li> <li>Publish a provider API with name service_1.</li> <li>Register Invoker and Onboard Invoker at CCF.</li> <li>Subscribe Invoker to ACCESS_CONTROL_POLICY_UNAVAILABLE event.</li> <li>Discover APIs filtered by aef_id</li> <li>Create Security Context for Invoker.</li> <li>Provider Retrieve ACL.</li> <li>Remove Security Context for Invoker.</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li>Perform invoker onboarding</li> <li>Event Subscription to ACCESS_CONTROL_POLICY_UNAVAILABLE event:<ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['ACCESS_CONTROL_POLICY_UNAVAILABLE']</li> <li>eventFilters: apiInvokerIds array with apiInvokerId of invoker</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li>Discover published APIs</li> <li>Create Security Context for Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li>Provider Retrieve ACL<ul> <li>Send GET https://{CAPIF_HOSTNAME}/access-control-policy/v1/accessControlPolicyList/${serviceApiId}?aef-id=${aef_id}</li> <li>Use serviceApiId and aefId</li> <li>Use AEF Provider Certificate</li> </ul> </li> <li>Delete Security Context of Invoker by Provider:<ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Use AEF certificate</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Create security context:<ol> <li>201 Created response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}</li> </ol> </li> <li>ACL Response:<ol> <li>200 OK Response.</li> <li>body returned must accomplish AccessControlPolicyList data structure.</li> <li>apiInvokerPolicies must:<ol> <li>contain only one object.</li> <li>apiInvokerId must match apiInvokerId registered previously.</li> </ol> </li> </ol> </li> <li>Delete security context:<ol> <li>204 No Content response.</li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>One Event has been received.</li> <li>Validate received event follow EventNotification data structure, without eventDetail parameter.<ol> <li>One Event should be ACCESS_CONTROL_POLICY_UNAVAILABLE without eventDetail.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_events_service/#test-case-12-invoker-receives-an-invoker-authorization-revoked-and-acl-unavailable-event-when-provider-revoke-invoker-authorization","title":"Test Case 12: Invoker receives an Invoker Authorization Revoked and ACL unavailable event when Provider revoke Invoker Authorization.","text":"<p>Test ID: capif_api_events-12, mockserver</p> <p>Description:</p> <p>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.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid InvokerId or apfId from CAPIF Authority)</li> <li>CAPIF provider is correctly registered.</li> <li>API Provider had one Service API Published on CAPIF</li> <li>Mock Server is up and running to receive requests.</li> <li>Mock Server is clean.</li> </ul> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF.</li> <li>Publish a provider API with name service_1.</li> <li>Register Invoker and Onboard Invoker at CCF.</li> <li>Subscribe Invoker to ACCESS_CONTROL_POLICY_UNAVAILABLE and API_INVOKER_AUTHORIZATION_REVOKED events.</li> <li>Discover APIs filtered by aef_id</li> <li>Create Security Context for Invoker.</li> <li>Revoke Authorization by Provider.</li> </ol> <p>Information of Test:</p> <ol> <li>Check and Clean Mock Server</li> <li>Perform provider registration</li> <li>Perform invoker onboarding</li> <li>Event Subscription to ACCESS_CONTROL_POLICY_UNAVAILABLE and API_INVOKER_AUTHORIZATION_REVOKED event:<ol> <li>Send POST to https://{CAPIF_HOSTNAME}/capif-events/v1/{subscriberId}/subscriptions</li> <li>body event subscription request body with:<ol> <li>events: ['ACCESS_CONTROL_POLICY_UNAVAILABLE','API_INVOKER_AUTHORIZATION_REVOKED']</li> <li>eventFilters: apiInvokerIds array with apiInvokerId of invoker</li> </ol> </li> <li>Use Invoker Certificate</li> </ol> </li> <li>Discover published APIs</li> <li>Create Security Context for Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> <li>Revoke Authorization by Provider:<ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/delete</li> <li>body security notification body</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Expected Result:</p> <ol> <li>Response to Event Subscription must accomplish:<ol> <li>201 Created</li> <li>The URI of the created resource shall be returned in the \"Location\" HTTP header, following this structure: {apiRoot}/capif-events/{apiVersion}/{subscriberId}/subscriptions/{subscriptionId}</li> <li>Response Body must follow EventSubscription data structure.</li> </ol> </li> <li>Create security context:<ol> <li>201 Created response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}</li> </ol> </li> <li>Revoke Authorization:<ol> <li>204 No Content response.</li> </ol> </li> <li>Mock Server received messages must accomplish:<ol> <li>Two Events has been received.</li> <li>Validate received event follow EventNotification data structure, without eventDetail parameter.<ol> <li>One Event should be ACCESS_CONTROL_POLICY_UNAVAILABLE without eventDetail.</li> <li>One Event should be API_INVOKER_AUTHORIZATION_REVOKED without eventDetail.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/","title":"Test Plan for CAPIF Api Invoker Management","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_invoker_management/#test-case-1-onboard-netapp","title":"Test Case 1: Onboard NetApp","text":"<p>Test ID: capif_api_invoker_management-1</p> <p>Description:</p> <p>This test will try to register new NetApp at CAPIF Core.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was not registered previously</li> <li>NetApp was not onboarded previously</li> <li>Preconditions: The administrator must have previously registered the User.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at invoker</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Onboard Invoker:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers</li> <li>Reference Request Body: invoker onboarding body</li> <li>\"onboardingInformation\"->\"apiInvokerPublicKey\": must contain public key generated by Invoker.</li> <li>Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token})</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Retrieve access_token by User from register</li> <li>Onboard Invoker at CCF</li> <li>Store signed Certificate</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-2-onboard-netapp-already-onboarded","title":"Test Case 2: Onboard NetApp Already onboarded","text":"<p>Test ID: capif_api_invoker_management-2</p> <p>Description:</p> <p>This test will check second onboard of same NetApp is not allowed.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Repeat Onboard Invoker:</p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers</li> <li>Reference Request Body: invoker onboarding body</li> <li>\"onboardingInformation\"->\"apiInvokerPublicKey\": must contain public key generated by Invoker.</li> <li>Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token})</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register NetApp at CCF</li> <li>Onboard NetApp at CCF</li> <li>Store signed Certificate at NetApp</li> <li>Onboard Again the NetApp at CCF</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Second Onboard of NetApp must accomplish:<ol> <li>403 Forbidden</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 403</li> <li>title with message \"Forbidden\"</li> <li>detail with message \"Invoker Already registered\".</li> <li>cause with message \"Identical invoker public key\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-3-update-onboarded-netapp","title":"Test Case 3: Update Onboarded NetApp","text":"<p>Test ID: capif_api_invoker_management-3</p> <p>Description:</p> <p>This test will try to update information of previous onboard NetApp at CAPIF Core.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId}</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Update information of previously onboarded Invoker:</p> <ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> <li>\"notificationDestination\": \"http://host.docker.internal:8086/netapp_new_callback\",</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Update Onboarding Information at CCF with a minor change on \"notificationDestination\"</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Update Request (PUT) with minor change must contain:<ol> <li>200 OK response.</li> <li>notificationDestination on response must contain the new value</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-4-update-not-onboarded-netapp","title":"Test Case 4: Update Not Onboarded NetApp","text":"<p>Test ID: capif_api_invoker_management-4</p> <p>Description:</p> <p>This test will try to update information of not onboarded NetApp at CAPIF Core.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was not onboarded previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Update information of not onboarded Invoker:</p> <ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{INVOKER_NOT_REGISTERED}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Onboard Invoker at CCF</li> <li>Update Onboarding Information at CCF of not onboarded</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> </ol> </li> <li>Response to Update Request (PUT) must contain:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Please provide an existing Netapp ID\".</li> <li>cause with message \"Not exist NetappID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-5-offboard-netapp","title":"Test Case 5: Offboard NetApp","text":"<p>Test ID: capif_api_invoker_management-5</p> <p>Description:</p> <p>This test case will check that a Registered NetApp can be deleted.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Offboard:</p> <ul> <li>Send Delete to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Onboard Invoker at CCF</li> <li>Offboard Invoker at CCF</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> </ol> </li> <li>Response to Offboard Request (DELETE) must contain:<ol> <li>204 No Content</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-6-offboard-not-previsouly-onboarded-netapp","title":"Test Case 6: Offboard Not previsouly Onboarded NetApp","text":"<p>Test ID: capif_api_invoker_management-6</p> <p>Description:</p> <p>This test case will check that a Non-Registered NetApp cannot be deleted</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was not onboarded previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Offboard:</p> <ul> <li>Send Delete to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{INVOKER_NOT_REGISTERED}</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Offboard Invoker at CCF</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Offboard Request (DELETE) must contain:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Please provide an existing Netapp ID\".</li> <li>cause with message \"Not exist NetappID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_invoker_management/#test-case-7-update-onboarded-netapp-certificate","title":"Test Case 7: Update Onboarded NetApp Certificate","text":"<p>Test ID: capif_api_invoker_management-7</p> <p>Description:</p> <p>This test will try to update public key and get a new signed certificate by CAPIF Core.</p> <p>Pre-Conditions:</p> <ul> <li>NetApp was registered previously</li> <li>NetApp was onboarded previously with {onboardingId} and {public_key_1}</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding with public_key_1.</p> </li> <li> <p>Create {public_key_2}</p> </li> <li> <p>Update information of previously onboarded Invoker:</p> <ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> <li>[\"onboardingInformation\"][\"apiInvokerPublicKey\"]: {public_key_2},</li> <li>Store new certificate.</li> </ul> </li> <li> <p>Update information of previously onboarded Invoker Using new certificate:</p> <ul> <li>Send PUT to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers/{onboardingId}</li> <li>Reference Request Body is: [put invoker onboarding body]</li> <li>\"notificationDestination\": \"http://host.docker.internal:8086/netapp_new_callback\",</li> <li>Use new invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Invoker at CCF</li> <li>Onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Update Onboarding Information at CCF with new public key</li> <li>Update Onboarding Information at CCF with minor change</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Onboard request must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> <li>Response to Update Request (PUT) with new public key:<ol> <li>200 OK response.</li> <li>apiInvokerCertificate with new certificate on response -> store to use.</li> </ol> </li> <li>Response to Update Request (PUT) with minor change must contain:<ol> <li>200 OK response.</li> <li>notificationDestination on response must contain the new value</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_logging_service/","title":"Test Plan for CAPIF Api Logging Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_logging_service/#test-case-1-creates-a-new-individual-capif-log-entry","title":"Test Case 1: Creates a new individual CAPIF Log Entry.","text":"<p>Test ID: capif_api_logging-1</p> <p>Description:</p> <p>This test case will check that a CAPIF AEF can create log entry to Logging Service</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid aefId from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>201 Created</li> <li>Response Body must follow InvocationLog data structure with:<ul> <li>aefId</li> <li>apiInvokerId</li> <li>logs</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invocation-logs/v1/{aefId}/logs/{logId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_logging_service/#test-case-2-creates-a-new-individual-capif-log-entry-with-invalid-aefid","title":"Test Case 2: Creates a new individual CAPIF Log Entry with Invalid aefId","text":"<p>Test ID: capif_api_logging-2</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (AEF) cannot create Log Entry without valid aefId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is not pre-authorised (has not valid aefId from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{not-valid-aefId}/logs</li> <li>body log entry request body</li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Exposer not exist\".</li> <li>cause with message \"Exposer id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_logging_service/#test-case-3-creates-a-new-individual-capif-log-entry-with-invalid-serviceapi","title":"Test Case 3: Creates a new individual CAPIF Log Entry with Invalid serviceAPI","text":"<p>Test ID: capif_api_logging-3</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (AEF) cannot create Log Entry without valid aefId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid aefId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body [log entry request body with serviceAPI apiName apiId not valid]</li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not exist\".</li> <li>cause with message \"Invoker id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_logging_service/#test-case-4-creates-a-new-individual-capif-log-entry-with-invalid-apiinvokerid","title":"Test Case 4: Creates a new individual CAPIF Log Entry with Invalid apiInvokerId","text":"<p>Test ID: capif_api_logging-4</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (AEF) cannot create Log Entry without valid aefId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid aefId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body [log entry request body with invokerId not valid]</li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure.</li> <li>For each apiProvFuncs, we must check:<ol> <li>apiProvFuncId is set</li> <li>apiProvCert under regInfo is set properly</li> </ol> </li> <li>Location Header must contain the new resource URL {apiRoot}/api-provider-management/v1/registrations/{registrationId}</li> </ol> </li> <li> <p>Response to Logging Service must accomplish:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not exist\".</li> <li>cause with message \"Invoker id not found\".</li> </ul> </li> </ol> </li> <li> <p>Log Entry are not stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_logging_service/#test-case-5-creates-a-new-individual-capif-log-entry-with-invalid-aefid-in-body","title":"Test Case 5: Creates a new individual CAPIF Log Entry with Invalid aefId in body","text":"<p>Test ID: capif_api_logging-5</p> <p>Description:</p> <p>This test case will check that a CAPIF subscriber (AEF) cannot create Log Entry without valid aefId in body</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF provider is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>Service exist in CAPIF</li> <li>Invoker exist in CAPIF</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform provider onboarding and invoker onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Log Entry:</p> <ol> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invocation-logs/v1/{aefId}/logs</li> <li>body [log entry request body with bad aefId] </li> <li>Use AEF Certificate</li> </ol> </li> </ol> <p>Execution Steps: 1. Register Provider and Invoker CCF 2. Publish Service 3. Create Log Entry</p> <p>Expected Result:</p> <ol> <li>Response to Logging Service must accomplish:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"AEF id not matching in request and body\".</li> <li>cause with message \"Not identical AEF id\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/","title":"Test Plan for CAPIF Api Provider Management","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_provider_management/#test-case-1-register-api-provider","title":"Test Case 1: Register Api Provider","text":"<p>Test ID: capif_api_provider_management-1</p> <p>Description:</p> <p>This test case will check that Api Provider can be registered con CCF</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid certificate from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Create private and public key for provider and each function to register.</li> <li>Register Provider.</li> </ol> <p>Expected Result:</p> <ol> <li>Register Provider at Provider Management:<ol> <li>201 Created response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure.</li> <li>For each apiProvFuncs, we must check:<ol> <li>apiProvFuncId is set</li> <li>apiProvCert under regInfo is set properly</li> </ol> </li> <li>Location Header must contain the new resource URL {apiRoot}/api-provider-management/v1/registrations/{registrationId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-2-register-api-provider-already-registered","title":"Test Case 2: Register Api Provider Already registered","text":"<p>Test ID: capif_api_provider_management-2</p> <p>Description:</p> <p>This test case will check that a Api Provider previously registered cannot be re-registered</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was registered previously and there is a {registerId} for his Api Provider in the DB</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Re-Register Provider:</p> <ul> <li>Same regSec than Previous registration</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Create private and public key for provider and each function to register.</li> <li>Register Provider.</li> <li>Re-Register Provider.</li> </ol> <p>Expected Result:</p> <ol> <li>Re-Register Provider:<ol> <li>403 Forbidden response.</li> <li> <p>body returned must accomplish ProblemDetails data structure, with:</p> <ul> <li>status 403</li> <li>title with message \"Forbidden\"</li> <li>detail with message \"Provider already registered\".</li> <li>cause with message \"Identical provider reg sec\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-3-update-registered-api-provider","title":"Test Case 3: Update Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-3</p> <p>Description:</p> <p>This test case will check that a Registered Api Provider can be updated</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was registered previously and there is a {registerId} for his Api Provider in the DB</li> </ul> <p>Information of Test:</p> <ol> <li>Create public and private key at provider for provider itself and each function (apf, aef and amf)</li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Get Resource URL from Location</li> </ul> </li> <li> <p>Update Provider:</p> <ul> <li>Send PUT to Resource URL returned at registration https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{registrationId}</li> <li>body provider request body with apiProvDomInfo set to ROBOT_TESTING_MOD</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Create private and public key for provider and each function to register.</li> <li>Register Provider</li> <li>Update Provider</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Register Provider:</p> <ol> <li>201 Created response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/api-provider-management/v1/registrations/{registrationId}</li> </ol> </li> <li> <p>Update Provider:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure, with:<ul> <li>apiProvDomInfo set to ROBOT_TESTING_MOD</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-4-update-not-registered-api-provider","title":"Test Case 4: Update Not Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-4</p> <p>Description:</p> <p>This test case will check that a Non-Registered Api Provider cannot be updated</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was not registered previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Update Not Registered Provider:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{API_PROVIDER_NOT_REGISTERED}</li> <li>body provider request body</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Update Not Registered Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Update Not Registered Provider:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Not Exist Provider Enrolment Details\".</li> <li>cause with message \"Not found registrations to send this api provider details\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-5-partially-update-registered-api-provider","title":"Test Case 5: Partially Update Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-5</p> <p>Description:</p> <p>This test case will check that a Registered Api Provider can be partially updated</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was registered previously and there is a {registerId} for his Api Provider in the DB</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Partial update provider:</p> <ul> <li>Send PATCH https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{registrationId}</li> <li>body provider request patch body</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Register Provider</li> <li>Partial update provider</li> </ol> <p>Expected Result:</p> <ol> <li>Partial update provider at Provider Management:<ol> <li>200 OK response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure, with:<ul> <li>apiProvDomInfo with \"ROBOT_TESTING_MOD\"</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-6-partially-update-not-registered-api-provider","title":"Test Case 6: Partially Update Not Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-6</p> <p>Description:</p> <p>This test case will check that a Non-Registered Api Provider cannot be partially updated </p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was not registered previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Partial update Provider:</p> <ul> <li>Send PATCH https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{API_API_PROVIDER_NOT_REGISTERED}</li> <li>body provider request patch body</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Register Provider</li> <li>Partial update provider</li> </ol> <p>Expected Result:</p> <ol> <li>Partial update provider:<ol> <li>404 Not Found response.</li> <li> <p>body returned must accomplish ProblemDetails data structure, with:</p> <ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Not Exist Provider Enrolment Details\".</li> <li>cause with message \"Not found registrations to send this api provider details\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-7-delete-registered-api-provider","title":"Test Case 7: Delete Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-7</p> <p>Description:</p> <p>This test case will check that a Registered Api Provider can be deleted</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was registered previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Delete registered provider:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{registrationId}</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Register Provider</li> <li>Delete Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Delete Provider:<ol> <li>204 No Content response.</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_provider_management/#test-case-8-delete-not-registered-api-provider","title":"Test Case 8: Delete Not Registered Api Provider","text":"<p>Test ID: capif_api_provider_management-8</p> <p>Description:</p> <p>This test case will check that a Non-Registered Api Provider cannot be deleted</p> <p>Pre-Conditions:</p> <ul> <li>Api Provider was not registered previously</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Create public and private key at provider for provider itself and each function (apf, aef and amf)</p> </li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Authentication Bearer with access_token</li> <li>Store each cert in a file with according name.</li> </ul> </li> <li> <p>Delete registered provider at Provider Management:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations/{API_PROVIDER_NOT_REGISTERED}</li> <li>Use AMF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Delete Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Delete Provider:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Not Exist Provider Enrolment Details\".</li> <li>cause with message \"Not found registrations to send this api provider details\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/","title":"Test Plan for CAPIF Api Publish Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_publish_service/#test-case-1-publish-api-by-authorised-api-publisher","title":"Test Case 1: Publish API by Authorised API Publisher","text":"<p>Test ID: capif_api_publish_service-1</p> <p>Description:</p> <p>This test case will check that an API Publisher can Publish an API</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> </li> <li> <p>Send Post to ccf_publish_url: https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</p> </li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ol> <p>Execution Steps:</p> <ol> <li> <p>Register Provider at CCF and store certificates.</p> </li> <li> <p>Publish Service API</p> </li> <li> <p>Retrieve {apiId} from body and Location header with new resource created from response</p> </li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Published Service API is stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-2-publish-api-by-non-authorised-api-publisher","title":"Test Case 2: Publish API by NON Authorised API Publisher","text":"<p>Test ID: capif_api_publish_service-2</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Publish an API withot valid apfId </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is NOT pre-authorised (has invalid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API with invalid APF ID at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{APF_ID_NOT_VALID}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API with invalid APF ID</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Publisher not existing\".</li> <li>cause with message \"Publisher id not found\".</li> </ul> </li> </ol> </li> <li> <p>Service API is NOT stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-3-retrieve-all-apis-published-by-authorised-apfid","title":"Test Case 3: Retrieve all APIs Published by Authorised apfId","text":"<p>Test ID: capif_api_publish_service-3</p> <p>Description:</p> <p>This test case will check that an API Publisher can Retrieve all API published</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>At least 2 service APIs are published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Publish Other Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve all published APIs:</p> <ul> <li>Send Get to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API service_1</li> <li>Retrieve {apiId1} from body and Location header with new resource created from response</li> <li>Publish Service API service_2</li> <li>Retrieve {apiId2} from body and Location header with new resource created from response</li> <li>Retrieve All published APIs and check if both are present.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to service 1 Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId1}</li> </ol> </li> <li> <p>Response to service 2 Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId2}</li> </ol> </li> <li> <p>Published Service APIs are stored in CAPIF Database</p> </li> <li> <p>Response to Retrieve all published APIs:</p> <ol> <li>200 OK</li> <li>Response body must return an array of ServiceAPIDescription data.</li> <li>Array must contain all previously published APIs.</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-4-retrieve-all-apis-published-by-non-authorised-apfid","title":"Test Case 4: Retrieve all APIs Published by NON Authorised apfId","text":"<p>Test ID: capif_api_publish_service-4</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Retrieve API published when apfId is not authorised </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is NOT pre-authorised (has invalid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Retrieve all published APIs:</p> <ul> <li>Send Get to https://{CAPIF_HOSTNAME}/published-apis/v1/{APF_ID_NOT_VALID}/service-apis</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Retrieve All published APIs</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>401 Non Authorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Provider not existing\".</li> <li>cause with message \"Provider id not found\".</li> </ul> </li> </ol> </li> <li> <p>Service API is NOT stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-5-retrieve-single-apis-published-by-authorised-apfid","title":"Test Case 5: Retrieve single APIs Published by Authorised apfId","text":"<p>Test ID: capif_api_publish_service-5</p> <p>Description:</p> <p>This test case will check that an API Publisher can Retrieve API published one by one</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>At least 2 service APIs are published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Publish Other Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_2</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve service_1 published APIs detail:</p> <ul> <li>Send Get to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{apiId1}</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve service_2 published APIs detail:</p> <ul> <li>Send Get to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{apiId2}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API service_1.</li> <li>Retrieve {apiId1} from body and Location header with new resource created from response.</li> <li>Publish Service API service_2.</li> <li>Retrieve {apiId2} from body and Location header with new resource created from response.</li> <li>Retrieve service_1 API Detail.</li> <li>Retrieve service_2 API Detail.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to service 1 Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId1}</li> </ol> </li> <li> <p>Response to service 2 Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId2}</li> </ol> </li> <li> <p>Published Service APIs are stored in CAPIF Database</p> </li> <li> <p>Response to Retrieve service_1 published API using apiId1:</p> <ol> <li>200 OK</li> <li>Response body must return a ServiceAPIDescription data.</li> <li>Array must contain same information than service_1 published registration response.</li> </ol> </li> <li> <p>Response to Retrieve service_2 published API using apiId2:</p> <ol> <li>200 OK</li> <li>Response body must return a ServiceAPIDescription data.</li> <li>Array must contain same information than service_2 published registration response.</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-6-retrieve-single-apis-non-published-by-authorised-apfid","title":"Test Case 6: Retrieve single APIs non Published by Authorised apfId","text":"<p>Test ID: capif_api_publish_service-6</p> <p>Description:</p> <p>This test case will check that an API Publisher try to get detail of not published api.</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>No published api</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration</li> <li>Retrieve not published APIs detail:<ul> <li>Send Get to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID_NOT_VALID}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Retrieve not published API Detail.</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Retrieve for NOT published API must accomplish:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Service API not found\".</li> <li>cause with message \"No Service with specific credentials exists\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-7-retrieve-single-apis-published-by-non-authorised-apfid","title":"Test Case 7: Retrieve single APIs Published by NON Authorised apfId","text":"<p>Test ID: capif_api_publish_service-7</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Retrieve detailed API published when apfId is not authorised </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is NOT pre-authorised (has invalid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve detailed published APIs:</p> <ul> <li>Send Get to https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/${apiId}</li> <li>Use Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API at CCF</li> <li>Retrieve {apiId} from body and Location header with new resource created from response.</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Invoker Certificate</li> <li>Retrieve detailed published API acting as Invoker</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Retrieve Detailed published API acting as Invoker must accomplish:</p> <ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"User not authorized\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> <li> <p>Service API is NOT stored in CAPIF Database</p> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-8-update-api-published-by-authorised-apfid-with-valid-serviceapiid","title":"Test Case 8: Update API Published by Authorised apfId with valid serviceApiId","text":"<p>Test ID: capif_api_publish_service-8</p> <p>Description:</p> <p>This test case will check that an API Publisher can Update published API with a valid serviceApiId </p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> <li>A service APIs is published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>get resource url from location Header.</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Update published API at CCF:</p> <ul> <li>Send PUT to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>body service api description with overrided apiName to service_1_modified</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Retrieve detail of service API:</p> <ul> <li>Send Get to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>check apiName is service_1_modified</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API</li> <li>Retrieve {apiId} from body and Location header with new resource url created from response</li> <li>Update published Service API.</li> <li>Retrieve detail of Service API</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Update Published Service API:</p> <ol> <li>200 OK</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiName service_1_modified</li> </ul> </li> </ol> </li> <li> <p>Response to Retrieve detail of Service API:</p> <ol> <li>200 OK</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiName service_1_modified.</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-9-update-apis-published-by-authorised-apfid-with-invalid-serviceapiid","title":"Test Case 9: Update APIs Published by Authorised apfId with invalid serviceApiId","text":"<p>Test ID: capif_api_publish_service-9</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Update published API with a invalid serviceApiId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Update published API at CCF:</p> <ul> <li>Send PUT to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID_NOT_VALID}</li> <li>body service api description with overrided apiName to service_1_modified</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Update published Service API.</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Response to Update Published Service API:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Service API not found\".</li> <li>cause with message \"Service API id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-10-update-apis-published-by-non-authorised-apfid","title":"Test Case 10: Update APIs Published by NON Authorised apfId","text":"<p>Test ID: capif_api_publish_service-10</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Update API published when apfId is not authorised</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is NOT pre-authorised (has invalid apfId from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Update published API at CCF:</p> <ul> <li>Send PUT to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> <li>body service api description with overrided apiName to service_1_modified</li> <li>Use invoker certificate</li> </ul> </li> <li> <p>Retrieve detail of service API:</p> <ul> <li>Send Get to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>check apiName is service_1</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API at CCF</li> <li>Retrieve {apiId} from body and Location header with new resource created from response.</li> <li>Register and onboard Invoker at CCF</li> <li>Store signed Invoker Certificate</li> <li>Update published API at CCF as Invoker</li> <li>Retrieve detail of Service API as publisher</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Update published API acting as Invoker must accomplish:</p> <ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"User not authorized\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> <li> <p>Response to Retrieve Detail of Service API:</p> <ol> <li>200 OK</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiName service_1.</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-11-delete-api-published-by-authorised-apfid-with-valid-serviceapiid","title":"Test Case 11: Delete API Published by Authorised apfId with valid serviceApiId","text":"<p>Test ID: capif_api_publish_service-11</p> <p>Description:</p> <p>This test case will check that an API Publisher can Delete published API with a valid serviceApiId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority).</li> <li>A service APIs is published.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Remove published Service API at CCF:</p> <ul> <li>Send DELETE to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> <li>Use APF Certificate</li> </ul> </li> <li>Retrieve detail of service API:<ul> <li>Send Get to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{serivceApiId}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Publish Service API</li> <li>Retrieve {apiId} from body and Location header with new resource created from response</li> <li>Remove published API at CCF</li> <li>Try to retreive deleted service API from CCF</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Response to Publish request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow ServiceAPIDescription data structure with:<ul> <li>apiId</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/published-apis/v1/{apfId}/service-apis/{serviceApiId}</li> </ol> </li> <li> <p>Published Service API is stored in CAPIF Database</p> </li> <li> <p>Response to Remove published Service API at CCF:</p> <ol> <li>204 No Content</li> </ol> </li> <li> <p>Response to Retrieve for DELETED published API must accomplish:</p> <ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Service API not found\".</li> <li>cause with message \"No Service with specific credentials exists\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-12-delete-apis-published-by-authorised-apfid-with-invalid-serviceapiid","title":"Test Case 12: Delete APIs Published by Authorised apfId with invalid serviceApiId","text":"<p>Test ID: capif_api_publish_service-12</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Delete with invalid serviceApiId</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority).</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Remove published Service API at CCF with invalid serviceId:</p> <ul> <li>Send DELETE to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID_NOT_VALID}</li> <li>Use APF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Remove published API at CCF with invalid serviceId</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Remove published Service API at CCF:<ol> <li>404 Not Found</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Service API not found\".</li> <li>cause with message \"Service API id not found\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_publish_service/#test-case-13-delete-apis-published-by-non-authorised-apfid","title":"Test Case 13: Delete APIs Published by NON Authorised apfId","text":"<p>Test ID: capif_api_publish_service-12</p> <p>Description:</p> <p>This test case will check that an API Publisher cannot Delete API published when apfId is not authorised</p> <p>Pre-Conditions:</p> <ul> <li>CAPIF subscriber is pre-authorised (has valid apfId from CAPIF Authority).</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body service api description with apiName service_1</li> <li>Get apiId</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Remove published Service API at CCF with invalid serviceId as Invoker:</p> <ul> <li>Send DELETE to resource URL https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis/{SERVICE_API_ID_NOT_VALID}</li> <li>Use invoker certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF and store certificates.</li> <li>Register Invoker and onboard Invoker at CCF</li> <li>Remove published API at CCF with invalid serviceId as Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Remove published Service API at CCF:<ol> <li>401 Unauthorized</li> <li>Error Response Body must accomplish with ProblemDetails data structure with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"User not authorized\".</li> <li>cause with message \"Certificate not authorized\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/","title":"Test Plan for CAPIF Api Security Service","text":"<p>At this documentation you will have all information and related files and examples of test plan for this API.</p>"},{"location":"testing/testplan/api_security_service/#test-case-1-create-a-security-context-for-an-api-invoker","title":"Test Case 1: Create a security context for an API invoker","text":"<p>Test ID: capif_security_api-1</p> <p>Description:</p> <p>This test case will check that an API Invoker can create a Security context</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Invoker Onboarding</li> <li>Create Security Context for this Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> </ol> <p>Expected Result:</p> <ol> <li>Create security context:<ol> <li>201 Created response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> <li>Location Header must contain the new resource URL {apiRoot}/capif-security/v1/trustedInvokers/{apiInvokerId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-2-create-a-security-context-for-an-api-invoker-with-provider-role","title":"Test Case 2: Create a security context for an API invoker with Provider role","text":"<p>Test ID:: capif_security_api-2</p> <p>Description:</p> <p>This test case will check that an Provider cannot create a Security context with valid apiInvokerId.</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID), but user that create Security Context with Provider role</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker but using Provider certificate.</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Create security context using Provider certificate:</p> <ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be invoker\".</li> </ul> </li> </ol> </li> <li> <p>No context stored at DB</p> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-3-create-a-security-context-for-an-api-invoker-with-provider-entity-role-and-invalid-apiinvokerid","title":"Test Case 3: Create a security context for an API invoker with Provider entity role and invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-3</p> <p>Description:</p> <p>This test case will check that an Provider cannot create a Security context with invalid apiInvokerID.</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID), but user that create Security Context with Provider role</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Create Security Context for this not valid apiInvokerId and using Provider certificate.</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>body service security body</li> <li>Using AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> </ol> <p>Expected Result:</p> <ol> <li>Create security context using Provider certificate:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be invoker\".</li> </ul> </li> </ol> </li> <li>No context stored at DB</li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-4-create-a-security-context-for-an-api-invoker-with-invoker-entity-role-and-invalid-apiinvokerid","title":"Test Case 4: Create a security context for an API invoker with Invoker entity role and invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-4</p> <p>Description:</p> <p>This test case will check that an Invoker cannot create a Security context with valid apiInvokerId.</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID), but user that create Security Context with invalid apiInvokerId</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>body service security body</li> <li>Use Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Create Security Context using Provider certificate</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Create security context using Provider certificate:</p> <ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> <li> <p>No context stored at DB</p> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-5-retrieve-the-security-context-of-an-api-invoker","title":"Test Case 5: Retrieve the Security Context of an API Invoker","text":"<p>Test ID:: capif_security_api-5</p> <p>Description:</p> <p>This test case will check that an provider can retrieve the Security context of an API Invoker</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority) and API Invoker has created a valid Security Context</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker.</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker certificate</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using AEF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> <li>Retrieve Security Context by Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Retrieve security context:<ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-6-retrieve-the-security-context-of-an-api-invoker-with-invalid-apiinvokerid","title":"Test Case 6: Retrieve the Security Context of an API Invoker with invalid apiInvokerID","text":"<p>Test ID:: capif_security_api-6</p> <p>Description:</p> <p>This test case will check that an provider can retrieve the Security context of an API Invoker</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority) and API Invoker has created a valid Security Context</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Retrieve Security Context of invalid Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> <li>Retrieve Security Context by Provider of invalid invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Retrieve security context:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-7-retrieve-the-security-context-of-an-api-invoker-with-invalid-apfid","title":"Test Case 7: Retrieve the Security Context of an API Invoker with invalid apfId","text":"<p>Test ID:: capif_security_api-7</p> <p>Description:</p> <p>This test case will check that an Provider cannot retrieve the Security context of an API Invoker without valid apfId</p> <p>Pre-Conditions:</p> <ul> <li>API Exposure Function is not pre-authorised (has invalid apfId)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate</li> </ul> </li> <li> <p>Retrieve Security Context as Invoker role:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using Invoker Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Store signed Certificate</li> <li>Create Security Context</li> <li>Retrieve Security Context as Provider.</li> </ol> <p>Expected Result:</p> <ol> <li>Create security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be aef\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-8-delete-the-security-context-of-an-api-invoker","title":"Test Case 8: Delete the Security Context of an API Invoker","text":"<p>Test ID:: capif_security_api-8</p> <p>Description:</p> <p>This test case will check that an Provider can delete a Security context</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority) and API Invoker has created a valid Security Context</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker but using Provider certificate.</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using AEF certificate</li> </ul> </li> <li> <p>Delete Security Context of Invoker by Provider:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Use AEF certificate</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using AEF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> <li>Delete Security Context by Provider</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Delete security context:</p> <ol> <li>204 No Content response.</li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Security context not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-9-delete-the-security-context-of-an-api-invoker-with-invoker-entity-role","title":"Test Case 9: Delete the Security Context of an API Invoker with Invoker entity role","text":"<p>Test ID:: capif_security_api-9</p> <p>Description:</p> <p>This test case will check that an Invoker cannot delete a Security context</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority) and API Invoker has created a valid Security Context</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker certificate</li> </ul> </li> <li> <p>Delete Security Context of Invoker:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Use Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Create Security Context using Provider certificate</li> <li>Delete Security Context by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Delete security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be aef\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-10-delete-the-security-context-of-an-api-invoker-with-invoker-entity-role-and-invalid-apiinvokerid","title":"Test Case 10: Delete the Security Context of an API Invoker with Invoker entity role and invalid apiInvokerID","text":"<p>Test ID:: capif_security_api-10</p> <p>Description:</p> <p>This test case will check that an Invoker cannot delete a Security context with invalid </p> <p>Pre-Conditions:</p> <ul> <li>Invoker is pre-authorised.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Invoker Onboarding</p> </li> <li> <p>Delete Security Context of Invoker:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>Use Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Delete Security Context by invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Delete security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be aef\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-11-delete-the-security-context-of-an-api-invoker-with-invalid-apiinvokerid","title":"Test Case 11: Delete the Security Context of an API Invoker with invalid apiInvokerID","text":"<p>Test ID:: capif_security_api-11</p> <p>Description:</p> <p>This test case will check that an Provider cannot delete a Security context of invalid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>Provider is pre-authorised (has valid apfId from CAPIF Authority).</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Delete Security Context of Invoker by Provider:</p> <ul> <li>Send DELETE https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}</li> <li>Use AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Delete Security Context by provider</li> </ol> <p>Expected Result:</p> <ol> <li>Retrieve security context:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-12-update-the-security-context-of-an-api-invoker","title":"Test Case 12: Update the Security Context of an API Invoker","text":"<p>Test ID:: capif_security_api-12</p> <p>Description:</p> <p>This test case will check that an API Invoker can update a Security context</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Update Security Context of Invoker:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/update</li> <li>body service security body but with notification destination modified to http://robot.testing2</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context By Invoker</li> <li>Update Security Context By Invoker</li> <li>Retrieve Security Context By Provider</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Update security context:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.</li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.<ol> <li>Check is this returned object match with modified one.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-13-update-the-security-context-of-an-api-invoker-with-provider-entity-role","title":"Test Case 13: Update the Security Context of an API Invoker with Provider entity role","text":"<p>Test ID:: capif_security_api-13</p> <p>Description:</p> <p>This test case will check that an Provider cannot update a Security context</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized.</li> <li>Invoker has created the Security Context previously.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Update Security Context of Invoker by Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/update</li> <li>body service security body but with notification destination modified to http://robot.testing2</li> <li>Using AEF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context</li> <li>Update Security Context as Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Update security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be invoker\". </li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-14-update-the-security-context-of-an-api-invoker-with-aef-entity-role-and-invalid-apiinvokerid","title":"Test Case 14: Update the Security Context of an API Invoker with AEF entity role and invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-14</p> <p>Description:</p> <p>This test case will check that an Provider cannot update a Security context of invalid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized.</li> <li>Invoker has created the Security Context previously.</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration</p> </li> <li> <p>Update Security Context of Invoker by Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}/update</li> <li>body service security body</li> <li>Using AEF Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF</li> <li>Update Security Context as Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Update security context:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be invoker\". </li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-15-update-the-security-context-of-an-api-invoker-with-invalid-apiinvokerid","title":"Test Case 15: Update the Security Context of an API Invoker with invalid apiInvokerID","text":"<p>Test ID:: capif_security_api-15</p> <p>Description:</p> <p>This test case will check that an API Invoker cannot update a Security context not valid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority)</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Update Security Context of Invoker:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}/update</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Update Security Context</li> </ol> <p>Expected Result:</p> <ol> <li>Retrieve security context:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-16-revoke-the-authorization-of-the-api-invoker-for-apis","title":"Test Case 16: Revoke the authorization of the API invoker for APIs.","text":"<p>Test ID:: capif_security_api-16</p> <p>Description:</p> <p>This test case will check that a Provider can revoke the authorization for APIs</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context By Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate</li> </ul> </li> <li> <p>Revoke Authorization by Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/delete</li> <li>body security notification body</li> <li>Using AEF Certificate.</li> </ul> </li> <li> <p>Retrieve Security Context by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context by Invoker</li> <li>Revoke Security Context by Provider</li> <li>Retrieve Security Context by Provider</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Revoke Authorization:</p> <ol> <li>204 No Content response.</li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Security context not found\".</li> <li>cause with message \"API Invoker has no security context\".</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-17-revoke-the-authorization-of-the-api-invoker-for-apis-without-valid-apfid","title":"Test Case 17: Revoke the authorization of the API invoker for APIs without valid apfID.","text":"<p>Test ID:: capif_security_api-17</p> <p>Description:</p> <p>This test case will check that an Invoker can't revoke the authorization for APIs</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Revoke Authorization by invoker:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}/delete</li> <li>body security notification body</li> <li>Using Invoker Certificate</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>Using Provider Certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context</li> <li>Revoke Security Context by invoker</li> <li>Retrieve Security Context</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Revoke Security Context by invoker:</p> <ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 401</li> <li>title with message \"Unauthorized\"</li> <li>detail with message \"Role not authorized for this API route\".</li> <li>cause with message \"User role must be provider\". </li> </ul> </li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.<ol> <li>Check is this returned object match with created one.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-18-revoke-the-authorization-of-the-api-invoker-for-apis-with-invalid-apiinvokerid","title":"Test Case 18: Revoke the authorization of the API invoker for APIs with invalid apiInvokerId.","text":"<p>Test ID:: capif_security_api-18</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot revoke the authorization for APIs for invalid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Create Security Context for this Invoker:</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> </ul> </li> <li> <p>Revoke Authorization by Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/trustedInvokers/{API_INVOKER_NOT_VALID}/delete</li> <li>body security notification body</li> <li>Using AEF Certificate.</li> </ul> </li> <li> <p>Retrieve Security Context of Invoker by Provider:</p> <ul> <li>Send GET https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}?authenticationInfo=true&authorizationInfo=true</li> <li>This request will ask with parameter to retrieve authenticationInfo and authorizationInfo</li> <li>Using AEF Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register and onboard Invoker at CCF</li> <li>Register Provider at CCF</li> <li>Create Security Context</li> <li>Revoke Security Context by Provider</li> <li>Retrieve Security Context</li> </ol> <p>Expected Result:</p> <ol> <li> <p>Revoke Security Context by invoker:</p> <ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails data structure, with:<ul> <li>status 404</li> <li>title with message \"Not Found\"</li> <li>detail with message \"Invoker not found\".</li> <li>cause with message \"API Invoker not exists or invalid ID\".</li> </ul> </li> </ol> </li> <li> <p>Retrieve security context:</p> <ol> <li>200 OK response.</li> <li>body returned must accomplish ServiceSecurity data structure.<ol> <li>Check is this return one object that match with created one.</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-19-retrieve-access-token","title":"Test Case 19: Retrieve access token","text":"<p>Test ID:: capif_security_api-19</p> <p>Description:</p> <p>This test case will check that an API Invoker can retrieve a security access token OAuth 2.0.</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerId)</li> <li>Service API of Provider is published</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token:</li> <li>body access token req body and example example</li> <li>securityId is apiInvokerId.</li> <li>grant_type=client_credentials.</li> <li>Create Scope properly for request: 3gpp#{aef_id}:{api_name}</li> <li>Using Invoker Certificate.</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>200 OK</li> <li>body must follow AccessTokenRsp with:<ol> <li>access_token present</li> <li>token_type=Bearer</li> </ol> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-20-retrieve-access-token-by-provider","title":"Test Case 20: Retrieve access token by Provider","text":"<p>Test ID:: capif_security_api-20</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot revoke the authorization for APIs for invalid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerID from CAPIF Authority) and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by provider:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token:</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>Using AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error unauthorized_client</li> <li>error_description=Role not authorized for this API route</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-21-retrieve-access-token-by-provider-with-invalid-apiinvokerid","title":"Test Case 21: Retrieve access token by Provider with invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-21</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token without valid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by provider:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{API_INVOKER_NOT_VALID}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>Using AEF certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Provider</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>401 Unauthorized response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error unauthorized_client</li> <li>error_description=Role not authorized for this API route</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-22-retrieve-access-token-with-invalid-apiinvokerid","title":"Test Case 22: Retrieve access token with invalid apiInvokerId","text":"<p>Test ID:: capif_security_api-22</p> <p>Description:</p> <p>This test case will check that an API Invoker can't retrieve a security access token without valid apiInvokerId</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised (has valid apiInvokerId)</li> </ul> <p>Information of Test:</p> <ol> <li>Perform Provider Registration and Invoker Onboarding</li> <li>Publish Service API at CCF:<ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li>Request Discover Published APIs not filtered:<ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li>Create Security Context for this Invoker<ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li>Request Access Token by invoker:<ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{API_INVOKER_NOT_VALID}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>404 Not Found response.</li> <li>body returned must accomplish ProblemDetails29571 data structure, with:<ul> <li>status 404</li> <li>title Not Found</li> <li>detail Security context not found</li> <li>cause API Invoker has no security context</li> </ul> </li> </ol> </li> </ol> <p>NOTE: ProblemDetails29571 is the definition present for this request at swagger of ProblemDetails, and this is different from definition of ProblemDetails across other CAPIF Services</p>"},{"location":"testing/testplan/api_security_service/#test-case-23-retrieve-access-token-with-invalid-client_id","title":"Test Case 23: Retrieve access token with invalid client_id","text":"<p>Test ID:: capif_security_api-23</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token without valid client_id at body</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>client_id is not-valid </li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error invalid_client</li> <li>error_description=Client Id not found</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-24-retrieve-access-token-with-unsupported-grant_type","title":"Test Case 24: Retrieve access token with unsupported grant_type","text":"<p>Test ID:: capif_security_api-24</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token with unsupported grant_type</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=not_valid</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error unsupported_grant_type</li> <li>error_description=Invalid value for <code>grant_type</code> \\(${grant_type}\\), must be one of \\['client_credentials'\\] - 'grant_type'</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-25-retrieve-access-token-with-invalid-scope","title":"Test Case 25: Retrieve access token with invalid scope","text":"<p>Test ID:: capif_security_api-25</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token with complete invalid scope</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>scope=not-valid-scope</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error invalid_scope</li> <li>error_description=The first characters must be '3gpp'</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-26-retrieve-access-token-with-invalid-aefid-at-scope","title":"Test Case 26: Retrieve access token with invalid aefid at scope","text":"<p>Test ID:: capif_security_api-26</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token with invalid aefId at scope</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>scope=3gpp#1234:service_1</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error invalid_scope</li> <li>error_description=One of aef_id not belongs of your security context</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/api_security_service/#test-case-27-retrieve-access-token-with-invalid-apiname-at-scope","title":"Test Case 27: Retrieve access token with invalid apiName at scope","text":"<p>Test ID:: capif_security_api-27</p> <p>Description:</p> <p>This test case will check that an API Exposure Function cannot retrieve a security access token with invalid apiName at scope</p> <p>Pre-Conditions:</p> <ul> <li>API Invoker is pre-authorised and Provider is also authorized</li> </ul> <p>Information of Test:</p> <ol> <li> <p>Perform Provider Registration and Invoker Onboarding</p> </li> <li> <p>Publish Service API at CCF:</p> <ul> <li>Send Post to ccf_publish_url https://{CAPIF_HOSTNAME}/published-apis/v1/{apfId}/service-apis</li> <li>body [service api description] with apiName service_1</li> <li>Use APF Certificate</li> </ul> </li> <li> <p>Request Discover Published APIs not filtered:</p> <ul> <li>Send GET to ccf_discover_url https://{CAPIF_HOSTNAME}/service-apis/v1/allServiceAPIs?api-invoker-id={apiInvokerId}</li> <li>Param api-invoker-id is mandatory</li> <li>Using invoker certificate</li> </ul> </li> <li> <p>Create Security Context for this Invoker</p> <ul> <li>Send PUT https://{CAPIF_HOSTNAME}/trustedInvokers/{apiInvokerId}</li> <li>body service security body</li> <li>Using Invoker Certificate.</li> <li>Create Security Information Body with one securityInfo for each aef present at each serviceAPIDescription present at Discover.</li> </ul> </li> <li> <p>Request Access Token by invoker:</p> <ul> <li>Sent POST https://{CAPIF_HOSTNAME}/securities/{securityId}/token.</li> <li>body access token req body</li> <li>securityId is apiInvokerId</li> <li>grant_type=client_credentials</li> <li>scope=3gpp#{aef_id}:not-valid</li> <li>Using Invoker certificate</li> </ul> </li> </ol> <p>Execution Steps:</p> <ol> <li>Register Provider at CCF, store certificates and Publish Service API service_1 at CCF</li> <li>Register and onboard Invoker at CCF</li> <li>Discover Service APIs by Invoker.</li> <li>Create Security Context According to Service APIs discovered.</li> <li>Request Access Token by Invoker</li> </ol> <p>Expected Result:</p> <ol> <li>Response to Request of Access Token:<ol> <li>400 Bad Request response.</li> <li>body returned must accomplish AccessTokenErr data structure, with:<ul> <li>error invalid_scope</li> <li>error_description=One of the api names does not exist or is not associated with the aef id provided</li> </ul> </li> </ol> </li> </ol>"},{"location":"testing/testplan/common_operations/","title":"Common Operations","text":""},{"location":"testing/testplan/common_operations/#register-new-user","title":"Register new user","text":"<p>In order to use OpenCAPIF we must add a new user. This new user can onboard/register any Invokers or Providers.</p> <p>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.</p> <p>The steps to register a new user at Register Service are:</p>"},{"location":"testing/testplan/common_operations/#admin-create-user","title":"Admin create User","text":"<p>1) Login as Admin to get access_token:</p> <ul> <li>Send POST to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/login<ul> <li>Include basic Auth Header with Admin credentials</li> </ul> </li> <li>Get access_token and refresh_token from response</li> </ul> <p></p> <p>2) Create User:</p> <ul> <li>Send POST to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/createUser<ul> <li>Include Admin access_token in Authorization Bearer Header</li> <li>Body user_registration_body</li> </ul> </li> </ul> <p></p>"},{"location":"testing/testplan/common_operations/#user-retrieve-access-token-and-other-information","title":"User Retrieve access token and other information","text":"<p>1) Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth<ul> <li>Include basic Auth Header with User credentials</li> </ul> </li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> <p></p>"},{"location":"testing/testplan/common_operations/#onboard-an-invoker","title":"Onboard an Invoker","text":""},{"location":"testing/testplan/common_operations/#steps-to-perform-operation","title":"Steps to perform operation","text":"<p>Preconditions: The administrator must have previously registered the User.</p> <ol> <li>Create public and private key at invoker</li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Onboard Invoker: </p> <ul> <li>Send POST to https://{CAPIF_HOSTNAME}/api-invoker-management/v1/onboardedInvokers</li> <li>Reference Request Body: invoker onboarding body</li> <li>\"onboardingInformation\"->\"apiInvokerPublicKey\": must contain public key generated by Invoker.</li> <li>Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token})</li> </ul> </li> </ol>"},{"location":"testing/testplan/common_operations/#checks-to-ensure-onboarding","title":"Checks to ensure onboarding","text":"<ol> <li> <p>Response to Get Auth:</p> <ol> <li>200 OK</li> <li>access_token returned.</li> </ol> </li> <li> <p>Response to Onboard request must accomplish:</p> <ol> <li>201 Created</li> <li>Response Body must follow APIInvokerEnrolmentDetails data structure with:<ul> <li>apiInvokerId</li> <li>onboardingInformation->apiInvokerCertificate must contain the public key signed.</li> </ul> </li> <li>Response Header Location must be received with URI to new resource created, following this structure: {apiRoot}/api-invoker-management/{apiVersion}/onboardedInvokers/{onboardingId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/common_operations/#example-flow","title":"Example Flow","text":""},{"location":"testing/testplan/common_operations/#register-a-provider","title":"Register a Provider","text":""},{"location":"testing/testplan/common_operations/#steps-to-perform-operation_1","title":"Steps to Perform operation","text":"<ol> <li>Create public and private key at provider for provider itself and each function (apf, aef and amf)</li> <li> <p>Retrieve access_token by User:</p> <ul> <li>Send GET to https://${CAPIF_REGISTER}:${CAPIF_REGISTER_PORT}/getauth</li> <li>Include basic Auth Header with Admin user/password</li> <li>Retrieve access_token and the urls needed for next requests from response body user_getauth_response_body_example</li> </ul> </li> <li> <p>Register Provider:</p> <ul> <li>Send POST https://{CAPIF_HOSTNAME}/api-provider-management/v1/registrations</li> <li>body provider request body</li> <li>Send at Authorization Header the Bearer access_token obtained previously (Authorization:Bearer ${access_token})</li> <li>Store each cert in a file with according name.</li> </ul> </li> </ol>"},{"location":"testing/testplan/common_operations/#checks-to-ensure-provider-registration","title":"Checks to ensure provider registration","text":"<ol> <li> <p>Response to Register:</p> <ol> <li>201 Created</li> </ol> </li> <li> <p>Response to Get Auth:</p> <ol> <li>200 OK</li> <li>access_token returned.</li> </ol> </li> <li> <p>Register Provider at Provider Management:</p> <ol> <li>201 Created response.</li> <li>body returned must accomplish APIProviderEnrolmentDetails data structure.</li> <li>For each apiProvFuncs, we must check:<ol> <li>apiProvFuncId is set</li> <li>apiProvCert under regInfo is set properly</li> </ol> </li> <li>Location Header must contain the new resource URL {apiRoot}/api-provider-management/v1/registrations/{registrationId}</li> </ol> </li> </ol>"},{"location":"testing/testplan/common_operations/#example-flow_1","title":"Example Flow","text":""}]} \ No newline at end of file diff --git a/public/develop/sitemap.xml b/public/develop/sitemap.xml index d6aa0e8a..93d528dd 100644 --- a/public/develop/sitemap.xml +++ b/public/develop/sitemap.xml @@ -2,97 +2,97 @@ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>https://ocf.etsi.org/develop/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/FAQ/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/architecture/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/releasenotes/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/gettingstarted/howtorun/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/gettingstarted/repository/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/postman/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/robotframework/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/api_access_control_policy/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/api_auditing_service/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/api_discover_service/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/api_events_service/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/api_invoker_management/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/api_logging_service/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/api_provider_management/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/api_publish_service/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/api_security_service/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> <url> <loc>https://ocf.etsi.org/develop/testing/testplan/common_operations/</loc> - <lastmod>2024-06-13</lastmod> + <lastmod>2024-06-19</lastmod> <changefreq>daily</changefreq> </url> </urlset> \ No newline at end of file diff --git a/public/develop/sitemap.xml.gz b/public/develop/sitemap.xml.gz index 08f21cb1681e11ed0b22acd48825150a610c66d7..1df63d6805d0d90ef5b3b1b1f6a1b035d2377644 100644 GIT binary patch literal 398 zcmV;90df8xiwFpSA97{_|8r?{Wo=<_E_iKh0M(a0PsA_`hIf9&srxktLI_Z=h=m;q z<|?GVHX@CK?Y7*%$2}0bl?63fk~og_<jr^W@HvFd1jHWWzPK!R#Rj6o*2ivNyuLhb zZ;OZeq8ceM$VQzb?~7g}-5WE{bID#{u!&2g&amsX!sf?lcI91BUu-lj0lrJ8R0VYP z5`{Gkx<d)V{uH8x2?8clnZ@P2hh-VCt*>@h*W2CA_VP|$er#uMuD7v+1Hnh#S|5&; zIkSADm|vDZQ$0OCuawM^>s56pBf&}$2?AJ#hyuJ)#12Gs<+E54Xibl^AdN8(T7rbS z^N@}!<<)s#c#UGpVB^Xu3Exl-WCxg$-d7I0<SDFP(_c)Rd}Eyh=Eh+Z!q6lP-W{_V zz}Wi5+Az@M9puE`dUmMyeva(g7KL*p_i@5^6<tq1+rgm2$ca8+*JZlg6XE3BY?V*r sTkzcHs62xk$;&Y}c89}&vB3mnML$RX@MJw@`pv4p0O#mxs+J7^071pYpa1{> literal 400 zcmV;B0dM{viwFpSHfm-9|8r?{Wo=<_E_iKh0M(bfZtE}%hW9>2;5}A)!NEBPj*Bkc z3$(ML%8V^U7FCkAo3}5W7Dd)Bg{4IjMG>EV-K+ceA#5ff_89lYb-61x5FNHYcKhPx z`EmQVxUa9OkphEk)H(9L=ta_hW9E4-*$WIdaf#F!cD+{Ed>PHI{8!Xh8%;}q&(b+n z0bRXBVGV=sP=c^OhiGAffXP&5ak=baSq5zDo88UbcK2s{eXA~CwhK4c+t|T@;H_@0 z52wmpSUyqAH_MNy9v_}oN@mIRs=AYrV5Nuz0W3pA0bVI$2O_%iSu6>(rpH;3#+U~! zK|<YmNT-$Z>bx(!MlofuapjbRuP6ty1I$RrmBTK13ai)j8`CD=Sm%JbaTtX#Gzo)u zr>q7rwtlfT3^aKMIkC5%9qPTGBfGXm;T*|*obafk>*;4Z7<3pp(Fg3hOqY8ioP3+D u@@aexp8Fh?XK*8VIpxOga2PN)n1HP4=ja!ntmjO>S@j31bJHM}4FCWL8p2-y diff --git a/public/develop/testing/postman/index.html b/public/develop/testing/postman/index.html index 175a35c2..4ed4e444 100644 --- a/public/develop/testing/postman/index.html +++ b/public/develop/testing/postman/index.html @@ -22,7 +22,7 @@ <link rel="icon" href="../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/robotframework/index.html b/public/develop/testing/robotframework/index.html index dbaf9c1d..d9f96987 100644 --- a/public/develop/testing/robotframework/index.html +++ b/public/develop/testing/robotframework/index.html @@ -22,7 +22,7 @@ <link rel="icon" href="../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/api_access_control_policy/index.html b/public/develop/testing/testplan/api_access_control_policy/index.html index 4a843a16..30cf846d 100644 --- a/public/develop/testing/testplan/api_access_control_policy/index.html +++ b/public/develop/testing/testplan/api_access_control_policy/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/api_auditing_service/index.html b/public/develop/testing/testplan/api_auditing_service/index.html index 782f51be..f3501d53 100644 --- a/public/develop/testing/testplan/api_auditing_service/index.html +++ b/public/develop/testing/testplan/api_auditing_service/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/api_discover_service/index.html b/public/develop/testing/testplan/api_discover_service/index.html index d1769776..f4c61117 100644 --- a/public/develop/testing/testplan/api_discover_service/index.html +++ b/public/develop/testing/testplan/api_discover_service/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/api_events_service/index.html b/public/develop/testing/testplan/api_events_service/index.html index 919c9bbc..e59c3590 100644 --- a/public/develop/testing/testplan/api_events_service/index.html +++ b/public/develop/testing/testplan/api_events_service/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/api_invoker_management/index.html b/public/develop/testing/testplan/api_invoker_management/index.html index eae172ec..27b2a66d 100644 --- a/public/develop/testing/testplan/api_invoker_management/index.html +++ b/public/develop/testing/testplan/api_invoker_management/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/api_logging_service/index.html b/public/develop/testing/testplan/api_logging_service/index.html index 4ff1e758..cfe8b207 100644 --- a/public/develop/testing/testplan/api_logging_service/index.html +++ b/public/develop/testing/testplan/api_logging_service/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/api_provider_management/index.html b/public/develop/testing/testplan/api_provider_management/index.html index b657b2c8..d5c111eb 100644 --- a/public/develop/testing/testplan/api_provider_management/index.html +++ b/public/develop/testing/testplan/api_provider_management/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/api_publish_service/index.html b/public/develop/testing/testplan/api_publish_service/index.html index 4868acff..c46abf08 100644 --- a/public/develop/testing/testplan/api_publish_service/index.html +++ b/public/develop/testing/testplan/api_publish_service/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/api_security_service/index.html b/public/develop/testing/testplan/api_security_service/index.html index 8856d61a..c0622050 100644 --- a/public/develop/testing/testplan/api_security_service/index.html +++ b/public/develop/testing/testplan/api_security_service/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/common_operations/index.html b/public/develop/testing/testplan/common_operations/index.html index 5c1ab8c1..17c9ef33 100644 --- a/public/develop/testing/testplan/common_operations/index.html +++ b/public/develop/testing/testplan/common_operations/index.html @@ -18,7 +18,7 @@ <link rel="icon" href="../../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> diff --git a/public/develop/testing/testplan/index.html b/public/develop/testing/testplan/index.html index ddcb5428..ee971820 100644 --- a/public/develop/testing/testplan/index.html +++ b/public/develop/testing/testplan/index.html @@ -22,7 +22,7 @@ <link rel="icon" href="../../images/favicon.png"> - <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.26"> + <meta name="generator" content="mkdocs-1.6.0, mkdocs-material-9.5.27"> -- GitLab