add: cover authkeys api endpoints, extend openapi spec

pull/80/head
Luciano Righetti 2022-01-18 17:39:41 +01:00
parent afcfe57767
commit 850eb0fb2d
12 changed files with 348 additions and 20 deletions

View File

@ -11,9 +11,18 @@ class AuthKeysFixture extends TestFixture
{
public $connection = 'test';
public const ADMIN_API_ID = 1;
public const ADMIN_API_KEY = 'd033e22ae348aeb5660fc2140aec35850c4da997';
public const SYNC_API_ID = 2;
public const SYNC_API_KEY = '6b387ced110858dcbcda36edb044dc18f91a0894';
public const ORG_ADMIN_API_ID = 3;
public const ORG_ADMIN_API_KEY = '1c4685d281d478dbcebd494158024bc3539004d0';
public const REGULAR_USER_API_ID = 4;
public const REGULAR_USER_API_KEY = '12dea96fec20593566ab75692c9949596833adc9';
public function init(): void
@ -23,6 +32,7 @@ class AuthKeysFixture extends TestFixture
$this->records = [
[
'id' => self::ADMIN_API_ID,
'uuid' => $faker->uuid(),
'authkey' => $hasher->hash(self::ADMIN_API_KEY),
'authkey_start' => substr(self::ADMIN_API_KEY, 0, 4),
@ -34,6 +44,7 @@ class AuthKeysFixture extends TestFixture
'modified' => $faker->dateTime()->getTimestamp()
],
[
'id' => self::SYNC_API_ID,
'uuid' => $faker->uuid(),
'authkey' => $hasher->hash(self::SYNC_API_KEY),
'authkey_start' => substr(self::SYNC_API_KEY, 0, 4),
@ -45,6 +56,7 @@ class AuthKeysFixture extends TestFixture
'modified' => $faker->dateTime()->getTimestamp()
],
[
'id' => self::ORG_ADMIN_API_ID,
'uuid' => $faker->uuid(),
'authkey' => $hasher->hash(self::ORG_ADMIN_API_KEY),
'authkey_start' => substr(self::ORG_ADMIN_API_KEY, 0, 4),
@ -56,6 +68,7 @@ class AuthKeysFixture extends TestFixture
'modified' => $faker->dateTime()->getTimestamp()
],
[
'id' => self::REGULAR_USER_API_ID,
'uuid' => $faker->uuid(),
'authkey' => $hasher->hash(self::REGULAR_USER_API_KEY),
'authkey_start' => substr(self::REGULAR_USER_API_KEY, 0, 4),

View File

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\Users;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\UsersFixture;
use App\Test\Helper\ApiTestTrait;
class AddAuthKeyApiTest extends TestCase
{
use IntegrationTestTrait;
use ApiTestTrait;
protected const ENDPOINT = '/api/v1/authKeys/add';
protected $fixtures = [
'app.Organisations',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys'
];
public function testAddAdminAuthKey(): void
{
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$faker = \Faker\Factory::create();
$uuid = $faker->uuid;
$this->post(
self::ENDPOINT,
[
'uuid' => $uuid,
'authkey' => $faker->sha1,
'expiration' => 0,
'user_id' => UsersFixture::USER_ADMIN_ID,
'comment' => $faker->text
]
);
$this->assertResponseOk();
$this->assertResponseContains(sprintf('"uuid": "%s"', $uuid));
$this->assertDbRecordExists('AuthKeys', ['uuid' => $uuid]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec(self::ENDPOINT, 'post');
}
public function testAddAdminAuthKeyNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$faker = \Faker\Factory::create();
$uuid = $faker->uuid;
$this->post(
self::ENDPOINT,
[
'uuid' => $uuid,
'authkey' => $faker->sha1,
'expiration' => 0,
'user_id' => UsersFixture::USER_ADMIN_ID,
'comment' => $faker->text
]
);
$this->assertResponseCode(404);
$this->addWarning('Should return 405 Method Not Allowed instead of 404 Not Found');
$this->assertDbRecordNotExists('AuthKeys', ['uuid' => $uuid]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec(self::ENDPOINT, 'post');
}
}

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\Users;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\BroodsFixture;
use App\Test\Helper\ApiTestTrait;
class DeleteAuthKeyApiTest extends TestCase
{
use IntegrationTestTrait;
use ApiTestTrait;
protected const ENDPOINT = '/api/v1/authKeys/delete';
protected $fixtures = [
'app.Organisations',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys',
];
public function testDeleteAdminAuthKey(): void
{
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, AuthKeysFixture::ADMIN_API_ID);
$this->delete($url);
$this->assertResponseOk();
$this->assertDbRecordNotExists('AuthKeys', ['id' => AuthKeysFixture::ADMIN_API_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
}
public function testDeleteOrgAdminAuthKeyNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, AuthKeysFixture::ORG_ADMIN_API_ID);
$this->delete($url);
$this->assertResponseCode(405);
$this->assertDbRecordExists('AuthKeys', ['id' => AuthKeysFixture::ORG_ADMIN_API_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
}
}

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\Users;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Helper\ApiTestTrait;
class IndexAuthKeysApiTest extends TestCase
{
use IntegrationTestTrait;
use ApiTestTrait;
protected const ENDPOINT = '/api/v1/authKeys/index';
protected $fixtures = [
'app.Organisations',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys'
];
public function testIndexAuthKeys(): void
{
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->get(self::ENDPOINT);
$this->assertResponseOk();
$this->assertResponseContains(sprintf('"id": %d', AuthKeysFixture::ADMIN_API_ID));
// TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec(self::ENDPOINT);
}
public function testIndexDoesNotShowAdminAuthKeysAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->get(self::ENDPOINT);
$this->assertResponseOk();
$this->assertResponseNotContains(sprintf('"id": %d', AuthKeysFixture::REGULAR_USER_API_KEY));
// TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec(self::ENDPOINT);
}
}

View File

@ -10,7 +10,7 @@ use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\BroodsFixture;
use App\Test\Helper\ApiTestTrait;
class DeleteBroodsApiTest extends TestCase
class DeleteBroodApiTest extends TestCase
{
use IntegrationTestTrait;
use ApiTestTrait;
@ -36,7 +36,6 @@ class DeleteBroodsApiTest extends TestCase
$this->assertDbRecordNotExists('Broods', ['id' => BroodsFixture::BROOD_A_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
public function testDeleteBroodNotAllowedAsRegularUser(): void
@ -49,6 +48,5 @@ class DeleteBroodsApiTest extends TestCase
$this->assertDbRecordExists('Broods', ['id' => BroodsFixture::BROOD_A_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
}

View File

@ -36,7 +36,6 @@ class DeleteEncryptionKeyApiTest extends TestCase
$this->assertDbRecordNotExists('EncryptionKeys', ['id' => EncryptionKeysFixture::ENCRYPTION_KEY_ORG_A_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
public function testDeleteEncryptionKeyNotAllowedAsRegularUser(): void
@ -49,6 +48,5 @@ class DeleteEncryptionKeyApiTest extends TestCase
$this->assertDbRecordExists('EncryptionKeys', ['id' => EncryptionKeysFixture::ENCRYPTION_KEY_ORG_B_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
}

View File

@ -35,7 +35,6 @@ class DeleteIndividualApiTest extends TestCase
$this->assertDbRecordNotExists('Individuals', ['id' => IndividualsFixture::INDIVIDUAL_A_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
public function testDeleteIndividualNotAllowedAsRegularUser(): void
@ -48,6 +47,5 @@ class DeleteIndividualApiTest extends TestCase
$this->assertDbRecordExists('Individuals', ['id' => IndividualsFixture::INDIVIDUAL_ADMIN_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
}

View File

@ -35,7 +35,6 @@ class DeleteOrganisationApiTest extends TestCase
$this->assertDbRecordNotExists('Organisations', ['id' => OrganisationsFixture::ORGANISATION_B_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
public function testDeleteOrganisationNotAllowedAsRegularUser(): void
@ -48,6 +47,5 @@ class DeleteOrganisationApiTest extends TestCase
$this->assertDbRecordExists('Organisations', ['id' => OrganisationsFixture::ORGANISATION_B_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
}

View File

@ -36,7 +36,6 @@ class DeleteSharingGroupApiTest extends TestCase
$this->assertDbRecordNotExists('SharingGroups', ['id' => SharingGroupsFixture::SHARING_GROUP_A_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
public function testDeleteSharingGroupNotAllowedAsRegularUser(): void
@ -49,6 +48,5 @@ class DeleteSharingGroupApiTest extends TestCase
$this->assertDbRecordExists('SharingGroups', ['id' => SharingGroupsFixture::SHARING_GROUP_A_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
}

View File

@ -37,7 +37,6 @@ class DeleteUserApiTest extends TestCase
$this->assertDbRecordNotExists('Users', ['id' => UsersFixture::USER_REGULAR_USER_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
public function testDeleteUserNotAllowedAsRegularUser(): void
@ -50,6 +49,5 @@ class DeleteUserApiTest extends TestCase
$this->assertDbRecordExists('Users', ['id' => UsersFixture::USER_ORG_ADMIN_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
}

View File

@ -57,6 +57,7 @@ class EditUserApiTest extends TestCase
]
);
$this->assertResponseOk();
$this->assertDbRecordNotExists('Users', [
'id' => UsersFixture::USER_REGULAR_USER_ID,
'role_id' => RolesFixture::ROLE_ADMIN_ID
@ -75,6 +76,7 @@ class EditUserApiTest extends TestCase
]
);
$this->assertResponseOk();
$this->assertDbRecordExists('Users', [
'id' => UsersFixture::USER_REGULAR_USER_ID,
'username' => 'test'

View File

@ -25,6 +25,8 @@ tags:
description: "Cerebrate can connect to other Cerebrate instances to exchange trust information and to instrument interconnectivity between connected local tools. Each such Cerebrate instance with its connected tools is considered to be a brood."
- name: EncryptionKeys
description: "Assign encryption keys to the user, used to securely communicate or validate messages coming from the user."
- name: AuthKeys
description: "Authkeys are used for API access. A user can have more than one authkey, so if you would like to use separate keys per tool that queries Cerebrate, add additional keys. Use the comment field to make identifying your keys easier."
paths:
/api/v1/individuals/index:
@ -726,6 +728,61 @@ paths:
default:
$ref: "#/components/responses/ApiErrorResponse"
# AuthKeys
/api/v1/authKeys/index:
get:
summary: "Get auth keys list"
operationId: getAuthKeys
tags:
- AuthKeys
parameters:
- $ref: "#/components/parameters/quickFilter"
responses:
"200":
$ref: "#/components/responses/AuthKeyListResponse"
"403":
$ref: "#/components/responses/UnauthorizedApiErrorResponse"
"405":
$ref: "#/components/responses/MethodNotAllowedApiErrorResponse"
default:
$ref: "#/components/responses/ApiErrorResponse"
/api/v1/authKeys/add:
post:
summary: "Add auth keys"
operationId: addAuthKey
tags:
- AuthKeys
requestBody:
$ref: "#/components/requestBodies/CreateAuthKeyRequest"
responses:
"200":
$ref: "#/components/responses/AuthKeyResponse"
"403":
$ref: "#/components/responses/UnauthorizedApiErrorResponse"
"405":
$ref: "#/components/responses/MethodNotAllowedApiErrorResponse"
default:
$ref: "#/components/responses/ApiErrorResponse"
/api/v1/authKeys/delete/{authKeyId}:
delete:
summary: "Delete auth key by ID"
operationId: deleteAuthKeyById
tags:
- AuthKeys
parameters:
- $ref: "#/components/parameters/authKeyId"
responses:
"200":
$ref: "#/components/responses/AuthKeyResponse"
"403":
$ref: "#/components/responses/UnauthorizedApiErrorResponse"
"405":
$ref: "#/components/responses/MethodNotAllowedApiErrorResponse"
default:
$ref: "#/components/responses/ApiErrorResponse"
components:
schemas:
# General
@ -750,9 +807,6 @@ components:
format: email
example: "user@example.com"
AuthKey:
type: string
ModelName:
type: string
enum:
@ -1195,7 +1249,7 @@ components:
skip_proxy:
type: boolean
authkey:
$ref: "#/components/schemas/AuthKey"
$ref: "#/components/schemas/AuthKeyRaw"
organisation:
$ref: "#/components/schemas/Organisation"
created:
@ -1224,7 +1278,7 @@ components:
EncryptionKeyExpiration:
type: integer
description: "Timestamp or null of there is no expiration"
description: "UNIX timestamp or null of there is no expiration"
nullable: true
EncryptionKey:
@ -1256,6 +1310,58 @@ components:
items:
$ref: "#/components/schemas/EncryptionKey"
# AuthKeys
AuthKeyRaw:
type: string
AuthKeyHashed:
type: string
AuthKeyExpiration:
type: integer
description: "0 or UNIX timestamp"
example: 0
AuthKeyCreatedAt:
type: integer
description: "UNIX timestamp"
AuthKeyComment:
type: string
AuthKey:
type: object
properties:
id:
$ref: "#/components/schemas/ID"
uuid:
$ref: "#/components/schemas/UUID"
authkey:
$ref: "#/components/schemas/AuthKeyHashed"
authkey_start:
type: string
example: abcd
authkey_end:
type: string
example: abcd
created:
$ref: "#/components/schemas/AuthKeyCreatedAt"
expiration:
$ref: "#/components/schemas/AuthKeyExpiration"
type: integer
description: "0 or UNIX timestamp"
user_id:
$ref: "#/components/schemas/ID"
comment:
$ref: "#/components/schemas/AuthKeyComment"
user:
$ref: "#/components/schemas/User"
AuthKeyList:
type: array
items:
$ref: "#/components/schemas/AuthKey"
# Errors
ApiError:
type: object
@ -1373,6 +1479,14 @@ components:
schema:
$ref: "#/components/schemas/ID"
authKeyId:
name: authKeyId
in: path
description: "Numeric ID of the AuthKey"
required: true
schema:
$ref: "#/components/schemas/ID"
quickFilter:
name: quickFilter
in: query
@ -1629,7 +1743,7 @@ components:
skip_proxy:
type: boolean
authkey:
$ref: "#/components/schemas/AuthKey"
$ref: "#/components/schemas/AuthKeyRaw"
EditBroodRequest:
required: true
@ -1655,7 +1769,7 @@ components:
skip_proxy:
type: boolean
authkey:
$ref: "#/components/schemas/AuthKey"
$ref: "#/components/schemas/AuthKeyRaw"
CreateEncryptionKeyRequest:
required: true
@ -1701,6 +1815,25 @@ components:
owner_model:
$ref: "#/components/schemas/ModelName"
# AuthKeys
CreateAuthKeyRequest:
required: true
content:
application/json:
schema:
type: object
properties:
uuid:
$ref: "#/components/schemas/UUID"
authkey:
$ref: "#/components/schemas/AuthKeyRaw"
expiration:
$ref: "#/components/schemas/AuthKeyExpiration"
user_id:
$ref: "#/components/schemas/ID"
comment:
$ref: "#/components/schemas/AuthKeyComment"
responses:
# Individuals
IndividualResponse:
@ -1881,6 +2014,21 @@ components:
schema:
$ref: "#/components/schemas/EncryptionKeyList"
# AuthKeys
AuthKeyResponse:
description: "Auth key response"
content:
application/json:
schema:
$ref: "#/components/schemas/AuthKey"
AuthKeyListResponse:
description: "Auth key list response"
content:
application/json:
schema:
$ref: "#/components/schemas/AuthKeyList"
# Errors
ApiErrorResponse:
description: "Unexpected API error"