We need to create a JSON API for Panopticon to facilitate automations which cannot use the currently available CLI commands. The relevant discussion is here.
Authentication
We will use an API token to authenticate users. The API token is communicated to us in one of the following ways:
- Bearer authentication. An HTTP header
Authentication: Bearer TOKEN_CONTENT
- Custom HTTP header. An HTTP header
X-Panopticon-Token: TOKEN_CONTENT
- GET parameter. A parameter is added to the URL:
_panopticon_token=TOKEN_CONTENT
Bearer authentication is strongly preferred. The first one to exist will be used and the rest (if provided) will be ignored.
Each user can have one or more token records associated with their user account, stored in the database. The database stores an automatically assigned numeric ID for the token, the user ID it corresponds to, an optional description, a randomly generated 64-byte seed stored as base64, whether the token is active, the user ID and date/time the token was created, and the user ID and date/time the token was last modified.
The actual token shown to the user and provided in HTTP requests is calculated as follows:
- The seed is base64 decoded
- The decoded seed is combined with the site's secret (stored in the site configuration config.php or .env as $secret) using HMAC and SHA-256:
$message = hash_hmac('SHA-256', $decodedSeed, $siteSecret)
- The token is created as follows:
base64_encode('SHA-256:' . $userId . ':' . $message)
When a token is received, we try to base64-decode it. We check that it has exactly three colon-separated fields, that the first field is exactly 'SHA-256', and the second field is an integer within PHP's range. We then load all of the enabled tokens for this user ID and create their token representations as described above. We then do a time-safe compare between the provided token and each and every user token we calculated.
If at least one of the tokens match, we log in the respective user ID. Otherwise, we throw an exception with code 403 and message "Access forbidden". To avoid time-based attacks we will do a time-safe string comparison against ALL tokens, resulting in an array of bools telling us the result of each comparison. Then, we will do an array_reduce on that array to see if there is at least one true value.
If there is at least one successful token match, we log the user in. The user must be automatically logged out at the end of the request.
On the user interface side, each user has an API Tokens menu item. Over there they can see the API endpoint of the application (the path to the application plus /api) and a list of their tokens. They can create, enable, disable, and delete tokens.
API Endpoint
The API endpoint of the application is the /api path. We need a route to handle /api/* paths.
In case a .htaccess file is not used or cannot be used, the API endpoint will of course be /index.php/api. Make a note in the documentation and the user interface.
API features
API features are executed under the privileges of the user logged into the application. The token controls who is logged in for the duration of the request.
The features we need to have are:
- Sites (v1/sites and v1/site)
- List the sites the user can access (GET v1/sites)
- Get a site's configuration (GET v1/site/:id)
- Add a site (PUT /v1/site)
- Modify a site (POST v1/site/:id)
- Fix core Joomla update site (POST v1/site/:id/fixjoomlacoreupdate) similar to \Akeeba\Panopticon\Controller\Sites::fixJoomlaCoreUpdateSite
- Refresh site information (POST v1/site/:id/refresh) similar to \Akeeba\Panopticon\Controller\Sites::refreshSiteInformation
- Refresh site extension information (POST v1/site/:id/extensions) similar to \Akeeba\Panopticon\Controller\Sites::refreshExtensionsInformation
- Schedule CMS (Joomla or WordPress) update (POST v1/site/:id/cmsupdate)
- Unschedule CMS update (POST v1/site/:id/cmsupdate/cancel)
- Clear CMS update schedule error (POST v1/site/:id/cmsupdate/clear)
- Clear extension / plugins update schedule error (POST v1/site/:id/extensions/clear) similar to \Akeeba\Panopticon\Controller\Sites::clearExtensionUpdatesScheduleError
- Reset the extension / plugin update queue (POST v1/site/:id/extensions/reset) similar to \Akeeba\Panopticon\Controller\Sites::resetExtensionUpdate
- List site's extensions / plugins (GET v1/site/:id/extensions)
- Schedule extension / plugin update (POST v1/site/:id/extensions/scheduleupdate/:extensionId)
- Unschedule extension / plugin update (POST v1/site/:id/extensions/cancel/:extensionId)
- Get the Download Key for an extension (GET v1/site/:id/extension/:id/downloadkey)
- Apply a new Download Key to an extension (POST v1/site/:id/extension/:id/downloadkey)
- Panopticon configuration (v1/sysconfig)
- List configuration options (GET v1/sysconfig)
- Get a configuration value (GET v1/sysconfig/:paramName)
- Set a configuration value (POST v1/sysconfig/:paramName)
- Task management (v1/tasks and v1/task)
- List tasks (GET v1/tasks)
- Get a task's information (GET v1/task/:id)
- Add a task (PUT v1/task)
- Modify a task (POSTv1/task/:id)
- Self-update (v1/selfupdate) – Requires additional super admin check for all features
- Get update information (GET v1/selfupdate). Optionally force reload information with
?force=1
- Download the update (GET v1/selfupdate/download). Similar to \Akeeba\Panopticon\Controller\Selfupdate::update
- Install the update (GET v1/selfupdate/install). Similar to \Akeeba\Panopticon\Controller\Selfupdate::install
- Post-installation actions (GET v1/selfupdate/postinstall). Similar to \Akeeba\Panopticon\Controller\Selfupdate::postinstall
Documentation
Update the documentation wiki with the information on how to access the API (endpoint, authentication) and separate pages for each API topic (Site management, Panopticon configuration, ...).
We need to create a JSON API for Panopticon to facilitate automations which cannot use the currently available CLI commands. The relevant discussion is here.
Authentication
We will use an API token to authenticate users. The API token is communicated to us in one of the following ways:
Authentication: Bearer TOKEN_CONTENTX-Panopticon-Token: TOKEN_CONTENT_panopticon_token=TOKEN_CONTENTBearer authentication is strongly preferred. The first one to exist will be used and the rest (if provided) will be ignored.
Each user can have one or more token records associated with their user account, stored in the database. The database stores an automatically assigned numeric ID for the token, the user ID it corresponds to, an optional description, a randomly generated 64-byte seed stored as base64, whether the token is active, the user ID and date/time the token was created, and the user ID and date/time the token was last modified.
The actual token shown to the user and provided in HTTP requests is calculated as follows:
$message = hash_hmac('SHA-256', $decodedSeed, $siteSecret)base64_encode('SHA-256:' . $userId . ':' . $message)When a token is received, we try to base64-decode it. We check that it has exactly three colon-separated fields, that the first field is exactly 'SHA-256', and the second field is an integer within PHP's range. We then load all of the enabled tokens for this user ID and create their token representations as described above. We then do a time-safe compare between the provided token and each and every user token we calculated.
If at least one of the tokens match, we log in the respective user ID. Otherwise, we throw an exception with code 403 and message "Access forbidden". To avoid time-based attacks we will do a time-safe string comparison against ALL tokens, resulting in an array of bools telling us the result of each comparison. Then, we will do an array_reduce on that array to see if there is at least one true value.
If there is at least one successful token match, we log the user in. The user must be automatically logged out at the end of the request.
On the user interface side, each user has an API Tokens menu item. Over there they can see the API endpoint of the application (the path to the application plus
/api) and a list of their tokens. They can create, enable, disable, and delete tokens.API Endpoint
The API endpoint of the application is the
/apipath. We need a route to handle/api/*paths.In case a .htaccess file is not used or cannot be used, the API endpoint will of course be
/index.php/api. Make a note in the documentation and the user interface.API features
API features are executed under the privileges of the user logged into the application. The token controls who is logged in for the duration of the request.
The features we need to have are:
?force=1Documentation
Update the documentation wiki with the information on how to access the API (endpoint, authentication) and separate pages for each API topic (Site management, Panopticon configuration, ...).