diff --git a/collect_api_endpoints.in b/collect_api_endpoints.in index fc13bd55d..6f649543c 100644 --- a/collect_api_endpoints.in +++ b/collect_api_endpoints.in @@ -2,10 +2,10 @@ {{ title_underline }} {% for controller in controllers %} .. csv-table:: {{controller.type}} ({{controller.filename}}) - :header: "Method", "Module", "Controller", "Command", "Parameters" - :widths: 4, 15, 15, 30, 40 + :header: "Method", "Module", "Controller", "Command", "Parameters", "Privilege required" + :widths: 4, 15, 15, 30, 40, 40 {% for endpoint in controller.endpoints %} - "``{{endpoint.method}}``","{{endpoint.module}}","{{endpoint.controller}}","{{endpoint.command}}","{{endpoint.parameters}}" + "``{{endpoint.method}}``","{{endpoint.module}}","{{endpoint.controller}}","{{endpoint.command}}","{{endpoint.parameters}}","{{', '.join(endpoint.acl_names)}}" {%- endfor %} {%- if controller.uses %} {% for use in controller.uses %} diff --git a/collect_api_endpoints.py b/collect_api_endpoints.py index ca72bd36e..dc1b28266 100755 --- a/collect_api_endpoints.py +++ b/collect_api_endpoints.py @@ -27,6 +27,7 @@ import os import argparse import re +import xml.etree.ElementTree as ET from jinja2 import Template EXCLUDE_CONTROLLERS = ['Core/Api/FirmwareController.php'] @@ -85,6 +86,19 @@ def parse_api_php(src_filename): if os.path.isfile(model_xml): model_filename = model_xml.replace('//', '/') + acl_names = [] + if len(m) > 0: + app_location = "/".join(src_filename.split('/')[:-5]) + acl_location = "/".join(m[0].replace("\\", "/").split('/')[:-1]) + acl_xml = "%s/models/%s/ACL/ACL.xml" % (app_location, acl_location) + if os.path.isfile(acl_xml): + acl_filename = acl_xml.replace('//', '/') + tree = ET.parse(acl_filename) + for page in tree.findall('*'): + for pattern in page.findall('patterns/pattern'): + if pattern.text in [f"api/{module_name}/{controller}", f"api/{module_name}/*"]: + acl_names += [page.find('name').text] + function_callouts = re.findall(r"(\n\s+(private|public|protected)\s+function\s+(\w+)\((.*)\))", data) result = list() this_commands = [] @@ -106,7 +120,8 @@ def parse_api_php(src_filename): 'command': function[2][:-6], 'parameters': function[3].replace(' ', '').replace('"', '""'), 'filename': base_filename, - 'model_filename': model_filename + 'model_filename': model_filename, + 'acl_names': acl_names } if is_abstract: record['type'] = 'Abstract [non-callable]' @@ -141,7 +156,8 @@ def parse_api_php(src_filename): 'command': item['command'], 'parameters': item['parameters'], 'filename': base_filename, - 'model_filename': model_filename + 'model_filename': model_filename, + 'acl_names': acl_names }) return sorted(result, key=lambda i: i['command']) diff --git a/source/development/api.rst b/source/development/api.rst index e762bf57e..d8654c76e 100644 --- a/source/development/api.rst +++ b/source/development/api.rst @@ -18,6 +18,9 @@ There are two HTTP verbs used in the OPNsense API: The body of the HTTP POST request and response is an 'application/json' object. +Authentication +-------------- + The $key and $secret parameters are used to pass the API credentials using curl. You need to set these parameters with your own API credentials before using them in the examples: .. code-block:: sh @@ -29,6 +32,31 @@ The $key and $secret parameters are used to pass the API credentials using curl. When using Postman to test an API call, use the 'basic auth' authorization type. The $key and $secret parameters go into Username/Password respectively. +Authorization +------------- + +When using the API, the user for which the $key and $secret were issued, may require specific privileges granted to use the API modules and their controllers. +Such privileges, if any, are explicitly mentioned in the API documentation, alongside each method. + +In case of doubts, however, you can grep through the source code. For example, for the `sslh` module, +the privileges can be found in https://github.com/opnsense/plugins/blob/master/net/sslh/src/opnsense/mvc/app/models/OPNsense/Sslh/ACL/ACL.xml (you may need to choose a tag that is relevant to the OPNsense version you use). + +The corresponding privilege name that needs to be granted is denoted by the `` element. For the `sslh` module and "any" controller (as denoted by the "`/*`" wildcard), +it is `Services: SSLH`: + +.. code-block:: xml + + + + Services: SSLH + + ui/sslh/* + api/sslh/* + + + + + Core API --------