diff --git a/.gitignore b/.gitignore
index 869728c..ba05ac6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,4 @@ webroot/theme/node_modules
.vscode
docker/run/
.phpunit.result.cache
-config.json
\ No newline at end of file
+config.json
diff --git a/composer.json b/composer.json
index bc562a0..4e1af85 100644
--- a/composer.json
+++ b/composer.json
@@ -19,7 +19,9 @@
"cakephp/bake": "^2.0.3",
"cakephp/cakephp-codesniffer": "~4.0.0",
"cakephp/debug_kit": "^4.0",
+ "fzaninotto/faker": "^1.9",
"josegonzalez/dotenv": "^3.2",
+ "league/openapi-psr7-validator": "^0.16.4",
"phpunit/phpunit": "^8.5",
"psy/psysh": "@stable"
},
@@ -44,7 +46,6 @@
"scripts": {
"post-install-cmd": "App\\Console\\Installer::postInstall",
"post-create-project-cmd": "App\\Console\\Installer::postInstall",
- "post-autoload-dump": "Cake\\Composer\\Installer\\PluginInstaller::postAutoloadDump",
"check": [
"@test",
"@cs-check"
diff --git a/config/routes.php b/config/routes.php
index ac1ab26..e467725 100644
--- a/config/routes.php
+++ b/config/routes.php
@@ -92,14 +92,14 @@ $routes->prefix('Open', function (RouteBuilder $routes) {
$routes->fallbacks(DashedRoute::class);
});
-/*
- * If you need a different set of middleware or none at all,
- * open new scope and define routes there.
- *
- * ```
- * $routes->scope('/api', function (RouteBuilder $builder) {
- * // No $builder->applyMiddleware() here.
- * // Connect API actions here.
- * });
- * ```
- */
+// API routes
+$routes->scope('/api', function (RouteBuilder $routes) {
+ // $routes->applyMiddleware('ratelimit', 'auth.api');
+ $routes->scope('/v1', function (RouteBuilder $routes) {
+ // $routes->applyMiddleware('v1compat');
+ $routes->setExtensions(['json']);
+
+ // Generic API route
+ $routes->connect('/{controller}/{action}/*');
+ });
+});
\ No newline at end of file
diff --git a/src/Controller/ApiController.php b/src/Controller/ApiController.php
new file mode 100644
index 0000000..65cd11c
--- /dev/null
+++ b/src/Controller/ApiController.php
@@ -0,0 +1,19 @@
+set('url', $url);
+ }
+}
diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php
index afa70ff..0c6d897 100644
--- a/src/Controller/Component/ACLComponent.php
+++ b/src/Controller/Component/ACLComponent.php
@@ -193,6 +193,9 @@ class ACLComponent extends Component
'getBookmarks' => ['*'],
'saveBookmark' => ['*'],
'deleteBookmark' => ['*']
+ ],
+ 'Api' => [
+ 'index' => ['*']
]
);
diff --git a/templates/Api/index.php b/templates/Api/index.php
new file mode 100644
index 0000000..96be4b8
--- /dev/null
+++ b/templates/Api/index.php
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/tests/Fixture/AuthKeysFixture.php b/tests/Fixture/AuthKeysFixture.php
index 5924c5b..8291811 100644
--- a/tests/Fixture/AuthKeysFixture.php
+++ b/tests/Fixture/AuthKeysFixture.php
@@ -11,24 +11,60 @@ class AuthKeysFixture extends TestFixture
{
public $connection = 'test';
- public const ADMIN_API_KEY = '4cd687b314a3b9c4d83264e6195b9a3706ef4c2f';
+ public const ADMIN_API_KEY = 'd033e22ae348aeb5660fc2140aec35850c4da997';
+ public const SYNC_API_KEY = '6b387ced110858dcbcda36edb044dc18f91a0894';
+ public const ORG_ADMIN_API_KEY = '1c4685d281d478dbcebd494158024bc3539004d0';
+ public const USER_API_KEY = '12dea96fec20593566ab75692c9949596833adc9';
public function init(): void
{
$hasher = new DefaultPasswordHasher();
+ $faker = \Faker\Factory::create();
$this->records = [
[
- 'id' => 1,
- 'uuid' => '3ebfbe50-e7d2-406e-a092-f031e604b6e5',
+ 'uuid' => $faker->uuid(),
'authkey' => $hasher->hash(self::ADMIN_API_KEY),
- 'authkey_start' => '4cd6',
- 'authkey_end' => '4c2f',
+ 'authkey_start' => substr(self::ADMIN_API_KEY, 0, 4),
+ 'authkey_end' => substr(self::ADMIN_API_KEY, -4),
'expiration' => 0,
- 'user_id' => 1,
+ 'user_id' => UsersFixture::USER_ADMIN_ID,
'comment' => '',
- 'created' => time(),
- 'modified' => time()
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'uuid' => $faker->uuid(),
+ 'authkey' => $hasher->hash(self::SYNC_API_KEY),
+ 'authkey_start' => substr(self::SYNC_API_KEY, 0, 4),
+ 'authkey_end' => substr(self::SYNC_API_KEY, -4),
+ 'expiration' => 0,
+ 'user_id' => UsersFixture::USER_SYNC_ID,
+ 'comment' => '',
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'uuid' => $faker->uuid(),
+ 'authkey' => $hasher->hash(self::ORG_ADMIN_API_KEY),
+ 'authkey_start' => substr(self::ORG_ADMIN_API_KEY, 0, 4),
+ 'authkey_end' => substr(self::ORG_ADMIN_API_KEY, -4),
+ 'expiration' => 0,
+ 'user_id' => UsersFixture::USER_ORG_ADMIN_ID,
+ 'comment' => '',
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'uuid' => $faker->uuid(),
+ 'authkey' => $hasher->hash(self::USER_API_KEY),
+ 'authkey_start' => substr(self::USER_API_KEY, 0, 4),
+ 'authkey_end' => substr(self::USER_API_KEY, -4),
+ 'expiration' => 0,
+ 'user_id' => UsersFixture::USER_REGULAR_USER_ID,
+ 'comment' => '',
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
]
];
parent::init();
diff --git a/tests/Fixture/IndividualsFixture.php b/tests/Fixture/IndividualsFixture.php
index 31261f2..9c96f8b 100644
--- a/tests/Fixture/IndividualsFixture.php
+++ b/tests/Fixture/IndividualsFixture.php
@@ -10,16 +10,64 @@ class IndividualsFixture extends TestFixture
{
public $connection = 'test';
- public $records = [
- [
- 'id' => 1,
- 'uuid' => '3ebfbe50-e7d2-406e-a092-f031e604b6e1',
- 'email' => 'admin@admin.test',
- 'first_name' => 'admin',
- 'last_name' => 'admin',
- 'position' => 'admin',
- 'created' => '2022-01-04 10:00:00',
- 'modified' => '2022-01-04 10:00:00'
- ]
- ];
+ // Admin individual
+ public const INDIVIDUAL_ADMIN_ID = 1;
+
+ // Sync individual
+ public const INDIVIDUAL_SYNC_ID = 2;
+
+ // Org Admin individual
+ public const INDIVIDUAL_ORG_ADMIN_ID = 3;
+
+ // Regular User individual
+ public const INDIVIDUAL_REGULAR_USER_ID = 4;
+
+ public function init(): void
+ {
+ $faker = \Faker\Factory::create();
+
+ $this->records = [
+ [
+ 'id' => self::INDIVIDUAL_ADMIN_ID,
+ 'uuid' => $faker->uuid(),
+ 'email' => $faker->email(),
+ 'first_name' => $faker->firstName,
+ 'last_name' => $faker->lastName,
+ 'position' => 'admin',
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'id' => self::INDIVIDUAL_SYNC_ID,
+ 'uuid' => $faker->uuid(),
+ 'email' => $faker->email(),
+ 'first_name' => $faker->firstName,
+ 'last_name' => $faker->lastName,
+ 'position' => 'sync',
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'id' => self::INDIVIDUAL_ORG_ADMIN_ID,
+ 'uuid' => $faker->uuid(),
+ 'email' => $faker->email(),
+ 'first_name' => $faker->firstName,
+ 'last_name' => $faker->lastName,
+ 'position' => 'org_admin',
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'id' => self::INDIVIDUAL_REGULAR_USER_ID,
+ 'uuid' => $faker->uuid(),
+ 'email' => $faker->email(),
+ 'first_name' => $faker->firstName,
+ 'last_name' => $faker->lastName,
+ 'position' => 'user',
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ]
+ ];
+ parent::init();
+ }
}
diff --git a/tests/Fixture/OrganisationsFixture.php b/tests/Fixture/OrganisationsFixture.php
new file mode 100644
index 0000000..d8b81f9
--- /dev/null
+++ b/tests/Fixture/OrganisationsFixture.php
@@ -0,0 +1,52 @@
+records = [
+ [
+ 'id' => self::ORGANISATION_A_ID,
+ 'uuid' => $faker->uuid(),
+ 'name' => 'Organisation A',
+ 'url' => $faker->url,
+ 'nationality' => $faker->countryCode,
+ 'sector' => 'IT',
+ 'type' => '',
+ 'contacts' => '',
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'id' => self::ORGANISATION_B_ID,
+ 'uuid' => $faker->uuid(),
+ 'name' => 'Organisation B',
+ 'url' => $faker->url,
+ 'nationality' => $faker->countryCode,
+ 'sector' => 'IT',
+ 'type' => '',
+ 'contacts' => '',
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ]
+ ];
+ parent::init();
+ }
+}
diff --git a/tests/Fixture/RolesFixture.php b/tests/Fixture/RolesFixture.php
index ced82a1..1230e8f 100644
--- a/tests/Fixture/RolesFixture.php
+++ b/tests/Fixture/RolesFixture.php
@@ -10,15 +10,53 @@ class RolesFixture extends TestFixture
{
public $connection = 'test';
- public $records = [
- [
- 'id' => 1,
- 'uuid' => '3ebfbe50-e7d2-406e-a092-f031e604b6e4',
- 'name' => 'admin',
- 'is_default' => true,
- 'perm_admin' => true,
- 'perm_sync' => true,
- 'perm_org_admin' => true
- ]
- ];
+ public const ROLE_ADMIN_ID = 1;
+ public const ROLE_SYNC_ID = 2;
+ public const ROLE_ORG_ADMIN_ID = 3;
+ public const ROLE_REGULAR_USER_ID = 4;
+
+ public function init(): void
+ {
+ $faker = \Faker\Factory::create();
+
+ $this->records = [
+ [
+ 'id' => self::ROLE_ADMIN_ID,
+ 'uuid' => $faker->uuid(),
+ 'name' => 'admin',
+ 'is_default' => false,
+ 'perm_admin' => true,
+ 'perm_sync' => false,
+ 'perm_org_admin' => false
+ ],
+ [
+ 'id' => self::ROLE_SYNC_ID,
+ 'uuid' => $faker->uuid(),
+ 'name' => 'sync',
+ 'is_default' => false,
+ 'perm_admin' => false,
+ 'perm_sync' => true,
+ 'perm_org_admin' => false
+ ],
+ [
+ 'id' => self::ROLE_ORG_ADMIN_ID,
+ 'uuid' => $faker->uuid(),
+ 'name' => 'org_admin',
+ 'is_default' => false,
+ 'perm_admin' => false,
+ 'perm_sync' => false,
+ 'perm_org_admin' => true
+ ],
+ [
+ 'id' => self::ROLE_REGULAR_USER_ID,
+ 'uuid' => $faker->uuid(),
+ 'name' => 'user',
+ 'is_default' => true,
+ 'perm_admin' => false,
+ 'perm_sync' => false,
+ 'perm_org_admin' => false
+ ]
+ ];
+ parent::init();
+ }
}
diff --git a/tests/Fixture/UsersFixture.php b/tests/Fixture/UsersFixture.php
index df6b8bd..ba35e7a 100644
--- a/tests/Fixture/UsersFixture.php
+++ b/tests/Fixture/UsersFixture.php
@@ -11,26 +11,80 @@ class UsersFixture extends TestFixture
{
public $connection = 'test';
- public const ADMIN_USER = 'admin';
- public const ADMIN_PASSWORD = 'Password1234';
+ // Admin user
+ public const USER_ADMIN_ID = 1;
+ public const USER_ADMIN_USERNAME = 'admin';
+ public const USER_ADMIN_PASSWORD = 'AdminPassword';
+
+ // Sync user
+ public const USER_SYNC_ID = 2;
+ public const USER_SYNC_USERNAME = 'sync';
+ public const USER_SYNC_PASSWORD = 'SyncPassword';
+
+ // Org Admin user
+ public const USER_ORG_ADMIN_ID = 3;
+ public const USER_ORG_ADMIN_USERNAME = 'org_admin';
+ public const USER_ORG_ADMIN_PASSWORD = 'OrgAdminPassword';
+
+ // Regular User user
+ public const USER_REGULAR_USER_ID = 4;
+ public const USER_REGULAR_USER_USERNAME = 'user';
+ public const USER_REGULAR_USER_PASSWORD = 'UserPassword';
+
public function init(): void
{
$hasher = new DefaultPasswordHasher();
-
+ $faker = \Faker\Factory::create();
$this->records = [
[
- 'id' => 1,
- 'uuid' => '3ebfbe50-e7d2-406e-a092-f031e604b6e5',
- 'username' => self::ADMIN_USER,
- 'password' => $hasher->hash(self::ADMIN_PASSWORD),
- 'role_id' => 1,
- 'individual_id' => 1,
+ 'id' => self::USER_ADMIN_ID,
+ 'uuid' => $faker->uuid(),
+ 'username' => self::USER_ADMIN_USERNAME,
+ 'password' => $hasher->hash(self::USER_ADMIN_PASSWORD),
+ 'role_id' => RolesFixture::ROLE_ADMIN_ID,
+ 'individual_id' => IndividualsFixture::INDIVIDUAL_ADMIN_ID,
'disabled' => 0,
- 'organisation_id' => 1,
- 'created' => '2022-01-04 10:00:00',
- 'modified' => '2022-01-04 10:00:00'
+ 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID,
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'id' => self::USER_SYNC_ID,
+ 'uuid' => $faker->uuid(),
+ 'username' => self::USER_SYNC_USERNAME,
+ 'password' => $hasher->hash(self::USER_SYNC_PASSWORD),
+ 'role_id' => RolesFixture::ROLE_SYNC_ID,
+ 'individual_id' => IndividualsFixture::INDIVIDUAL_SYNC_ID,
+ 'disabled' => 0,
+ 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID,
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'id' => self::USER_ORG_ADMIN_ID,
+ 'uuid' => $faker->uuid(),
+ 'username' => self::USER_ORG_ADMIN_USERNAME,
+ 'password' => $hasher->hash(self::USER_ORG_ADMIN_PASSWORD),
+ 'role_id' => RolesFixture::ROLE_ORG_ADMIN_ID,
+ 'individual_id' => IndividualsFixture::INDIVIDUAL_ORG_ADMIN_ID,
+ 'disabled' => 0,
+ 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID,
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
+ ],
+ [
+ 'id' => self::USER_REGULAR_USER_ID,
+ 'uuid' => $faker->uuid(),
+ 'username' => self::USER_REGULAR_USER_USERNAME,
+ 'password' => $hasher->hash(self::USER_REGULAR_USER_PASSWORD),
+ 'role_id' => RolesFixture::ROLE_REGULAR_USER_ID,
+ 'individual_id' => IndividualsFixture::INDIVIDUAL_REGULAR_USER_ID,
+ 'disabled' => 0,
+ 'organisation_id' => OrganisationsFixture::ORGANISATION_A_ID,
+ 'created' => $faker->dateTime()->getTimestamp(),
+ 'modified' => $faker->dateTime()->getTimestamp()
]
];
parent::init();
diff --git a/tests/Helper/ApiTestTrait.php b/tests/Helper/ApiTestTrait.php
new file mode 100644
index 0000000..e749677
--- /dev/null
+++ b/tests/Helper/ApiTestTrait.php
@@ -0,0 +1,79 @@
+_authToken = $authToken;
+
+ // somehow this is not set automatically in test environment
+ $_SERVER['HTTP_AUTHORIZATION'] = $authToken;
+
+ $this->configRequest([
+ 'headers' => [
+ 'Accept' => 'application/json',
+ 'Authorization' => $this->_authToken
+ ]
+ ]);
+ }
+
+ /**
+ * Parse the OpenAPI specification and create a validator
+ *
+ * @param string $specFile
+ * @return void
+ */
+ public function initializeValidator(string $specFile): void
+ {
+ $this->validator = (new ValidatorBuilder)->fromYamlFile($specFile);
+ $this->requestValidator = $this->validator->getRequestValidator();
+ $this->responseValidator = $this->validator->getResponseValidator();
+ }
+
+ /**
+ * Validates the API request against the OpenAPI spec
+ *
+ * @param string $path The path to the API endpoint
+ * @param string $method The HTTP method used to call the endpoint
+ * @return void
+ */
+ public function validateRequest(string $endpoint, string $method = 'get'): void
+ {
+ // TODO: find a workaround to create a PSR-7 request object for validation
+ throw NotImplementedException("Unfortunately cakephp does not save the PSR-7 request object in the test context");
+ }
+
+ /**
+ * Validates the API response against the OpenAPI spec
+ *
+ * @param string $path The path to the API endpoint
+ * @param string $method The HTTP method used to call the endpoint
+ * @return void
+ */
+ public function validateResponse(string $endpoint, string $method = 'get'): void
+ {
+ $address = new OperationAddress($endpoint, $method);
+ $this->responseValidator->validate($address, $this->_response);
+ }
+}
diff --git a/tests/README.md b/tests/README.md
index 898e249..f37bcf3 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,5 +1,14 @@
# Testing
-Add a test database to your `config/app_local.php` config file and set `debug` mode to `true`.
+
+1. Add a `cerebrate_test` database to the db:
+```mysql
+CREATE DATABASE cerebrate_test;
+GRANT ALL PRIVILEGES ON cerebrate_test.* to cerebrate@localhost;
+FLUSH PRIVILEGES;
+QUIT;
+```
+
+2. Add a the test database to your `config/app_local.php` config file and set `debug` mode to `true`.
```php
'debug' => true,
'Datasources' => [
diff --git a/tests/TestCase/Api/Users/UsersApiTest.php b/tests/TestCase/Api/Users/UsersApiTest.php
new file mode 100644
index 0000000..2f2c2b8
--- /dev/null
+++ b/tests/TestCase/Api/Users/UsersApiTest.php
@@ -0,0 +1,55 @@
+initializeValidator(APP . '../webroot/docs/openapi.yaml');
+ }
+
+ public function testViewMe(): void
+ {
+ $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
+ $this->get(self::ENDPOINT);
+
+ $this->assertResponseOk();
+ $this->assertResponseContains(sprintf('"username": "%s"', UsersFixture::USER_ADMIN_USERNAME));
+ // TODO: $this->validateRequest()
+ $this->validateResponse(self::ENDPOINT);
+ }
+
+ public function testViewById(): void
+ {
+ $this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
+ $url = sprintf('%s/%d', self::ENDPOINT, UsersFixture::USER_ADMIN_ID);
+ $this->get($url);
+
+ $this->assertResponseOk();
+ $this->assertResponseContains(sprintf('"username": "%s"', UsersFixture::USER_ADMIN_USERNAME));
+ // TODO: $this->validateRequest()
+ $this->validateResponse($url);
+ }
+}
diff --git a/tests/TestCase/Api/UsersApiTest.php b/tests/TestCase/Api/UsersApiTest.php
deleted file mode 100644
index 5353305..0000000
--- a/tests/TestCase/Api/UsersApiTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-configRequest([
- 'headers' => [
- // this does not work: https://book.cakephp.org/4/en/development/testing.html#testing-stateless-authentication-and-apis
- // 'Authorization' => AuthKeysFixture::ADMIN_API_KEY,
- 'Accept' => 'application/json'
- ]
- ]);
-
- $this->get('/users/view');
-
- $this->assertResponseOk();
- $this->assertResponseContains(sprintf('"username": "%s"', UsersFixture::ADMIN_USER));
- }
-}
diff --git a/tests/TestCase/Controller/UsersControllerTest.php b/tests/TestCase/Controller/Users/UsersControllerTest.php
similarity index 93%
rename from tests/TestCase/Controller/UsersControllerTest.php
rename to tests/TestCase/Controller/Users/UsersControllerTest.php
index 0c856e8..37dd73a 100644
--- a/tests/TestCase/Controller/UsersControllerTest.php
+++ b/tests/TestCase/Controller/Users/UsersControllerTest.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace App\Test\TestCase\Controller;
+namespace App\Test\TestCase\Controller\Users;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index f8088be..9283095 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -66,4 +66,6 @@ if (!in_array('skip-migrations', $_SERVER['argv'])) {
['plugin' => 'Tags', 'connection' => 'test', 'skip' => ['*']],
['plugin' => 'ADmad/SocialAuth', 'connection' => 'test', 'skip' => ['*']]
]);
+}else{
+ echo "[ * ] Skipping migrations ...\n";
}
diff --git a/webroot/docs/openapi.yaml b/webroot/docs/openapi.yaml
new file mode 100644
index 0000000..5a55225
--- /dev/null
+++ b/webroot/docs/openapi.yaml
@@ -0,0 +1,215 @@
+openapi: 3.0.0
+info:
+ version: 1.3.0
+ title: Cerebrate Project API
+ description: |
+
+ TODO: markdown description
+
+servers:
+ - url: https://cerebrate.local
+
+tags:
+ - name: Users
+ description: "TODO: users resource descriptions"
+
+paths:
+ /api/v1/users/view:
+ get:
+ summary: "Get information about the current user"
+ operationId: viewUserMe
+ tags:
+ - Users
+ responses:
+ "200":
+ $ref: "#/components/responses/ViewUserResponse"
+ "403":
+ $ref: "#/components/responses/UnauthorizedApiErrorResponse"
+ default:
+ $ref: "#/components/responses/ApiErrorResponse"
+
+ /api/v1/users/view/{userId}:
+ get:
+ summary: "Get information of a user by id"
+ operationId: viewUserById
+ tags:
+ - Users
+ parameters:
+ - $ref: "#/components/parameters/userId"
+ responses:
+ "200":
+ $ref: "#/components/responses/ViewUserResponse"
+ "403":
+ $ref: "#/components/responses/UnauthorizedApiErrorResponse"
+ default:
+ $ref: "#/components/responses/ApiErrorResponse"
+
+components:
+ schemas:
+ # General
+ UUID:
+ type: string
+ format: uuid
+ maxLength: 36
+ example: "c99506a6-1255-4b71-afa5-7b8ba48c3b1b"
+
+ ID:
+ type: integer
+ format: int32
+ example: 1
+
+ DateTime:
+ type: string
+ format: datetime
+ example: "2022-01-05T11:19:26+00:00"
+
+ # Users
+ Username:
+ type: string
+ example: "admin"
+
+ User:
+ type: object
+ properties:
+ id:
+ $ref: "#/components/schemas/ID"
+ uuid:
+ $ref: "#/components/schemas/UUID"
+ username:
+ $ref: "#/components/schemas/Username"
+ role_id:
+ $ref: "#/components/schemas/ID"
+ individual_id:
+ $ref: "#/components/schemas/ID"
+ disabled:
+ type: boolean
+ created:
+ $ref: "#/components/schemas/DateTime"
+ modified:
+ $ref: "#/components/schemas/DateTime"
+ organisation_id:
+ $ref: "#/components/schemas/ID"
+
+ # Individuals
+
+ # Organisations
+
+ # Roles
+ RoleName:
+ type: string
+ maxLength: 255
+ example: "admin"
+
+ Role:
+ type: object
+ properties:
+ id:
+ $ref: "#/components/schemas/ID"
+ name:
+ $ref: "#/components/schemas/RoleName"
+ is_default:
+ type: boolean
+ perm_admin:
+ type: boolean
+ perm_sync:
+ type: boolean
+ perm_org_admin:
+ type: boolean
+
+ # Errors
+ ApiError:
+ type: object
+ required:
+ - name
+ - message
+ - url
+ properties:
+ name:
+ type: string
+ message:
+ type: string
+ url:
+ type: string
+ example: "/users"
+
+ UnauthorizedApiError:
+ type: object
+ required:
+ - name
+ - message
+ - url
+ properties:
+ name:
+ type: string
+ example: "Authentication failed. Please make sure you pass the API key of an API enabled user along in the Authorization header."
+ message:
+ type: string
+ example: "Authentication failed. Please make sure you pass the API key of an API enabled user along in the Authorization header."
+ url:
+ type: string
+ example: "/users"
+
+ NotFoundApiError:
+ type: object
+ required:
+ - name
+ - message
+ - url
+ properties:
+ name:
+ type: string
+ example: "Invalid user"
+ message:
+ type: string
+ example: "Invalid user"
+ url:
+ type: string
+ example: "/users/1234"
+
+ parameters:
+ userId:
+ name: userId
+ in: path
+ description: "Numeric ID of the User"
+ required: true
+ schema:
+ $ref: "#/components/schemas/ID"
+
+ securitySchemes:
+ ApiKeyAuth:
+ type: apiKey
+ in: header
+ name: Authorization
+ description: |
+ The authorization is performed by using the following header in the HTTP requests:
+
+ Authorization: YOUR_API_KEY
+
+ # requestBodies:
+
+ responses:
+ # User
+ ViewUserResponse:
+ description: "User response"
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/User"
+
+ # Errors
+ ApiErrorResponse:
+ description: "Unexpected API error"
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ApiError"
+
+ UnauthorizedApiErrorResponse:
+ description: "Authentication failed. Please make sure you pass the API key of an API enabled user along in the Authorization header."
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UnauthorizedApiError"
+
+security:
+ - ApiKeyAuth: []