# REST API Hermine's API uses the [Djano REST](https://www.django-rest-framework.org/) framework. :::{warning} Most API endpoints have a trailing slash. If you omit it, you may get a 301 redirect, which may cause problems in case of `POST` requests. ::: ## Authentication via Token The most convenient way to authenticate to the API is probably to use a token. You can get such token by making a POST request on `api/token-auth/` with the fields `username` and `password` in a form-data, using your Hermine credentials. In Python, using [requests](https://docs.python-requests.org/en/latest/), it would look like: ```python import requests HERMINE_HOST = "https://your.hermine.instance.org/" CREDENTIALS = {"username": "admin", "password": "your_admin_pass"} ENDPOINT = "api/token-auth/" url = HERMINE_HOST + ENDPOINT r = requests.post(url, data=CREDENTIALS) print(r.json()) ``` You will get a response like : ```json { "token": "1273e3f6XXXXXXXXXXXXXXXX71209ac3bf29" } ``` This token can then be passed in a HTTP Header to authenticate your API calls: `"Authorization" : "Token 1273e3f6XXXXXXXXXXXXXXXX71209ac3bf29"`. This could look like: ```python import requests HERMINE_HOST = "https://your.hermine.instance.org/" ENDPOINT = "api/releases/" url = HERMINE_HOST + ENDPOINT headers = {"Authorization": "Token 1273e3f6XXXXXXXXXXXXXXXX71209ac3bf29"} r = requests.get(url, headers=headers) ``` ## Uploading a BOM file ### ORT Evaluated model ```{py:function} PUT /api/upload_ort/ Adds the content of the SBOM as EvaluatedModel to a given release :param int release: the id of the release the BOM will be added to :param file ort_file: the evaluated-model.json file generated by ORT :return: the status of the upload ``` A minimal example would be: ```python endpoint = "api/upload_ort/" url = HERMINE_URL + endpoint evmodel_path = "evaluated/evaluated-model.json" ort_file = open(evmodel_path, "rb") data = { "release": 1, } upload_response = requests.put(url, files={"ort_file": ort_file}, data=data, headers=headers) ``` ### SPDX file ```{py:function} PUT /api/upload_spdx/ Adds the content of the SBOM as SPDX to a given release :param int release: the id of the release the BOM will be added to :param file spdx_file: A yaml-SPDX BOM :return: the status of the upload ``` ## Checking the validation steps The validation process of a release is divided in 4 steps. You need to complete them in the right order. When you encounter a result that does not fit requirements to go to next step, you'll need to make the appropriate work inside Hermine UI. Every endpoint has a "valid" field that is set to True if every action that should be done has been done, and False otherwise. ### Step 1 ```{py:function} GET api/releases//validation_1/ Adds the content of the SBOM as SPDX to a given release :param int release: the id of the release the BOM will be added to :return: the status of the upload ``` Can be found at 'api/releases//validation_1/' API endpoint that allows to know if there are components with license information which is not a valid SPDX expression. The response is a dictionary with the following fields : invalid_expressions An array of Version objects for which license expression is empty or invalid. fixed_expressions An array of Version objects for which there is a corrected_license and no spdx_valid_license_expr. ### Step 2 Can be found at 'api/releases/int:release_id/validation_2/' Confirm ANDs operators in SPDX expressions are not poorly registered ORs. ### Step 3 Can be found at 'api/releases/int:release_id/validation_2/' Check that all scopes in the release have a default exploitation mode defined. ### Step 4 Can be found at 'api/releases//validation_4/' API endpoint that allows to know if there are complex license expressions in usages of this release. A complex license expression is an expression with more than one License expressed in it. A choice has to be made, either by keeping the whole expression either by picking the chosen licenses in the expression. The response is a dictionnary with the following fields : to_resolve An array of usages linked to a version that either has a complex "corrected_license" eother a "spdx_valid_license_expr" field, and for which no explicit choice as been made. To make a choice, click on "choose expression" in hermine UI. Pick the desired scope, type the actual expression you'll want to use for this component, and enter an explanation for your choice. This will create a UsageChoice object that is linked to the Usage object. resolved An array of usages linked to a version hat either has a complex "corrected_license" eother a "spdx_valid_license_expr" field, and for which an explicit choice as been made. ### Step 5 Can be found at 'api/releases//validation_5/' API endpoint that allows to know if there are Usages of unnacepted licenes in this release. In this case, you must set relevant derogations in Hermine UI. The response is a dictionnary with the following fields : usages_lic_never_allowed An array of usages containing a license that has been marked as never allowed by the legal team. usages_lic_context_allowed An array of usages containing a license that has been marked as allowed depending on context by the legal team. usages_lic_unknown An array of usages containing a license that still has to be reviewed by the legal team. involved_lic An array containing all the licenses that are red, orange or grey and that need a derogation for this release. derogations An array of the derogations that has been made. ## Example An example ```bash - run : "curl -X PUT https://chantier.hermine-foss.org/api/upload_ort/ -H 'Authorization: Token ${{ secrets.HERMINE_TOKEN }}' -F 'ort_file=@.tortellini/out/evaluated-model.json' -F 'release=1' --silent" if: ${{ success() }} - run : "curl https://chantier.hermine-foss.org/api/releases/1/validation_1/ -H 'Authorization: Token ${{ secrets.HERMINE_TOKEN }}' --output .hermine/validation_1.json --silent" if: ${{ success() }} - run : "curl https://chantier.hermine-foss.org/api/releases/1/validation_2/ -H 'Authorization: Token ${{ secrets.HERMINE_TOKEN }}' --output .hermine/validation_2.json --silent" if: ${{ success() }} - run : "curl https://chantier.hermine-foss.org/api/releases/1/validation_3/ -H 'Authorization: Token ${{ secrets.HERMINE_TOKEN }}' --output .hermine/validation_3.json --silent" if: ${{ success() }} - run : "curl https://chantier.hermine-foss.org/api/releases/1/validation_4/ -H 'Authorization: Token ${{ secrets.HERMINE_TOKEN }}' --output .hermine/validation_4.json --silent" if: ${{ success() }} - run : "curl https://chantier.hermine-foss.org/api/releases/1/validation_4/ -H 'Authorization: Token ${{ secrets.HERMINE_TOKEN }}' --output .hermine/validation_5.json --silent" if: ${{ success() }} - uses: actions/upload-artifact@v2 ``` ## JUnit A JUnit endpoint can be used at `api/releases/int:release_id/junit/`. It returns a testsuite where each validation step is a testcase. Example : ```xml ``` ## Endpoints for generic obligations ```{py:function} GET api/generics/ List generic obligations, optionnaly filtered by license or exploitation. Accept the following filtering parameters to list only obligations triggered by some licenses and usage contexts : :param str spdx: a comma-separated list of SPDX license id :param str exploitation: an exploitation among Usage.EXPLOITATION_CHOICES :param str modification: a modification among Usage.MODIFICATION_CHOICES :param str exploitation: an exploitation among Usage.EXPLOITATION_CHOICES :param str modification: a modification among Usage.MODIFICATION_CHOICES ``` ```{py:function} POST api/generics/sbom/ List generic obligations for a list of components with their licenses and context. :param list packages: a list of package objects with :param str packages.package_id: a arbitrary identifier name (used in the response) :param str packages.spdx: the SPDX identifier for the package license :param str packages.exploitation: an exploitation among Usage.EXPLOITATION_CHOICES :param str packages.modification: a modification among Usage.MODIFICATION_CHOICES :return: a list of generic obligations with a `triggered_by` attributes containing the package identifier which triggered the obligation ``` ### Example ``` POST /api/generics/sbom/ { "packages": [ { "package_id": "foobar", "spdx": "MIT", "exploitation": "DistributionSource", "modificaiton": "Altered" }, { "package_id": "barfoo", "spdx": "Apache-2.0", "exploitation": "DistributionSourceDistributionNonSource", "modificaiton": "Unmodified" } ] } 200 OK [ { "id": 1, "name": "Patent Grant", "description": "", "in_core": false, "metacategory": "", "team": null, "passivity": "", "triggered_by": ["foobar", "barfoo"] }, { "id": 12, "name": "Indemnification of contributors", "description": "", "in_core": false, "metacategory": "", "team": null, "passivity": "", "triggered_by": ["barfoo"] } ] ``` ## Generic API endpoints for Models For the main models of the application, you can get a list of their instances at: `/api/`. For example, the list of your products will be at `api/products/`. A detailed view for an instance of a class can be found at `/api//`. For example: `/api/products/1/`. You can check the list of the endpoints at `/api/`.