Skip to content

Commit 795a00d

Browse files
committed
DOC Update docs
1 parent 840c9b8 commit 795a00d

File tree

5 files changed

+33
-27
lines changed

5 files changed

+33
-27
lines changed

README.md

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -383,38 +383,41 @@ The `x-csrf-token` header is available as a constant on `RestApiEndpoint::CSRF_T
383383

384384
## API token<a name="readme-api-token"></a>
385385

386-
API's can be setup to allow members to authenticate with an `x-api-token` HTTP header instead of having to log in to the CMS.
386+
Non-public API's can be configured to allow members to authenticate using an HTTP header instead of having to log in to the CMS.
387387

388-
If API authenticate is used, the member will be logged in only for the duration of the request i.e. they will be logged out before the JSON response is returned.
388+
If API authentication is used, the user will be logged in only for the duration of the request i.e. they will be logged out before the JSON response is returned.
389+
390+
This module provides a permission "Use an API token" which is `API_TOKEN_AUTHENTICATION` which must be assigned to a group that users using API tokens must belong to. The endpoints `ALLOW_API_ACCESS` config must be set to `true`.
391+
392+
When a user and endpoint is set up to allow using an API token, pass an `x-api-token` header with the value of the API Token to authenticate. Note that API token authentication will bypass MFA if it was set up for that user.
393+
394+
### Setting up an API user and group using the CMS
395+
396+
#### Creating the API user and group
389397

390-
### Creating groups and users in the CMS
391398
1. Log in to the CMS as an administrator
399+
1. Go to the Security section
392400
1. Create a new group called "API Users"
393-
1. In the Permissions tab (top right), tick "Use an API token" - this is the label for the permission code `API_TOKEN_AUTHENTICATION`
401+
1. Click on the Permissions tab (top right)
402+
1. Tick "Use an API token" - this is the label for the permission code `API_TOKEN_AUTHENTICATION`
394403
1. Save the Group
395404
1. Click "Add Member"
396405
1. Create a new user with a "First name" of "api-user", an Email of "api-user@example.com", and a long random password
397406
1. Assign them to the "Api Users" group
398407
1. Tick the "Generate new API token" checkbox and click "Save"
399408
1. Copy the API token that is generated - you will only be shown this once
400409

401-
### Additional member permissions
402-
1. The "api-user" still needs to pass all necessary permissions checks for the API to work
403-
1. You can either update the "API Users" group to have the necessary permissions, or
404-
1. Set the api end point `RestApiEndoint::CHECK_CAN_METHODS` to `RestApiEndoint::NONE` though if you do this you *MUST* ensure that the API `RestApiEndoint::ACCESS` is set to a permission code, not merely `RestApiEndpoint::LOGGED_IN`.
410+
#### Additional group permissions
411+
412+
The "api-user" still needs to pass all necessary permissions checks for the API to work i.e. so that `canView()` checks still pass. You can either:
413+
- Update the "API Users" group to have the necessary permissions, or
414+
- Set the endpoints `CHECK_CAN_METHODS` to `NONE` though you *MUST* ensure that the API `ACCESS` is set to a permission code that is only assigned to dedicated api users.
405415

406-
### Update endpoint $api_config
407-
1. Set `RestApiEndpoint::ALLOW_API_ACCESS` to `true`
408-
1. Set `RestApiEndpoint::ACCESS` to `ApiTokenPermissionProvider::API_TOKEN_AUTHENTICATION` for the strictest access control (recommended). Note the API can still be accessed without a API token if the member with the "Use an API token" logs in the the CMS.
409-
1. Alternatively, set this to `RestApiEndpoint::LOGGED_IN` to allow any logged in user to use the API, including without an API token, though to use the API member must be in a group with the "Use an API token" permission in order to authenticate
410-
1. Run `dev/build flush=1` for the configuration to take effect
416+
### Programmatically updating a users API token
411417

412-
### Using the API token authentication
413-
1. Pass an `x-api-token` header with the value of the API Token
418+
Programmatically update a users API token with `$member->refreshApiToken();` followed by `$member->write();`. The returned value is the unencrypted API token. The members `ApiToken` field will be the encrypted API token.
414419

415-
### Programatically updating a members ApiToken
416-
1. Call `$member->refreshApiToken()`. The returned value is the unencrypted API token. The members `ApiToken` field will be the encrypted API token.
417-
1. Call `$member->write()`
420+
Note that for newly created users, `$member->write()` must be called at least once before calling `$member->refreshApiToken();` to ensure that the API token is properly encrypted.
418421

419422
## Extension hooks<a name="readme-extension-hooks"></a>
420423

src/Controllers/RestApiEndpoint.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use SilverStripe\Security\Permission;
1515
use stdClass;
1616
use emteknetnz\RestApi\Exceptions\RestApiEndpointConfigException;
17-
use emteknetnz\RestApi\PermissionProviders\ApiTokenPermissionProvider;
1817
use SilverStripe\ORM\DataObjectSchema;
1918
use SilverStripe\ORM\ValidationException;
2019
use SilverStripe\Security\SecurityToken;
@@ -49,6 +48,8 @@ abstract class RestApiEndpoint extends Controller
4948
public const DELIMITER = '_';
5049
public const CREATE_EDIT_DELETE_ACTION = 'CREATE_EDIT_DELETE_ACTION';
5150
public const VIEW_CREATE_EDIT_DELETE_ACTION = 'VIEW_CREATE_EDIT_DELETE_ACTION';
51+
// permissions
52+
public const API_TOKEN_AUTHENTICATION = 'API_TOKEN_AUTHENTICATION';
5253
// other constants
5354
public const CSRF_TOKEN_HEADER = 'x-csrf-token';
5455
public const API_TOKEN_HEADER = 'x-api-token';
@@ -140,7 +141,7 @@ private function authenticateWithApiToken(): void
140141
if ($apiToken) {
141142
$this->currentLoggedInMember = Security::getCurrentUser();
142143
// find members in groups with the API_TOKEN_AUTHENTICATION permission
143-
$groups = Permission::get_groups_by_permission(ApiTokenPermissionProvider::API_TOKEN_AUTHENTICATION);
144+
$groups = Permission::get_groups_by_permission(self::API_TOKEN_AUTHENTICATION);
144145
$members = [];
145146
foreach ($groups as $group) {
146147
foreach ($group->Members() as $member) {

src/Extensions/ApiTokenMemberExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace emteknetnz\RestApi\Extensions;
44

5-
use emteknetnz\RestApi\PermissionProviders\ApiTokenPermissionProvider;
5+
use emteknetnz\RestApi\Controllers\RestApiEndpoint;
66
use SilverStripe\Core\Extension;
77
use SilverStripe\Forms\CheckboxField;
88
use SilverStripe\Forms\FieldList;
@@ -40,7 +40,7 @@ public function updateCMSFields(FieldList $fields): void
4040
// Never show the encrypted API token database field
4141
$fields->removeByName('ApiToken');
4242
// Check the user is allowed to use API tokens
43-
$code = ApiTokenPermissionProvider::API_TOKEN_AUTHENTICATION;
43+
$code = RestApiEndpoint::API_TOKEN_AUTHENTICATION;
4444
if (!Permission::checkMember($member, $code)) {
4545
return;
4646
}

src/PermissionProviders/ApiTokenPermissionProvider.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
namespace emteknetnz\RestApi\PermissionProviders;
44

55
use SilverStripe\Security\PermissionProvider;
6+
use emteknetnz\RestApi\Controllers\RestApiEndpoint;
67

8+
// This exists as a standalone class rather than simply putting it on RestApiEndpoint because that is an abstract
9+
// class which cannot be instantiated and therefore cannot be used as a permission provider.
710
class ApiTokenPermissionProvider implements PermissionProvider
811
{
9-
public const API_TOKEN_AUTHENTICATION = 'API_TOKEN_AUTHENTICATION';
10-
1112
public function providePermissions()
1213
{
1314
return [
14-
self::API_TOKEN_AUTHENTICATION => 'Use an API token',
15+
RestApiEndpoint::API_TOKEN_AUTHENTICATION => 'Use an API token',
1516
];
1617
}
1718
}

tests/Controllers/RestApiEndpointTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use emteknetnz\RestApi\Exceptions\RestApiEndpointConfigException;
1919
use SilverStripe\Security\SecurityToken;
2020
use emteknetnz\RestApi\Tests\Controllers\RestApiTest\TestVersionedExtension;
21-
use emteknetnz\RestApi\PermissionProviders\ApiTokenPermissionProvider;
2221
use SilverStripe\Security\Group;
2322
use SilverStripe\Security\Member;
2423
use SilverStripe\Security\Permission;
@@ -58,6 +57,8 @@ class RestApiEndpointTest extends FunctionalTest
5857
public const DELIMITER = '_';
5958
public const CREATE_EDIT_DELETE_ACTION = 'CREATE_EDIT_DELETE_ACTION';
6059
public const VIEW_CREATE_EDIT_DELETE_ACTION = 'VIEW_CREATE_EDIT_DELETE_ACTION';
60+
// permissions
61+
public const API_TOKEN_AUTHENTICATION = 'API_TOKEN_AUTHENTICATION';
6162
// other consts
6263
public const CSRF_TOKEN_HEADER = 'x-csrf-token';
6364
public const API_TOKEN_HEADER = 'x-api-token';
@@ -372,7 +373,7 @@ public function provideCsrfToken(): array
372373
public function testApiTokenAuthentication(bool $allowApiToken, int $expectedStatusCode)
373374
{
374375
$this->setConfig(self::ALLOW_API_TOKEN, $allowApiToken);
375-
$code = ApiTokenPermissionProvider::API_TOKEN_AUTHENTICATION;
376+
$code = self::API_TOKEN_AUTHENTICATION;
376377
// update config to use api token authentication
377378
$this->setConfig(self::ACCESS, $code);
378379
// create a group + member

0 commit comments

Comments
 (0)