Task 04: OpenAPI Spec Enforcement with NGINX App Protect
========================================================

Our API has two endpoints:

- ``https://jobs.local/get-job`` is used to GET a random job title.
- ``https://jobs.local/add-job`` will accept an HTTP POST with a list of job titles in JSON format. For example:

.. code-block:: yaml

   ["Junior Analyst", "Site Reliability Engineer"]
   
In the URL bar of the web browser, connect to the ``https://jobs.local/`` API endpoint.

- Press [F12] to reveal the Firefox Developer Tools
- Select 'Network' and 'Response' just like the screenshot below 
- Press [F5] to create a new HTTP request
- Right-click on any request just like in the screenshot below
- Select 'Edit and Resend'
- On the left-hand side, in the 'New Request' section, select [Clear]
- Fill in the values just like the table below

+------------------------------------------------+
| New Request                                    |
+==============+=================================+
| POST         | https://jobs.local/add-job      |
+--------------+---------------------------------+
| *Headers*                                      |
+--------------+---------------------------------+
| Content-Type | application/json                |
+--------------+---------------------------------+
| *Body*                                         |
+------------------------------------------------+
| ["Professional Skateboarder"]                  |
+------------------------------------------------+

Click [Send]

The **Response** on the right-hand side will be a JSON formatted list of strings representing a list of jobs.
Go ahead and append to the list with more fun jobs. You can send multiple jobs at once: ["job1", "job2", "job3"].

.. image:: images/01_openapi_spec.jpg
  :scale: 50%
  
However, there is a problem with our ``https://jobs.local/add-job`` API endpoint.

Append new values to the list of job titles by POSTing a bad payload to ``https://jobs.local/add-job``. For example, an array of non-string types.

.. code-block:: yaml

   [42, true]

The API will accept anything! We need to enforce the API schema so only valid requests are allowed.

.. image:: images/02_bad_type.jpg
  :scale: 50%

Change to the task_04 directory.

.. code-block:: bash

   cd ../task_04

An `OpenAPI Spec <https://en.wikipedia.org/wiki/OpenAPI_Specification>`_ is a standard that defines the schema of a REST API. In an OpenAPI Spec file, we can define the allowable endpoints (/get-job, /add-job), methods (GET /get-job, POST /add-job) and value types (POST /add-job must be an array of strings). NGINX Plus App Protect can convert OpenAPI Spec files into enforceable Web Appliation Firewall (WAF) policies.

Review the OpenAPI spec file for our /get-job and /add-job API endpoints:

.. code-block:: bash

   bat jobs-openapi-spec.yaml

.. image:: images/03_bat_openapi.jpg
  :scale: 50%

The `jobs-openapi-spec.yaml <https://raw.githubusercontent.com/tmarfil/nginx-api-gateway-for-k8s/main/task_04/jobs-openapi-spec.yaml>`_ is hosted on GitHub.

Create an ``appolicy`` Custom Resource Definition (CRD). NGINX Plus App Protect includes a WAF policy template. You only need to reference your OpenAPI Spec link from this template.

.. code-block:: bash

   bat jobs-openapi-spec-appolicy.yaml

.. image:: images/04_bat_appolicy.jpg
  :scale: 50%

.. code-block:: bash

   k apply -f jobs-openapi-spec-appolicy.yaml

.. image:: images/05_apply_appolicy.jpg
  :scale: 50%

Create a ``policy`` Custom Resource Definition (CRD). The ``policy`` CRD references the ``appolicy`` CRD.

.. code-block:: bash

   bat app-protect-policy.yaml

.. image:: images/06_bat_app-protect.jpg
  :scale: 50%

.. code-block:: bash

   k apply -f app-protect-policy.yaml

Apply the modifed virtualserver CRD. Note how the ``/add-job`` path now has the ``app-protect-policy`` applied.

.. code-block:: bash

   bat VirtualServer.yaml

.. image:: images/07_bat_virtualserver.jpg
  :scale: 50%

.. code-block:: bash

   k apply -f VirtualServer.yaml

Confirm the status of the virtualserver 'my-virtualserver' you just modified.

.. code-block:: bash

   kubectl describe virtualserver my-virtualserver

If you now try to POST a payload that does not conform to the spec, it will be rejected and the supportID will be displayed in the response.

.. image:: images/08_post_rejected.jpg
  :scale: 50%

However, valid POST payloads (array of strings representing job titles) are still allowed.

.. code-block:: yaml

   ["OpenAPI Spec Enforcer"]

.. image:: images/09_post_accepted.jpg
  :scale: 50%