mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Implement auth for organization and account keys
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
// List of scopes for Account API keys (Tokens)
|
||||
|
||||
return [
|
||||
"account" => [
|
||||
"description" => 'Access to manage account, it\'s organizations, sessions, tokens, and billing.',
|
||||
],"teams.read" => [
|
||||
"description" => 'Access to read account\'s organizations.',
|
||||
],"teams.write" => [
|
||||
"description" => 'Access to create, update and delete account\'s organizations and it\'s memberships.',
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
// List of scopes for organization (teams) API keys
|
||||
|
||||
return [
|
||||
"platforms.read" => [
|
||||
"description" => 'Access to read project\'s platforms',
|
||||
],
|
||||
"platforms.write" => [
|
||||
"description" =>
|
||||
'Access to create, update, and delete project\'s platforms',
|
||||
],
|
||||
"projects.read" => [
|
||||
"description" => 'Access to read organization\'s projects',
|
||||
],
|
||||
"projects.write" => [
|
||||
"description" =>
|
||||
"Access to create, update, and delete projects in organization",
|
||||
],
|
||||
"keys.read" => [
|
||||
"description" => 'Access to read project\'s API keys',
|
||||
],
|
||||
"keys.write" => [
|
||||
"description" =>
|
||||
"Access to create, update, and delete project\'s API keys",
|
||||
],
|
||||
"devKeys.read" => [
|
||||
"description" => 'Access to read project\'s development keys',
|
||||
],
|
||||
"devKeys.write" => [
|
||||
"description" =>
|
||||
"Access to create, update, and delete project\'s development keys",
|
||||
],
|
||||
"webhooks.read" => [
|
||||
"description" =>
|
||||
"Access to read project\'s webhooks",
|
||||
],
|
||||
"webhooks.write" => [
|
||||
"description" =>
|
||||
"Access to create, update, and delete project\'s webhooks",
|
||||
],
|
||||
];
|
||||
@@ -1478,7 +1478,7 @@ App::post('/v1/projects/:projectId/keys')
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
||||
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
|
||||
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
|
||||
->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
@@ -1620,7 +1620,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('keyId', '', new UID(), 'Key unique ID.')
|
||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
||||
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
|
||||
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
|
||||
->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
@@ -1721,7 +1721,7 @@ App::post('/v1/projects/:projectId/jwts')
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
|
||||
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
|
||||
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
|
||||
@@ -191,7 +191,7 @@ App::post('/v1/mock/api-key-unprefixed')
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$scopes = array_keys(Config::getParam('scopes'));
|
||||
$scopes = array_keys(Config::getParam('projectScopes'));
|
||||
|
||||
$key = new Document([
|
||||
'$id' => ID::unique(),
|
||||
|
||||
@@ -22,7 +22,9 @@ Config::load('collections', __DIR__ . '/../config/collections.php', $configAdapt
|
||||
Config::load('frameworks', __DIR__ . '/../config/frameworks.php', $configAdapter);
|
||||
Config::load('usage', __DIR__ . '/../config/usage.php', $configAdapter);
|
||||
Config::load('roles', __DIR__ . '/../config/roles.php', $configAdapter); // User roles and scopes
|
||||
Config::load('scopes', __DIR__ . '/../config/scopes.php', $configAdapter); // User roles and scopes
|
||||
Config::load('projectScopes', __DIR__ . '/../config/scopes/project.php', $configAdapter);
|
||||
Config::load('organizationScopes', __DIR__ . '/../config/scopes/organization.php', $configAdapter);
|
||||
Config::load('accountScopes', __DIR__ . '/../config/scopes/account.php', $configAdapter);
|
||||
Config::load('services', __DIR__ . '/../config/services.php', $configAdapter); // List of services
|
||||
Config::load('variables', __DIR__ . '/../config/variables.php', $configAdapter); // List of env variables
|
||||
Config::load('regions', __DIR__ . '/../config/regions.php', $configAdapter); // List of available regions
|
||||
|
||||
@@ -243,6 +243,8 @@ const MESSAGE_TYPE_PUSH = 'push';
|
||||
// API key types
|
||||
const API_KEY_STANDARD = 'standard';
|
||||
const API_KEY_DYNAMIC = 'dynamic';
|
||||
const API_KEY_ORGANIZATION = 'organization';
|
||||
const API_KEY_ACCOUNT = 'account';
|
||||
// Usage metrics
|
||||
const METRIC_TEAMS = 'teams';
|
||||
const METRIC_USERS = 'users';
|
||||
|
||||
@@ -1072,15 +1072,15 @@ App::setResource('previewHostname', function (Request $request, ?Key $apiKey) {
|
||||
return '';
|
||||
}, ['request', 'apiKey']);
|
||||
|
||||
App::setResource('apiKey', function (Request $request, Document $project): ?Key {
|
||||
App::setResource('apiKey', function (Request $request, Document $project, Document $team, Document $user): ?Key {
|
||||
$key = $request->getHeader('x-appwrite-key');
|
||||
|
||||
if (empty($key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Key::decode($project, $key);
|
||||
}, ['request', 'project']);
|
||||
return Key::decode($project, $team, $user, $key);
|
||||
}, ['request', 'project', 'team', 'user']);
|
||||
|
||||
App::setResource('executor', fn () => new Executor());
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ class Key
|
||||
{
|
||||
public function __construct(
|
||||
protected string $projectId,
|
||||
protected string $teamId,
|
||||
protected string $userId,
|
||||
protected string $type,
|
||||
protected string $role,
|
||||
protected array $scopes,
|
||||
@@ -99,6 +101,8 @@ class Key
|
||||
*/
|
||||
public static function decode(
|
||||
Document $project,
|
||||
Document $team,
|
||||
Document $user,
|
||||
string $key
|
||||
): Key {
|
||||
if (\str_contains($key, '_')) {
|
||||
@@ -115,6 +119,8 @@ class Key
|
||||
|
||||
$guestKey = new Key(
|
||||
$project->getId(),
|
||||
'',
|
||||
'',
|
||||
$type,
|
||||
User::ROLE_GUESTS,
|
||||
$roles[User::ROLE_GUESTS]['scopes'] ?? [],
|
||||
@@ -152,6 +158,8 @@ class Key
|
||||
|
||||
return new Key(
|
||||
$projectId,
|
||||
'',
|
||||
'',
|
||||
$type,
|
||||
$role,
|
||||
$scopes,
|
||||
@@ -185,12 +193,90 @@ class Key
|
||||
|
||||
return new Key(
|
||||
$project->getId(),
|
||||
'',
|
||||
'',
|
||||
$type,
|
||||
$role,
|
||||
$scopes,
|
||||
$name,
|
||||
$expired
|
||||
);
|
||||
case API_KEY_ACCOUNT:
|
||||
$key = $user->find(
|
||||
key: 'secret',
|
||||
find: $key,
|
||||
subject: 'keys'
|
||||
);
|
||||
|
||||
// Invalid key
|
||||
if (!$key) {
|
||||
return $guestKey;
|
||||
}
|
||||
|
||||
$expire = $key->getAttribute('expire');
|
||||
$expired = false;
|
||||
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
|
||||
$expired = true;
|
||||
}
|
||||
|
||||
$name = $key->getAttribute('name', 'UNKNOWN');
|
||||
|
||||
$role = User::ROLE_USERS;
|
||||
|
||||
$roles = Config::getParam('roles', []);
|
||||
$scopes = $roles[$role]['scopes'] ?? [];
|
||||
$scopes = $key->getAttribute('scopes', []);
|
||||
|
||||
$key = new Key(
|
||||
'',
|
||||
'',
|
||||
$user->getId(),
|
||||
$type,
|
||||
$role,
|
||||
$scopes,
|
||||
$name,
|
||||
$expired
|
||||
);
|
||||
|
||||
return $key;
|
||||
case API_KEY_ORGANIZATION:
|
||||
$key = $team->find(
|
||||
key: 'secret',
|
||||
find: $key,
|
||||
subject: 'keys'
|
||||
);
|
||||
|
||||
// Invalid key
|
||||
if (!$key) {
|
||||
return $guestKey;
|
||||
}
|
||||
|
||||
$expire = $key->getAttribute('expire');
|
||||
$expired = false;
|
||||
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
|
||||
$expired = true;
|
||||
}
|
||||
|
||||
$name = $key->getAttribute('name', 'UNKNOWN');
|
||||
|
||||
$role = User::ROLE_APPS;
|
||||
|
||||
$roles = Config::getParam('roles', []);
|
||||
$scopes = $roles[$role]['scopes'] ?? [];
|
||||
$scopes = $key->getAttribute('scopes', []);
|
||||
|
||||
$key = new Key(
|
||||
'',
|
||||
$team->getId(),
|
||||
'',
|
||||
$type,
|
||||
$role,
|
||||
$scopes,
|
||||
$name,
|
||||
$expired
|
||||
);
|
||||
|
||||
return $key;
|
||||
default:
|
||||
return $guestKey;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ class Create extends Base
|
||||
->param('logging', true, new Boolean(), 'When disabled, executions will exclude logs and errors, and will be slightly faster.', true)
|
||||
->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
|
||||
->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
|
||||
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
|
||||
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
|
||||
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Control System) deployment.', true)
|
||||
->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function.', true)
|
||||
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function.', true)
|
||||
|
||||
@@ -83,7 +83,7 @@ class Update extends Base
|
||||
->param('logging', true, new Boolean(), 'When disabled, executions will exclude logs and errors, and will be slightly faster.', true)
|
||||
->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
|
||||
->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
|
||||
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API Key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
|
||||
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API Key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
|
||||
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Controle System) deployment.', true)
|
||||
->param('providerRepositoryId', null, new Nullable(new Text(128, 0)), 'Repository ID of the repo linked to the function', true)
|
||||
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true)
|
||||
|
||||
@@ -190,7 +190,7 @@ class Screenshot extends Action
|
||||
'cookie' => $cookieConsole
|
||||
], [
|
||||
'name' => 'Screenshot API key',
|
||||
'scopes' => \array_keys(Config::getParam('scopes', []))
|
||||
'scopes' => \array_keys(Config::getParam('projectScopes', []))
|
||||
]);
|
||||
|
||||
if ($response['headers']['status-code'] !== 201) {
|
||||
|
||||
Reference in New Issue
Block a user