1
0
Fork 0
mirror of synced 2024-06-20 03:30:30 +12:00

Merge branch '0.8.x' of https://github.com/appwrite/appwrite into feat-upgrade-php-version

This commit is contained in:
Torsten Dittmann 2021-04-23 17:06:42 +02:00
commit 3d960a2971
61 changed files with 994 additions and 277 deletions

View file

@ -8,6 +8,13 @@
- Added ARM support
- Splited token & session models to become 2 different internal entities (#922)
- Added Dart 2.12 as a new Cloud Functions runtime (#989)
- Added option to disable email/password
- Added option to disable anonymous login (need to merge and apply changed)
- Added option to disable JWT auth
- Added option to disable team invites
- Option to limit number of users (good for app launches + god account PR)
- Added 2 new endpoints to the projects API to allow new settings
- Enabled 501 errors (Not Implemented) from the error handler
- Added Python 3.9 as a new Cloud Functions runtime
- Added Deno 1.8 as a new Cloud Functions runtime (#989)
- ClamAV is now disabled by default to allow lower min requirments for Appwrite (#1064)

42
app/config/auth.php Normal file
View file

@ -0,0 +1,42 @@
<?php
// Auth methods
return [
'email-password' => [
'name' => 'Email/Password',
'key' => 'usersAuthEmailPassword',
'icon' => '/images/users/email-password.png',
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateSession',
'enabled' => true,
],
'anonymous' => [
'name' => 'Anonymous',
'key' => 'usersAuthAnonymous',
'icon' => '/images/users/anonymous.png',
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateAnonymousSession',
'enabled' => true,
],
'invites' => [
'name' => 'Invites',
'key' => 'usersAuthInvites',
'icon' => '/images/users/invites.png',
'docs' => 'https://appwrite.io/docs/client/teams?sdk=web#teamsCreateMembership',
'enabled' => true,
],
'jwt' => [
'name' => 'JWT',
'key' => 'usersAuthJWT',
'icon' => '/images/users/jwt.png',
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateJWT',
'enabled' => true,
],
'phone' => [
'name' => 'Phone',
'key' => 'usersAuthPhone',
'icon' => '/images/users/phone.png',
'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreatePhoneSession',
'docs' => '',
'enabled' => false,
],
];

View file

@ -5,6 +5,7 @@ use Utopia\Config\Config;
use Appwrite\Database\Database;
$providers = Config::getParam('providers', []);
$auth = Config::getParam('auth', []);
$collections = [
'console' => [
@ -770,6 +771,14 @@ $collections = [
'default' => '',
'required' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Max users allowed',
'key' => 'usersAuthLimit',
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
'default' => 0,
'required' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Webhooks',
@ -1710,4 +1719,15 @@ foreach ($providers as $index => $provider) {
];
}
foreach ($auth as $index => $method) {
$collections[Database::SYSTEM_COLLECTION_PROJECTS]['rules'][] = [
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => $method['name'] || '',
'key' => $method['key'] || '',
'type' => Database::SYSTEM_VAR_TYPE_BOOLEAN,
'default' => true,
'required' => false,
];
}
return $collections;

View file

@ -6,6 +6,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.amazon.com/apps-and-games/services-and-apis',
'icon' => 'icon-amazon',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -15,6 +16,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.apple.com/',
'icon' => 'icon-apple',
'enabled' => true,
'sandbox' => false,
'form' => 'apple.phtml', // Perperation for adding ability to customized OAuth UI forms, currently handled hardcoded.
'beta' => true,
'mock' => false,
@ -24,6 +26,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.atlassian.com/bitbucket',
'icon' => 'icon-bitbucket',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -33,6 +36,7 @@ return [ // Ordered by ABC.
'developers' => 'https://dev.bitly.com/v4_documentation.html',
'icon' => 'icon-bitly',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
@ -42,6 +46,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.box.com/reference/',
'icon' => 'icon-box',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
@ -51,6 +56,7 @@ return [ // Ordered by ABC.
'developers' => 'https://discordapp.com/developers/docs/topics/oauth2',
'icon' => 'icon-discord',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -60,6 +66,7 @@ return [ // Ordered by ABC.
'developers' => 'https://www.dropbox.com/developers/documentation',
'icon' => 'icon-dropbox',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -69,6 +76,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developers.facebook.com/',
'icon' => 'icon-facebook',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -78,6 +86,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.github.com/',
'icon' => 'icon-github-circled',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -87,6 +96,7 @@ return [ // Ordered by ABC.
'developers' => 'https://docs.gitlab.com/ee/api/',
'icon' => 'icon-gitlab',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -96,6 +106,7 @@ return [ // Ordered by ABC.
'developers' => 'https://support.google.com/googleapi/answer/6158849',
'icon' => 'icon-google',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -105,6 +116,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.linkedin.com/',
'icon' => 'icon-linkedin',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -114,6 +126,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.microsoft.com/en-us/',
'icon' => 'icon-windows',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -123,15 +136,17 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.paypal.com/docs/api/overview/',
'icon' => 'icon-paypal',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
],
'paypalSandbox' => [
'name' => 'PayPal (Sandbox)',
'name' => 'PayPal',
'developers' => 'https://developer.paypal.com/docs/api/overview/',
'icon' => 'icon-paypal',
'enabled' => true,
'sandbox' => true,
'form' => false,
'beta' => false,
'mock' => false
@ -141,6 +156,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.salesforce.com/docs/',
'icon' => 'icon-salesforce',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -150,6 +166,7 @@ return [ // Ordered by ABC.
'developers' => 'https://api.slack.com/',
'icon' => 'icon-slack',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -159,6 +176,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.spotify.com/documentation/general/guides/authorization-guide/',
'icon' => 'icon-spotify',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -168,15 +186,17 @@ return [ // Ordered by ABC.
'developers' => 'https://developers.tradeshift.com/docs/api',
'icon' => 'icon-tradeshift',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'tradeshiftBox' => [
'name' => 'Tradeshift Sandbox',
'name' => 'Tradeshift',
'developers' => 'https://developers.tradeshift.com/docs/api',
'icon' => 'icon-tradeshiftbox',
'enabled' => true,
'sandbox' => true,
'form' => false,
'beta' => false,
'mock' => false,
@ -186,6 +206,7 @@ return [ // Ordered by ABC.
'developers' => 'https://dev.twitch.tv/docs/authentication',
'icon' => 'icon-twitch',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -195,6 +216,7 @@ return [ // Ordered by ABC.
'developers' => 'https://vk.com/dev',
'icon' => 'icon-vk',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -204,6 +226,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.yahoo.com/oauth2/guide/flows_authcode/',
'icon' => 'icon-yahoo',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -213,6 +236,7 @@ return [ // Ordered by ABC.
'developers' => 'https://tech.yandex.com/oauth/',
'icon' => 'icon-yandex',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
@ -238,6 +262,7 @@ return [ // Ordered by ABC.
'developers' => 'https://developer.wordpress.com/docs/oauth2/',
'icon' => 'icon-wordpress',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false
@ -248,6 +273,7 @@ return [ // Ordered by ABC.
'developers' => 'https://appwrite.io',
'icon' => 'icon-appwrite',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => true,

File diff suppressed because one or more lines are too long

View file

@ -33,7 +33,7 @@ return [
],
[
'name' => '_APP_OPTIONS_FORCE_HTTPS',
'description' => 'Allows you to force HTTPS connection to your API. This feature redirects any HTTP call to HTTPS and adds the \'Strict-Transport-Security\' header to all HTTP responses. By default, set to \'disabled\'. To enable, set to \'enabled\'. This feature will work only when your ports are set to default 80 and 443.',
'description' => 'Allows you to force HTTPS connection to your API. This feature redirects any HTTP call to HTTPS and adds the \'Strict-Transport-Security\' header to all HTTP responses. By default, set to \'enabled\'. To disable, set to \'disabled\'. This feature will work only when your ports are set to default 80 and 443.',
'introduction' => '',
'default' => 'disabled',
'required' => false,

View file

@ -31,9 +31,10 @@ $oauthDefaultFailure = App::getEnv('_APP_HOME').'/auth/oauth2/failure';
App::post('/v1/account')
->desc('Create Account')
->groups(['api', 'account'])
->groups(['api', 'account', 'auth'])
->label('event', 'account.create')
->label('scope', 'public')
->label('auth.type', 'emailPassword')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', 'create')
@ -75,6 +76,22 @@ App::post('/v1/account')
}
}
$limit = $project->getAttribute('usersAuthLimit', 0);
if ($limit !== 0) {
$projectDB->getCollection([ // Count users
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
$sum = $projectDB->getSum();
if($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
}
}
$profile = $projectDB->getCollectionFirst([ // Get user by email address
'limit' => 1,
'filters' => [
@ -133,9 +150,10 @@ App::post('/v1/account')
App::post('/v1/account/sessions')
->desc('Create Account Session')
->groups(['api', 'account'])
->groups(['api', 'account', 'auth'])
->label('event', 'account.sessions.create')
->label('scope', 'public')
->label('auth.type', 'emailPassword')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createSession')
@ -469,7 +487,23 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
],
]);
if (!$user || empty($user->getId())) { // Last option -> create user alone, generate random password
if (!$user || empty($user->getId())) { // Last option -> create the user, generate random password
$limit = $project->getAttribute('usersAuthLimit', 0);
if ($limit !== 0) {
$projectDB->getCollection([ // Count users
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
$sum = $projectDB->getSum();
if($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
}
}
Authorization::disable();
try {
@ -579,9 +613,10 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
App::post('/v1/account/sessions/anonymous')
->desc('Create Anonymous Session')
->groups(['api', 'account'])
->groups(['api', 'account', 'auth'])
->label('event', 'account.sessions.create')
->label('scope', 'public')
->label('auth.type', 'anonymous')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createAnonymousSession')
@ -611,10 +646,30 @@ App::post('/v1/account/sessions/anonymous')
$protocol = $request->getProtocol();
if ($user->getId() || 'console' === $project->getId()) {
if ('console' === $project->getId()) {
throw new Exception('Failed to create anonymous user.', 401);
}
if ($user->getId()) {
throw new Exception('Cannot create an anonymous user when logged in.', 401);
}
$limit = $project->getAttribute('usersAuthLimit', 0);
if ($limit !== 0) {
$projectDB->getCollection([ // Count users
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
$sum = $projectDB->getSum();
if($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
}
}
Authorization::disable();
try {
$user = $projectDB->createDocument([
@ -635,7 +690,6 @@ App::post('/v1/account/sessions/anonymous')
} catch (Exception $th) {
throw new Exception('Failed saving user to DB', 500);
}
Authorization::reset();
if (false === $user) {
@ -703,8 +757,9 @@ App::post('/v1/account/sessions/anonymous')
App::post('/v1/account/jwt')
->desc('Create Account JWT')
->groups(['api', 'account'])
->groups(['api', 'account', 'auth'])
->label('scope', 'account')
->label('auth.type', 'jwt')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createJWT')
@ -1342,7 +1397,7 @@ App::post('/v1/account/recovery')
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$profile = $projectDB->getCollectionFirst([ // Get user by email address
@ -1432,7 +1487,7 @@ App::post('/v1/account/recovery')
$recovery // Hide secret for clients, sp
->setAttribute('secret',
($isPreviliggedUser || $isAppUser) ? $secret : '');
($isPrivilegedUser || $isAppUser) ? $secret : '');
$audits
->setParam('userId', $profile->getId())
@ -1560,7 +1615,7 @@ App::post('/v1/account/verification')
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $mails */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$verificationSecret = Auth::tokenGenerator();
@ -1635,7 +1690,7 @@ App::post('/v1/account/verification')
$verification // Hide secret for clients, sp
->setAttribute('secret',
($isPreviliggedUser || $isAppUser) ? $verificationSecret : '');
($isPrivilegedUser || $isAppUser) ? $verificationSecret : '');
$audits
->setParam('userId', $user->getId())

View file

@ -8,6 +8,7 @@ use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Appwrite\Network\Validator\URL;
use Utopia\Validator\Range;
use Utopia\Validator\Integer;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
use Appwrite\Auth\Auth;
@ -447,6 +448,80 @@ App::patch('/v1/projects/:projectId/oauth2')
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/limit')
->desc('Update Project users limit')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateAuthLimit')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('limit', false, new Integer(true), 'Set the max number of users allowed in this project. Use 0 for unlimited.')
->inject('response')
->inject('consoleDB')
->action(function ($projectId, $limit, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS != $project->getCollection()) {
throw new Exception('Project not found', 404);
}
if (false === $consoleDB->updateDocument(
\array_merge($project->getArrayCopy(), [
'usersAuthLimit' => $limit,
]))
) {
throw new Exception('Failed saving project to DB', 500);
};
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/:method')
->desc('Update Project auth method status. Use this endpoint to enable or disable a given auth method for this project.')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateAuthStatus')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: '.implode(',', \array_keys(Config::getParam('auth'))), false)
->param('status', false, new Boolean(true), 'Set the status of this auth method.')
->inject('response')
->inject('consoleDB')
->action(function ($projectId, $method, $status, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */
$project = $consoleDB->getDocument($projectId);
$auth = Config::getParam('auth')[$method] ?? [];
$authKey = $auth['key'] ?? '';
$status = ($status === '1' || $status === 'true' || $status === 1 || $status === true);
if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS != $project->getCollection()) {
throw new Exception('Project not found', 404);
}
if (false === $consoleDB->updateDocument(
\array_merge($project->getArrayCopy(), [
$authKey => $status,
]))
) {
throw new Exception('Failed saving project to DB', 500);
};
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::delete('/v1/projects/:projectId')
->desc('Delete Project')
->groups(['api', 'projects'])

View file

@ -44,7 +44,7 @@ App::post('/v1/teams')
Authorization::disable();
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$team = $projectDB->createDocument([
@ -54,7 +54,7 @@ App::post('/v1/teams')
'write' => ['team:{self}/owner'],
],
'name' => $name,
'sum' => ($isPreviliggedUser || $isAppUser) ? 0 : 1,
'sum' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
'dateCreated' => \time(),
]);
@ -64,7 +64,7 @@ App::post('/v1/teams')
throw new Exception('Failed saving team to DB', 500);
}
if (!$isPreviliggedUser && !$isAppUser) { // Don't add user on server mode
if (!$isPrivilegedUser && !$isAppUser) { // Don't add user on server mode
$membership = new Document([
'$collection' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
'$permissions' => [
@ -251,9 +251,10 @@ App::delete('/v1/teams/:teamId')
App::post('/v1/teams/:teamId/memberships')
->desc('Create Team Membership')
->groups(['api', 'teams'])
->groups(['api', 'teams', 'auth'])
->label('event', 'teams.memberships.create')
->label('scope', 'teams.write')
->label('auth.type', 'invites')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'createMembership')
@ -282,7 +283,7 @@ App::post('/v1/teams/:teamId/memberships')
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $mails */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$name = (empty($name)) ? $email : $name;
@ -311,6 +312,22 @@ App::post('/v1/teams/:teamId/memberships')
if (empty($invitee)) { // Create new user if no user with same email found
$limit = $project->getAttribute('usersAuthLimit', 0);
if ($limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed.
$projectDB->getCollection([ // Count users
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
],
]);
$sum = $projectDB->getSum();
if($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
}
}
Authorization::disable();
try {
@ -354,7 +371,7 @@ App::post('/v1/teams/:teamId/memberships')
}
}
if (!$isOwner && !$isPreviliggedUser && !$isAppUser) { // Not owner, not admin, not app (server)
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
throw new Exception('User is not allowed to send invitations for this team', 401);
}
@ -370,12 +387,12 @@ App::post('/v1/teams/:teamId/memberships')
'teamId' => $team->getId(),
'roles' => $roles,
'invited' => \time(),
'joined' => ($isPreviliggedUser || $isAppUser) ? \time() : 0,
'confirm' => ($isPreviliggedUser || $isAppUser),
'joined' => ($isPrivilegedUser || $isAppUser) ? \time() : 0,
'confirm' => ($isPrivilegedUser || $isAppUser),
'secret' => Auth::hash($secret),
]);
if ($isPreviliggedUser || $isAppUser) { // Allow admin to create membership
if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership
Authorization::disable();
$membership = $projectDB->createDocument($membership->getArrayCopy());
@ -427,7 +444,7 @@ App::post('/v1/teams/:teamId/memberships')
->setParam('{{text-cta}}', '#ffffff')
;
if (!$isPreviliggedUser && !$isAppUser) { // No need in comfirmation when in admin or app mode
if (!$isPrivilegedUser && !$isAppUser) { // No need in comfirmation when in admin or app mode
$mails
->setParam('event', 'teams.membership.create')
->setParam('from', ($project->getId() === 'console') ? '' : \sprintf($locale->getText('account.emails.team'), $project->getAttribute('name')))

View file

@ -271,7 +271,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
switch ($error->getCode()) {
switch ($error->getCode()) { // Don't show 500 errors!
case 400: // Error allowed publicly
case 401: // Error allowed publicly
case 402: // Error allowed publicly
@ -280,6 +280,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) {
case 409: // Error allowed publicly
case 412: // Error allowed publicly
case 429: // Error allowed publicly
case 501: // Error allowed publicly
$code = $error->getCode();
$message = $error->getMessage();
break;

View file

@ -61,12 +61,12 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
;
}
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
if (($abuse->check() // Route is rate-limited
&& App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not diabled
&& (!$isAppUser && !$isPreviliggedUser)) // User is not an admin or API key
&& (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key
{
throw new Exception('Too many requests', 429);
}
@ -110,6 +110,61 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes'], 'api');
App::init(function ($utopia, $request, $response, $project, $user) {
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Registry\Registry $register */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $functions */
$route = $utopia->match($request);
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
if($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
return;
}
switch ($route->getLabel('auth.type', '')) {
case 'emailPassword':
if($project->getAttribute('usersAuthEmailPassword', true) === false) {
throw new Exception('Email / Password authentication is disabled for this project', 501);
}
break;
case 'anonymous':
if($project->getAttribute('usersAuthAnonymous', true) === false) {
throw new Exception('Anonymous authentication is disabled for this project', 501);
}
break;
case 'invites':
if($project->getAttribute('usersAuthInvites', true) === false) {
throw new Exception('Invites authentication is disabled for this project', 501);
}
break;
case 'jwt':
if($project->getAttribute('usersAuthJWT', true) === false) {
throw new Exception('JWT authentication is disabled for this project', 501);
}
break;
default:
throw new Exception('Unsupported authentication route');
break;
}
}, ['utopia', 'request', 'response', 'project', 'user'], 'auth');
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $mode) {
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */

View file

@ -323,7 +323,10 @@ App::get('/console/users')
$page = new View(__DIR__.'/../../views/console/users/index.phtml');
$page->setParam('providers', Config::getParam('providers'));
$page
->setParam('auth', Config::getParam('auth'))
->setParam('providers', Config::getParam('providers'))
;
$layout
->setParam('title', APP_NAME.' - Users')

View file

@ -75,6 +75,7 @@ App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
* ENV vars
*/
Config::load('events', __DIR__.'/config/events.php');
Config::load('auth', __DIR__.'/config/auth.php');
Config::load('providers', __DIR__.'/config/providers.php');
Config::load('platforms', __DIR__.'/config/platforms.php');
Config::load('collections', __DIR__.'/config/collections.php');

View file

@ -1,5 +1,6 @@
<?php
$providers = $this->getParam('providers', []);
$auth = $this->getParam('auth', []);
?>
<div class="cover">
@ -300,9 +301,98 @@ $providers = $this->getParam('providers', []);
</li>
<li data-state="/console/users/providers?project={{router.params.project}}">
<h2>OAuth2 Providers</h2>
<p data-ls-if="{{console-project.usersAuthLimit}} == 0" class="text-fade text-size-small margin-bottom pull-end">Unlimited Users <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Set Limit</a></p>
<p data-ls-if="{{console-project.usersAuthLimit}} != 0" class="text-fade text-size-small margin-bottom pull-end"><span data-ls-bind="{{console-project.usersAuthLimit|statsTotal}}"></span> Users allowed <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Change Limit</a></p>
<h2>Settings</h2>
<div class="margin-bottom margin-top-large"
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="project-update-auth-users-limit">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Max Allowed Users</h1>
<form data-debug="1"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Users Limit"
data-service="projects.updateAuthLimit"
data-scope="console"
data-event="submit"
data-param-project-id="{{router.params.project}}"
data-success="alert,trigger"
data-success-param-alert-text="Updated project users limit successfully"
data-success-param-trigger-events="projects.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update project users limit"
data-failure-param-alert-classname="error">
<input name="limit" id="users-limit" type="number" data-ls-bind="{{console-project.usersAuthLimit}}" data-cast-to="numeric" min="0" />
<div class="info row thin margin-bottom margin-top">
<div class="col span-1">
<i class="icon-info-circled text-sign"></i>
</div>
<div class="col span-11">This limit will prevent new users from signing up for your project, no matter what auth method has been used. You will still be able to create users and team memberships from your Appwrite console. For an unlimited amount of users, set the limit to 0.</div>
</div>
<button>Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
<p class="text-fade margin-bottom">Choose auth methods you wish to use.</p>
<ul class="tiles cell-3 margin-bottom-small">
<?php foreach($auth as $index => $method):
$key = $method['key'] ?? '';
$name = $method['name'] ?? '';
$icon = $method['icon'] ?? '';
$docs = $method['docs'] ?? '';
$enabled = $method['enabled'] ?? false;
?>
<li class="">
<div class="box padding-small margin-bottom clear">
<?php if($enabled): ?>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Project Auth Status (<?php echo $this->escape($name); ?>)"
data-service="projects.updateAuthStatus"
data-scope="console"
data-event="change"
data-param-project-id="{{router.params.project}}"
data-success="alert,trigger"
data-success-param-alert-text="Updated project auth status successfully"
data-success-param-trigger-events="projects.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update project auth status settings"
data-failure-param-alert-classname="error">
<input name="method" id="<?php echo $this->escape($key); ?>" type="hidden" autocomplete="off" value="<?php echo $this->escape($index); ?>">
<input name="status" type="hidden" data-forms-switch data-ls-bind="{{console-project.<?php echo $this->escape($key); ?>}}" data-cast-to="boolean" class="pull-end" />
</form>
<?php endif; ?>
<img src="<?php echo $this->escape($icon); ?>?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="Email/Password Logo" class="pull-start provider margin-end" />
<span class="text-size-small"><?php echo $this->escape($name); ?><?php if(!$enabled): ?> <spann class="text-fade text-size-xs">soon</span><?php endif; ?></span>
<?php if($docs): ?>
<p class="margin-bottom-no text-one-liner text-size-small">
<a href="<?php echo $this->escape($docs); ?>" target="_blank" rel="noopener">Docs<i class="icon-link-ext"></i></a>
</p>
<?php endif; ?>
</div>
</li>
<?php endforeach; ?>
</ul>
<h3>OAuth2 Providers</h3>
<div class="margin-bottom margin-top"
data-service="projects.get"
data-event="load,projects.create,projects.update,projects.deleteProject"
data-name="console-project"
@ -312,6 +402,7 @@ $providers = $this->getParam('providers', []);
<?php foreach ($providers as $provider => $data):
if (isset($data['enabled']) && !$data['enabled']) { continue; }
if (isset($data['mock']) && $data['mock']) { continue; }
$sandbox = $data['sandbox'] ?? false;
$form = $data['form'] ?? false;
$name = $data['name'] ?? 'Unknown';
$beta = $data['beta'] ?? false;
@ -320,7 +411,7 @@ $providers = $this->getParam('providers', []);
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="provider-update-<?php echo $provider; ?>">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1><?php echo $this->escape($name); ?> OAuth2 Settings</h1>
<h1><?php echo $this->escape($name); ?> <?php if($sandbox): ?>Sandbox<?php endif; ?> OAuth2 Settings</h1>
<form
data-analytics
@ -383,10 +474,10 @@ $providers = $this->getParam('providers', []);
<button class="switch pull-end" data-ls-ui-trigger="provider-update-<?php echo $this->escape($provider); ?>"></button>
</span>
<img src="/images/oauth2/<?php echo $this->escape(strtolower($provider)); ?>.png?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="<?php echo $this->escape(ucfirst($provider)); ?> Logo" class="pull-start provider margin-end" />
<img src="/images/users/oauth2/<?php echo $this->escape(strtolower($provider)); ?>.png?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="<?php echo $this->escape(ucfirst($provider)); ?> Logo" class="pull-start provider margin-end" />
<span class="text-size-small">
<?php echo $this->escape($name); ?> <?php if($beta): ?>(beta)<?php endif; ?>
<?php echo $this->escape($name); ?> <?php if($sandbox): ?><span class="text-size-xs text-fade">sandbox</span><?php endif; ?> <?php if($beta): ?><span class="text-size-xs text-fade">beta</span><?php endif; ?>
</span>
<p class="margin-bottom-no text-one-liner text-size-small">

View file

@ -52,7 +52,6 @@
"utopia-php/swoole": "0.2.*",
"utopia-php/storage": "0.4.*",
"utopia-php/image": "0.2.*",
"resque/php-resque": "1.3.6",
"matomo/device-detector": "4.2.2",
"dragonmantank/cron-expression": "3.1.0",

72
composer.lock generated
View file

@ -1249,12 +1249,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce",
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce",
"shasum": ""
},
"require": {
@ -1266,7 +1266,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.22-dev"
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1304,7 +1304,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1"
"source": "https://github.com/symfony/polyfill-ctype/tree/main"
},
"funding": [
{
@ -1320,7 +1320,7 @@
"type": "tidelift"
}
],
"time": "2021-01-07T16:49:33+00:00"
"time": "2021-02-19T12:13:01+00:00"
},
{
"name": "utopia-php/abuse",
@ -5060,12 +5060,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170"
"reference": "053f7184175d5417c933817341c5cc0053ddacd5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170",
"reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/053f7184175d5417c933817341c5cc0053ddacd5",
"reference": "053f7184175d5417c933817341c5cc0053ddacd5",
"shasum": ""
},
"require": {
@ -5077,7 +5077,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.22-dev"
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -5117,7 +5117,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1"
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/main"
},
"funding": [
{
@ -5133,7 +5133,7 @@
"type": "tidelift"
}
],
"time": "2021-01-22T09:19:47+00:00"
"time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
@ -5141,12 +5141,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248"
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248",
"reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
"shasum": ""
},
"require": {
@ -5158,7 +5158,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.22-dev"
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -5201,7 +5201,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1"
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/main"
},
"funding": [
{
@ -5217,7 +5217,7 @@
"type": "tidelift"
}
],
"time": "2021-01-22T09:19:47+00:00"
"time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/polyfill-mbstring",
@ -5225,12 +5225,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
"reference": "298b87cbbe99cb2c9f88fb1d1de78833b64b483e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
"reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/298b87cbbe99cb2c9f88fb1d1de78833b64b483e",
"reference": "298b87cbbe99cb2c9f88fb1d1de78833b64b483e",
"shasum": ""
},
"require": {
@ -5242,7 +5242,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.22-dev"
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -5281,7 +5281,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1"
"source": "https://github.com/symfony/polyfill-mbstring/tree/main"
},
"funding": [
{
@ -5297,7 +5297,7 @@
"type": "tidelift"
}
],
"time": "2021-01-22T09:19:47+00:00"
"time": "2021-04-19T09:32:22+00:00"
},
{
"name": "symfony/polyfill-php73",
@ -5305,12 +5305,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
"reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
"reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
"reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010",
"reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010",
"shasum": ""
},
"require": {
@ -5319,7 +5319,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.22-dev"
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -5360,7 +5360,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1"
"source": "https://github.com/symfony/polyfill-php73/tree/main"
},
"funding": [
{
@ -5376,7 +5376,7 @@
"type": "tidelift"
}
],
"time": "2021-01-07T16:49:33+00:00"
"time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/polyfill-php80",
@ -5384,12 +5384,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
"reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
"reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0",
"reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0",
"shasum": ""
},
"require": {
@ -5398,7 +5398,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.22-dev"
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -5443,7 +5443,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1"
"source": "https://github.com/symfony/polyfill-php80/tree/main"
},
"funding": [
{
@ -5459,7 +5459,7 @@
"type": "tidelift"
}
],
"time": "2021-01-07T16:49:33+00:00"
"time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/service-contracts",

View file

@ -0,0 +1,15 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
let promise = sdk.projects.updateAuthLimit('[PROJECT_ID]', '');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -0,0 +1,15 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
.setKey('919c2d18fb5d4...a2ae413da83346ad2') // Your secret API key
;
let promise = sdk.projects.updateAuthStatus('[PROJECT_ID]', 'email-password', false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -27,7 +27,7 @@ return http.post(path,{'content-type':'application/json',},payload);},delete:fun
if(password===undefined){throw new Error('Missing required parameter: "password"');}
let path='/account/email';let payload={};if(email){payload['email']=email;}
if(password){payload['password']=password;}
return http.patch(path,{'content-type':'application/json',},payload);},createJWT:function(){let path='/account/jwt';let payload={};return http.post(path,{'content-type':'application/json',},payload);},getLogs:function(){let path='/account/logs';let payload={};return http.get(path,{'content-type':'application/json',},payload);},updateName:function(name){if(name===undefined){throw new Error('Missing required parameter: "name"');}
return http.patch(path,{'content-type':'application/json',},payload);},getLogs:function(){let path='/account/logs';let payload={};return http.get(path,{'content-type':'application/json',},payload);},updateName:function(name){if(name===undefined){throw new Error('Missing required parameter: "name"');}
let path='/account/name';let payload={};if(name){payload['name']=name;}
return http.patch(path,{'content-type':'application/json',},payload);},updatePassword:function(password,oldPassword){if(password===undefined){throw new Error('Missing required parameter: "password"');}
if(oldPassword===undefined){throw new Error('Missing required parameter: "oldPassword"');}
@ -244,7 +244,14 @@ if(legalTaxId){payload['legalTaxId']=legalTaxId;}
return http.patch(path,{'content-type':'application/json',},payload);},delete:function(projectId,password){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(password===undefined){throw new Error('Missing required parameter: "password"');}
let path='/projects/{projectId}'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(password){payload['password']=password;}
return http.delete(path,{'content-type':'application/json',},payload);},listDomains:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
return http.delete(path,{'content-type':'application/json',},payload);},updateAuthLimit:function(projectId,limit){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(limit===undefined){throw new Error('Missing required parameter: "limit"');}
let path='/projects/{projectId}/auth/limit'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
return http.patch(path,{'content-type':'application/json',},payload);},updateAuthStatus:function(projectId,method,status){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(method===undefined){throw new Error('Missing required parameter: "method"');}
if(status===undefined){throw new Error('Missing required parameter: "status"');}
let path='/projects/{projectId}/auth/{method}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{method}','g'),method);let payload={};if(status){payload['status']=status;}
return http.patch(path,{'content-type':'application/json',},payload);},listDomains:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/domains'.replace(new RegExp('{projectId}','g'),projectId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},createDomain:function(projectId,domain){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(domain===undefined){throw new Error('Missing required parameter: "domain"');}
let path='/projects/{projectId}/domains'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(domain){payload['domain']=domain;}
@ -2284,8 +2291,8 @@ if(element.array){document[element.key]=[];}}}}}};let getParams=function getPara
while((match=REGEX_PARAMETERS_VALUES.exec(functionAsString))){params.push(match[1]);}
return params;};let getValue=function(key,prefix,data){let result=null;if(!key){return null;}
let attrKey=prefix+key.charAt(0).toUpperCase()+key.slice(1);if(element.dataset[attrKey]){result=expression.parse(element.dataset[attrKey]);if(element.dataset[attrKey+"CastTo"]==="array"){result=result.split(",");}}
if(data[key]){result=data[key];}
if(!result){result="";}
if(typeof data[key]!=='undefined'){result=data[key];}
if(typeof result==='undefined'){result="";}
return result;};let resolve=function(target,prefix="param",data={}){if(!target){return function(){};}
let args=getParams(target);return target.apply(target,args.map(function(value){let result=getValue(value,prefix,data);return result;}));};let exec=function(event){let parsedSuccess=expression.parse(success);let parsedFailure=expression.parse(failure);let parsedAction=expression.parse(action);parsedSuccess=parsedSuccess&&parsedSuccess!=""?parsedSuccess.split(",").map(element=>element.trim()):[];parsedFailure=parsedFailure&&parsedFailure!=""?parsedFailure.split(",").map(element=>element.trim()):[];element.$lsSkip=true;element.classList.add("load-service-start");if(!document.body.contains(element)){element=undefined;return false;}
if(event){event.preventDefault();}
@ -2350,7 +2357,7 @@ score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){va
if(rtl.isRTL(content)){paragraph.style.direction='rtl';paragraph.style.textAlign='right';}
else{paragraph.style.direction='ltr';paragraph.style.textAlign='left';}
last=paragraph;}};var santize=function(e){clean(e);alignText(e);};element.addEventListener("change",function(){editor.content.innerHTML=markdown.render(element.value);alignText();});editor.content.setAttribute("placeholder",element.placeholder);editor.content.innerHTML=markdown.render(element.value);editor.content.tabIndex=0;alignText();editor.content.onkeydown=function preventTab(event){if(event.which===9){event.preventDefault();if(document.activeElement){var focussable=Array.prototype.filter.call(document.querySelectorAll('a:not([disabled]), button:not([disabled]), select:not([disabled]), input[type=text]:not([disabled]), input[type=checkbox]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'),function(element){return(element.offsetWidth>0||element.offsetHeight>0||element===document.activeElement);});var index=focussable.indexOf(document.activeElement);if(index>-1){if(event.shiftKey){var prevElement=focussable[index-1]||focussable[focussable.length-1];prevElement.focus();}else{var nextElement=focussable[index+1]||focussable[0];nextElement.focus();}}}}};div.addEventListener("paste",santize);div.addEventListener("drop",santize);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-remove",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){element.parentNode.removeChild(element);});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-run",repeat:false,controller:function(element,expression,container){let action=expression.parse(element.dataset["formsRun"]||'');element.addEventListener('click',function(){return container.path(action)();});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-select-all",controller:function(element){let select=document.createElement("button");let unselect=document.createElement("button");select.textContent='Select All';unselect.textContent='Unselect All';select.classList.add('link');select.classList.add('margin-top-tiny');select.classList.add('margin-start-small');select.classList.add('text-size-small');select.classList.add('pull-end');unselect.classList.add('link');unselect.classList.add('margin-top-tiny');unselect.classList.add('margin-start-small');unselect.classList.add('text-size-small');unselect.classList.add('pull-end');select.type='button';unselect.type='button';element.parentNode.insertBefore(select,element);element.parentNode.insertBefore(unselect,element);select.addEventListener('click',function(){let checkboxes=element.querySelectorAll("input[type='checkbox']");for(var i=0;i<checkboxes.length;i++){checkboxes[i].checked=true;checkboxes[i].dispatchEvent(new Event('change'));}})
unselect.addEventListener('click',function(){let checkboxes=element.querySelectorAll("input[type='checkbox']");for(var i=0;i<checkboxes.length;i++){checkboxes[i].checked=false;checkboxes[i].dispatchEvent(new Event('change'));}})}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-show-secret",controller:function(element,document){let button=document.createElement('span');button.className="link pull-end text-size-small margin-top-negative icon-eye";button.innerHTML=(element.type=='password')?'Show Secret':'Hide Secret';button.style.visibility=(element.value=='')?'hidden':'visible';element.insertAdjacentElement("beforebegin",button);button.addEventListener("click",function(event){switch(element.type){case"password":element.type="text";button.innerHTML='Hide Secret';break;case"text":element.type="password";button.innerHTML='Show Secret';break;default:console.warn("data-forms-show-secret: element.type NOT text NOR password");}});let sync=function(event){button.style.visibility=(element.value=='')?'hidden':'visible';};element.addEventListener("keyup",sync);element.addEventListener("change",sync);},});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-switch",controller:function(element){let input=window.document.createElement("input");input.type="checkbox";input.className="button switch";let syncA=function(){let value=input.checked?"true":"false"
unselect.addEventListener('click',function(){let checkboxes=element.querySelectorAll("input[type='checkbox']");for(var i=0;i<checkboxes.length;i++){checkboxes[i].checked=false;checkboxes[i].dispatchEvent(new Event('change'));}})}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-show-secret",controller:function(element,document){let button=document.createElement('span');button.className="link pull-end text-size-small margin-top-negative icon-eye";button.innerHTML=(element.type=='password')?'Show Secret':'Hide Secret';button.style.visibility=(element.value=='')?'hidden':'visible';element.insertAdjacentElement("beforebegin",button);button.addEventListener("click",function(event){switch(element.type){case"password":element.type="text";button.innerHTML='Hide Secret';break;case"text":element.type="password";button.innerHTML='Show Secret';break;default:console.warn("data-forms-show-secret: element.type NOT text NOR password");}});let sync=function(event){button.style.visibility=(element.value=='')?'hidden':'visible';};element.addEventListener("keyup",sync);element.addEventListener("change",sync);},});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-switch",controller:function(element){let input=window.document.createElement("input");input.type="checkbox";input.className="button switch "+element.className;let syncA=function(){let value=input.checked?"true":"false"
let old=element.value;element.value=value;if(value!==old){element.dispatchEvent(new Event('change'));}};let syncB=function(){input.checked=(element.value==="true");};input.addEventListener("input",syncA);input.addEventListener("change",syncA);element.addEventListener("input",syncB);element.addEventListener("change",syncB);syncA();element.parentNode.insertBefore(input,element);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-tags",controller:function(element){let array=[];let tags=window.document.createElement("div");let preview=window.document.createElement("ul");let add=window.document.createElement("input");let listen=function(event){if((event.key==="Enter"||event.key===" "||event.key==="Tab")&&add.value.length>0){array.push(add.value);add.value="";element.value=JSON.stringify(array);check();if(event.key!=="Tab"){event.preventDefault();}}
if((event.key==="Backspace"||event.key==="Delete")&&add.value===""){array.splice(-1,1);element.value=JSON.stringify(array);check();}
return false;};let check=function(){try{array=JSON.parse(element.value)||[];}catch(error){array=[];}

View file

@ -27,7 +27,7 @@ return http.post(path,{'content-type':'application/json',},payload);},delete:fun
if(password===undefined){throw new Error('Missing required parameter: "password"');}
let path='/account/email';let payload={};if(email){payload['email']=email;}
if(password){payload['password']=password;}
return http.patch(path,{'content-type':'application/json',},payload);},createJWT:function(){let path='/account/jwt';let payload={};return http.post(path,{'content-type':'application/json',},payload);},getLogs:function(){let path='/account/logs';let payload={};return http.get(path,{'content-type':'application/json',},payload);},updateName:function(name){if(name===undefined){throw new Error('Missing required parameter: "name"');}
return http.patch(path,{'content-type':'application/json',},payload);},getLogs:function(){let path='/account/logs';let payload={};return http.get(path,{'content-type':'application/json',},payload);},updateName:function(name){if(name===undefined){throw new Error('Missing required parameter: "name"');}
let path='/account/name';let payload={};if(name){payload['name']=name;}
return http.patch(path,{'content-type':'application/json',},payload);},updatePassword:function(password,oldPassword){if(password===undefined){throw new Error('Missing required parameter: "password"');}
if(oldPassword===undefined){throw new Error('Missing required parameter: "oldPassword"');}
@ -244,7 +244,14 @@ if(legalTaxId){payload['legalTaxId']=legalTaxId;}
return http.patch(path,{'content-type':'application/json',},payload);},delete:function(projectId,password){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(password===undefined){throw new Error('Missing required parameter: "password"');}
let path='/projects/{projectId}'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(password){payload['password']=password;}
return http.delete(path,{'content-type':'application/json',},payload);},listDomains:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
return http.delete(path,{'content-type':'application/json',},payload);},updateAuthLimit:function(projectId,limit){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(limit===undefined){throw new Error('Missing required parameter: "limit"');}
let path='/projects/{projectId}/auth/limit'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
return http.patch(path,{'content-type':'application/json',},payload);},updateAuthStatus:function(projectId,method,status){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(method===undefined){throw new Error('Missing required parameter: "method"');}
if(status===undefined){throw new Error('Missing required parameter: "status"');}
let path='/projects/{projectId}/auth/{method}'.replace(new RegExp('{projectId}','g'),projectId).replace(new RegExp('{method}','g'),method);let payload={};if(status){payload['status']=status;}
return http.patch(path,{'content-type':'application/json',},payload);},listDomains:function(projectId){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
let path='/projects/{projectId}/domains'.replace(new RegExp('{projectId}','g'),projectId);let payload={};return http.get(path,{'content-type':'application/json',},payload);},createDomain:function(projectId,domain){if(projectId===undefined){throw new Error('Missing required parameter: "projectId"');}
if(domain===undefined){throw new Error('Missing required parameter: "domain"');}
let path='/projects/{projectId}/domains'.replace(new RegExp('{projectId}','g'),projectId);let payload={};if(domain){payload['domain']=domain;}

View file

@ -328,8 +328,8 @@ if(element.array){document[element.key]=[];}}}}}};let getParams=function getPara
while((match=REGEX_PARAMETERS_VALUES.exec(functionAsString))){params.push(match[1]);}
return params;};let getValue=function(key,prefix,data){let result=null;if(!key){return null;}
let attrKey=prefix+key.charAt(0).toUpperCase()+key.slice(1);if(element.dataset[attrKey]){result=expression.parse(element.dataset[attrKey]);if(element.dataset[attrKey+"CastTo"]==="array"){result=result.split(",");}}
if(data[key]){result=data[key];}
if(!result){result="";}
if(typeof data[key]!=='undefined'){result=data[key];}
if(typeof result==='undefined'){result="";}
return result;};let resolve=function(target,prefix="param",data={}){if(!target){return function(){};}
let args=getParams(target);return target.apply(target,args.map(function(value){let result=getValue(value,prefix,data);return result;}));};let exec=function(event){let parsedSuccess=expression.parse(success);let parsedFailure=expression.parse(failure);let parsedAction=expression.parse(action);parsedSuccess=parsedSuccess&&parsedSuccess!=""?parsedSuccess.split(",").map(element=>element.trim()):[];parsedFailure=parsedFailure&&parsedFailure!=""?parsedFailure.split(",").map(element=>element.trim()):[];element.$lsSkip=true;element.classList.add("load-service-start");if(!document.body.contains(element)){element=undefined;return false;}
if(event){event.preventDefault();}
@ -394,7 +394,7 @@ score+=(variationCount-1)*10;return parseInt(score);};var callback=function(){va
if(rtl.isRTL(content)){paragraph.style.direction='rtl';paragraph.style.textAlign='right';}
else{paragraph.style.direction='ltr';paragraph.style.textAlign='left';}
last=paragraph;}};var santize=function(e){clean(e);alignText(e);};element.addEventListener("change",function(){editor.content.innerHTML=markdown.render(element.value);alignText();});editor.content.setAttribute("placeholder",element.placeholder);editor.content.innerHTML=markdown.render(element.value);editor.content.tabIndex=0;alignText();editor.content.onkeydown=function preventTab(event){if(event.which===9){event.preventDefault();if(document.activeElement){var focussable=Array.prototype.filter.call(document.querySelectorAll('a:not([disabled]), button:not([disabled]), select:not([disabled]), input[type=text]:not([disabled]), input[type=checkbox]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'),function(element){return(element.offsetWidth>0||element.offsetHeight>0||element===document.activeElement);});var index=focussable.indexOf(document.activeElement);if(index>-1){if(event.shiftKey){var prevElement=focussable[index-1]||focussable[focussable.length-1];prevElement.focus();}else{var nextElement=focussable[index+1]||focussable[0];nextElement.focus();}}}}};div.addEventListener("paste",santize);div.addEventListener("drop",santize);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-remove",controller:function(element){Array.prototype.slice.call(element.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){element.parentNode.removeChild(element);});});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-run",repeat:false,controller:function(element,expression,container){let action=expression.parse(element.dataset["formsRun"]||'');element.addEventListener('click',function(){return container.path(action)();});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-select-all",controller:function(element){let select=document.createElement("button");let unselect=document.createElement("button");select.textContent='Select All';unselect.textContent='Unselect All';select.classList.add('link');select.classList.add('margin-top-tiny');select.classList.add('margin-start-small');select.classList.add('text-size-small');select.classList.add('pull-end');unselect.classList.add('link');unselect.classList.add('margin-top-tiny');unselect.classList.add('margin-start-small');unselect.classList.add('text-size-small');unselect.classList.add('pull-end');select.type='button';unselect.type='button';element.parentNode.insertBefore(select,element);element.parentNode.insertBefore(unselect,element);select.addEventListener('click',function(){let checkboxes=element.querySelectorAll("input[type='checkbox']");for(var i=0;i<checkboxes.length;i++){checkboxes[i].checked=true;checkboxes[i].dispatchEvent(new Event('change'));}})
unselect.addEventListener('click',function(){let checkboxes=element.querySelectorAll("input[type='checkbox']");for(var i=0;i<checkboxes.length;i++){checkboxes[i].checked=false;checkboxes[i].dispatchEvent(new Event('change'));}})}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-show-secret",controller:function(element,document){let button=document.createElement('span');button.className="link pull-end text-size-small margin-top-negative icon-eye";button.innerHTML=(element.type=='password')?'Show Secret':'Hide Secret';button.style.visibility=(element.value=='')?'hidden':'visible';element.insertAdjacentElement("beforebegin",button);button.addEventListener("click",function(event){switch(element.type){case"password":element.type="text";button.innerHTML='Hide Secret';break;case"text":element.type="password";button.innerHTML='Show Secret';break;default:console.warn("data-forms-show-secret: element.type NOT text NOR password");}});let sync=function(event){button.style.visibility=(element.value=='')?'hidden':'visible';};element.addEventListener("keyup",sync);element.addEventListener("change",sync);},});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-switch",controller:function(element){let input=window.document.createElement("input");input.type="checkbox";input.className="button switch";let syncA=function(){let value=input.checked?"true":"false"
unselect.addEventListener('click',function(){let checkboxes=element.querySelectorAll("input[type='checkbox']");for(var i=0;i<checkboxes.length;i++){checkboxes[i].checked=false;checkboxes[i].dispatchEvent(new Event('change'));}})}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-show-secret",controller:function(element,document){let button=document.createElement('span');button.className="link pull-end text-size-small margin-top-negative icon-eye";button.innerHTML=(element.type=='password')?'Show Secret':'Hide Secret';button.style.visibility=(element.value=='')?'hidden':'visible';element.insertAdjacentElement("beforebegin",button);button.addEventListener("click",function(event){switch(element.type){case"password":element.type="text";button.innerHTML='Hide Secret';break;case"text":element.type="password";button.innerHTML='Show Secret';break;default:console.warn("data-forms-show-secret: element.type NOT text NOR password");}});let sync=function(event){button.style.visibility=(element.value=='')?'hidden':'visible';};element.addEventListener("keyup",sync);element.addEventListener("change",sync);},});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-switch",controller:function(element){let input=window.document.createElement("input");input.type="checkbox";input.className="button switch "+element.className;let syncA=function(){let value=input.checked?"true":"false"
let old=element.value;element.value=value;if(value!==old){element.dispatchEvent(new Event('change'));}};let syncB=function(){input.checked=(element.value==="true");};input.addEventListener("input",syncA);input.addEventListener("change",syncA);element.addEventListener("input",syncB);element.addEventListener("change",syncB);syncA();element.parentNode.insertBefore(input,element);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-tags",controller:function(element){let array=[];let tags=window.document.createElement("div");let preview=window.document.createElement("ul");let add=window.document.createElement("input");let listen=function(event){if((event.key==="Enter"||event.key===" "||event.key==="Tab")&&add.value.length>0){array.push(add.value);add.value="";element.value=JSON.stringify(array);check();if(event.key!=="Tab"){event.preventDefault();}}
if((event.key==="Backspace"||event.key==="Delete")&&add.value===""){array.splice(-1,1);element.value=JSON.stringify(array);check();}
return false;};let check=function(){try{array=JSON.parse(element.value)||[];}catch(error){array=[];}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
public/images/users/jwt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 4 KiB

View file

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View file

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 6 KiB

View file

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View file

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -311,10 +311,10 @@
*
* Use this endpoint to allow a new user to register a new account in your
* project. After the user registration completes successfully, you can use
* the [/account/verfication](/docs/client/account#createVerification) route
* to start verifying the user email address. To allow the new user to login
* to their new account, you need to create a new [account
* session](/docs/client/account#createSession).
* the [/account/verfication](/docs/client/account#accountCreateVerification)
* route to start verifying the user email address. To allow the new user to
* login to their new account, you need to create a new [account
* session](/docs/client/account#accountCreateSession).
*
* @param {string} email
* @param {string} password
@ -335,15 +335,15 @@
let payload = {};
if(email) {
if(typeof email !== 'undefined') {
payload['email'] = email;
}
if(password) {
if(typeof password !== 'undefined') {
payload['password'] = password;
}
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
@ -402,11 +402,11 @@
let payload = {};
if(email) {
if(typeof email !== 'undefined') {
payload['email'] = email;
}
if(password) {
if(typeof password !== 'undefined') {
payload['password'] = password;
}
@ -416,28 +416,6 @@
}, payload);
},
/**
* Create Account JWT
*
* Use this endpoint to create a JSON Web Token. You can use the resulting JWT
* to authenticate on behalf of the current user when working with the
* Appwrite server-side API and SDKs. The JWT secret is valid for 15 minutes
* from its creation and will be invalid if the user will logout.
*
* @throws {Error}
* @return {Promise}
*/
createJWT: function() {
let path = '/account/jwt';
let payload = {};
return http
.post(path, {
'content-type': 'application/json',
}, payload);
},
/**
* Get Account Logs
*
@ -476,7 +454,7 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
@ -510,11 +488,11 @@
let payload = {};
if(password) {
if(typeof password !== 'undefined') {
payload['password'] = password;
}
if(oldPassword) {
if(typeof oldPassword !== 'undefined') {
payload['oldPassword'] = oldPassword;
}
@ -562,7 +540,7 @@
let payload = {};
if(prefs) {
if(typeof prefs !== 'undefined') {
payload['prefs'] = prefs;
}
@ -579,8 +557,9 @@
* When the user clicks the confirmation link he is redirected back to your
* app password reset URL with the secret key and email address values
* attached to the URL query string. Use the query string params to submit a
* request to the [PUT /account/recovery](/docs/client/account#updateRecovery)
* endpoint to complete the process.
* request to the [PUT
* /account/recovery](/docs/client/account#accountUpdateRecovery) endpoint to
* complete the process.
*
* @param {string} email
* @param {string} url
@ -600,11 +579,11 @@
let payload = {};
if(email) {
if(typeof email !== 'undefined') {
payload['email'] = email;
}
if(url) {
if(typeof url !== 'undefined') {
payload['url'] = url;
}
@ -620,7 +599,7 @@
* Use this endpoint to complete the user account password reset. Both the
* **userId** and **secret** arguments will be passed as query parameters to
* the redirect URL you have provided when sending your request to the [POST
* /account/recovery](/docs/client/account#createRecovery) endpoint.
* /account/recovery](/docs/client/account#accountCreateRecovery) endpoint.
*
* Please note that in order to avoid a [Redirect
* Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
@ -655,19 +634,19 @@
let payload = {};
if(userId) {
if(typeof userId !== 'undefined') {
payload['userId'] = userId;
}
if(secret) {
if(typeof secret !== 'undefined') {
payload['secret'] = secret;
}
if(password) {
if(typeof password !== 'undefined') {
payload['password'] = password;
}
if(passwordAgain) {
if(typeof passwordAgain !== 'undefined') {
payload['passwordAgain'] = passwordAgain;
}
@ -721,11 +700,11 @@
let payload = {};
if(email) {
if(typeof email !== 'undefined') {
payload['email'] = email;
}
if(password) {
if(typeof password !== 'undefined') {
payload['password'] = password;
}
@ -851,7 +830,7 @@
* should redirect the user back to your app and allow you to complete the
* verification process by verifying both the **userId** and **secret**
* parameters. Learn more about how to [complete the verification
* process](/docs/client/account#updateVerification).
* process](/docs/client/account#accountUpdateVerification).
*
* Please note that in order to avoid a [Redirect
* Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md),
@ -872,7 +851,7 @@
let payload = {};
if(url) {
if(typeof url !== 'undefined') {
payload['url'] = url;
}
@ -908,11 +887,11 @@
let payload = {};
if(userId) {
if(typeof userId !== 'undefined') {
payload['userId'] = userId;
}
if(secret) {
if(typeof secret !== 'undefined') {
payload['secret'] = secret;
}
@ -1421,19 +1400,19 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(read) {
if(typeof read !== 'undefined') {
payload['read'] = read;
}
if(write) {
if(typeof write !== 'undefined') {
payload['write'] = write;
}
if(rules) {
if(typeof rules !== 'undefined') {
payload['rules'] = rules;
}
@ -1502,19 +1481,19 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(read) {
if(typeof read !== 'undefined') {
payload['read'] = read;
}
if(write) {
if(typeof write !== 'undefined') {
payload['write'] = write;
}
if(rules) {
if(typeof rules !== 'undefined') {
payload['rules'] = rules;
}
@ -1616,7 +1595,7 @@
*
* Create a new Document. Before using this route, you should create a new
* collection resource using either a [server
* integration](/docs/server/database?sdk=nodejs#createCollection) API or
* integration](/docs/server/database#databaseCreateCollection) API or
* directly from your database console.
*
* @param {string} collectionId
@ -1650,27 +1629,27 @@
let payload = {};
if(data) {
if(typeof data !== 'undefined') {
payload['data'] = data;
}
if(read) {
if(typeof read !== 'undefined') {
payload['read'] = read;
}
if(write) {
if(typeof write !== 'undefined') {
payload['write'] = write;
}
if(parentDocument) {
if(typeof parentDocument !== 'undefined') {
payload['parentDocument'] = parentDocument;
}
if(parentProperty) {
if(typeof parentProperty !== 'undefined') {
payload['parentProperty'] = parentProperty;
}
if(parentPropertyType) {
if(typeof parentPropertyType !== 'undefined') {
payload['parentPropertyType'] = parentPropertyType;
}
@ -1749,15 +1728,15 @@
let payload = {};
if(data) {
if(typeof data !== 'undefined') {
payload['data'] = data;
}
if(read) {
if(typeof read !== 'undefined') {
payload['read'] = read;
}
if(write) {
if(typeof write !== 'undefined') {
payload['write'] = write;
}
@ -1875,31 +1854,31 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(execute) {
if(typeof execute !== 'undefined') {
payload['execute'] = execute;
}
if(env) {
if(typeof env !== 'undefined') {
payload['env'] = env;
}
if(vars) {
if(typeof vars !== 'undefined') {
payload['vars'] = vars;
}
if(events) {
if(typeof events !== 'undefined') {
payload['events'] = events;
}
if(schedule) {
if(typeof schedule !== 'undefined') {
payload['schedule'] = schedule;
}
if(timeout) {
if(typeof timeout !== 'undefined') {
payload['timeout'] = timeout;
}
@ -1965,27 +1944,27 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(execute) {
if(typeof execute !== 'undefined') {
payload['execute'] = execute;
}
if(vars) {
if(typeof vars !== 'undefined') {
payload['vars'] = vars;
}
if(events) {
if(typeof events !== 'undefined') {
payload['events'] = events;
}
if(schedule) {
if(typeof schedule !== 'undefined') {
payload['schedule'] = schedule;
}
if(timeout) {
if(typeof timeout !== 'undefined') {
payload['timeout'] = timeout;
}
@ -2024,8 +2003,8 @@
*
* Get a list of all the current user function execution logs. You can use the
* query params to filter your results. On admin mode, this endpoint will
* return a list of all of the project's teams. [Learn more about different
* API modes](/docs/admin).
* return a list of all of the project's executions. [Learn more about
* different API modes](/docs/admin).
*
* @param {string} functionId
* @param {string} search
@ -2152,7 +2131,7 @@
let payload = {};
if(tag) {
if(typeof tag !== 'undefined') {
payload['tag'] = tag;
}
@ -2244,11 +2223,11 @@
let payload = {};
if(command) {
if(typeof command !== 'undefined') {
payload['command'] = command;
}
if(code) {
if(typeof code !== 'undefined') {
payload['code'] = code;
}
@ -2807,47 +2786,47 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(teamId) {
if(typeof teamId !== 'undefined') {
payload['teamId'] = teamId;
}
if(description) {
if(typeof description !== 'undefined') {
payload['description'] = description;
}
if(logo) {
if(typeof logo !== 'undefined') {
payload['logo'] = logo;
}
if(url) {
if(typeof url !== 'undefined') {
payload['url'] = url;
}
if(legalName) {
if(typeof legalName !== 'undefined') {
payload['legalName'] = legalName;
}
if(legalCountry) {
if(typeof legalCountry !== 'undefined') {
payload['legalCountry'] = legalCountry;
}
if(legalState) {
if(typeof legalState !== 'undefined') {
payload['legalState'] = legalState;
}
if(legalCity) {
if(typeof legalCity !== 'undefined') {
payload['legalCity'] = legalCity;
}
if(legalAddress) {
if(typeof legalAddress !== 'undefined') {
payload['legalAddress'] = legalAddress;
}
if(legalTaxId) {
if(typeof legalTaxId !== 'undefined') {
payload['legalTaxId'] = legalTaxId;
}
@ -2911,43 +2890,43 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(description) {
if(typeof description !== 'undefined') {
payload['description'] = description;
}
if(logo) {
if(typeof logo !== 'undefined') {
payload['logo'] = logo;
}
if(url) {
if(typeof url !== 'undefined') {
payload['url'] = url;
}
if(legalName) {
if(typeof legalName !== 'undefined') {
payload['legalName'] = legalName;
}
if(legalCountry) {
if(typeof legalCountry !== 'undefined') {
payload['legalCountry'] = legalCountry;
}
if(legalState) {
if(typeof legalState !== 'undefined') {
payload['legalState'] = legalState;
}
if(legalCity) {
if(typeof legalCity !== 'undefined') {
payload['legalCity'] = legalCity;
}
if(legalAddress) {
if(typeof legalAddress !== 'undefined') {
payload['legalAddress'] = legalAddress;
}
if(legalTaxId) {
if(typeof legalTaxId !== 'undefined') {
payload['legalTaxId'] = legalTaxId;
}
@ -2979,7 +2958,7 @@
let payload = {};
if(password) {
if(typeof password !== 'undefined') {
payload['password'] = password;
}
@ -2989,6 +2968,75 @@
}, payload);
},
/**
* Update Project users limit
*
*
* @param {string} projectId
* @param {string} limit
* @throws {Error}
* @return {Promise}
*/
updateAuthLimit: function(projectId, limit) {
if(projectId === undefined) {
throw new Error('Missing required parameter: "projectId"');
}
if(limit === undefined) {
throw new Error('Missing required parameter: "limit"');
}
let path = '/projects/{projectId}/auth/limit'.replace(new RegExp('{projectId}', 'g'), projectId);
let payload = {};
if(typeof limit !== 'undefined') {
payload['limit'] = limit;
}
return http
.patch(path, {
'content-type': 'application/json',
}, payload);
},
/**
* Update Project auth method status. Use this endpoint to enable or disable a given auth method for this project.
*
*
* @param {string} projectId
* @param {string} method
* @param {boolean} status
* @throws {Error}
* @return {Promise}
*/
updateAuthStatus: function(projectId, method, status) {
if(projectId === undefined) {
throw new Error('Missing required parameter: "projectId"');
}
if(method === undefined) {
throw new Error('Missing required parameter: "method"');
}
if(status === undefined) {
throw new Error('Missing required parameter: "status"');
}
let path = '/projects/{projectId}/auth/{method}'.replace(new RegExp('{projectId}', 'g'), projectId).replace(new RegExp('{method}', 'g'), method);
let payload = {};
if(typeof status !== 'undefined') {
payload['status'] = status;
}
return http
.patch(path, {
'content-type': 'application/json',
}, payload);
},
/**
* List Domains
*
@ -3034,7 +3082,7 @@
let payload = {};
if(domain) {
if(typeof domain !== 'undefined') {
payload['domain'] = domain;
}
@ -3178,11 +3226,11 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(scopes) {
if(typeof scopes !== 'undefined') {
payload['scopes'] = scopes;
}
@ -3252,11 +3300,11 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(scopes) {
if(typeof scopes !== 'undefined') {
payload['scopes'] = scopes;
}
@ -3318,15 +3366,15 @@
let payload = {};
if(provider) {
if(typeof provider !== 'undefined') {
payload['provider'] = provider;
}
if(appId) {
if(typeof appId !== 'undefined') {
payload['appId'] = appId;
}
if(secret) {
if(typeof secret !== 'undefined') {
payload['secret'] = secret;
}
@ -3389,23 +3437,23 @@
let payload = {};
if(type) {
if(typeof type !== 'undefined') {
payload['type'] = type;
}
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(key) {
if(typeof key !== 'undefined') {
payload['key'] = key;
}
if(store) {
if(typeof store !== 'undefined') {
payload['store'] = store;
}
if(hostname) {
if(typeof hostname !== 'undefined') {
payload['hostname'] = hostname;
}
@ -3473,19 +3521,19 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(key) {
if(typeof key !== 'undefined') {
payload['key'] = key;
}
if(store) {
if(typeof store !== 'undefined') {
payload['store'] = store;
}
if(hostname) {
if(typeof hostname !== 'undefined') {
payload['hostname'] = hostname;
}
@ -3596,39 +3644,39 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(status) {
if(typeof status !== 'undefined') {
payload['status'] = status;
}
if(schedule) {
if(typeof schedule !== 'undefined') {
payload['schedule'] = schedule;
}
if(security) {
if(typeof security !== 'undefined') {
payload['security'] = security;
}
if(httpMethod) {
if(typeof httpMethod !== 'undefined') {
payload['httpMethod'] = httpMethod;
}
if(httpUrl) {
if(typeof httpUrl !== 'undefined') {
payload['httpUrl'] = httpUrl;
}
if(httpHeaders) {
if(typeof httpHeaders !== 'undefined') {
payload['httpHeaders'] = httpHeaders;
}
if(httpUser) {
if(typeof httpUser !== 'undefined') {
payload['httpUser'] = httpUser;
}
if(httpPass) {
if(typeof httpPass !== 'undefined') {
payload['httpPass'] = httpPass;
}
@ -3721,39 +3769,39 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(status) {
if(typeof status !== 'undefined') {
payload['status'] = status;
}
if(schedule) {
if(typeof schedule !== 'undefined') {
payload['schedule'] = schedule;
}
if(security) {
if(typeof security !== 'undefined') {
payload['security'] = security;
}
if(httpMethod) {
if(typeof httpMethod !== 'undefined') {
payload['httpMethod'] = httpMethod;
}
if(httpUrl) {
if(typeof httpUrl !== 'undefined') {
payload['httpUrl'] = httpUrl;
}
if(httpHeaders) {
if(typeof httpHeaders !== 'undefined') {
payload['httpHeaders'] = httpHeaders;
}
if(httpUser) {
if(typeof httpUser !== 'undefined') {
payload['httpUser'] = httpUser;
}
if(httpPass) {
if(typeof httpPass !== 'undefined') {
payload['httpPass'] = httpPass;
}
@ -3881,27 +3929,27 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(events) {
if(typeof events !== 'undefined') {
payload['events'] = events;
}
if(url) {
if(typeof url !== 'undefined') {
payload['url'] = url;
}
if(security) {
if(typeof security !== 'undefined') {
payload['security'] = security;
}
if(httpUser) {
if(typeof httpUser !== 'undefined') {
payload['httpUser'] = httpUser;
}
if(httpPass) {
if(typeof httpPass !== 'undefined') {
payload['httpPass'] = httpPass;
}
@ -3983,27 +4031,27 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(events) {
if(typeof events !== 'undefined') {
payload['events'] = events;
}
if(url) {
if(typeof url !== 'undefined') {
payload['url'] = url;
}
if(security) {
if(typeof security !== 'undefined') {
payload['security'] = security;
}
if(httpUser) {
if(typeof httpUser !== 'undefined') {
payload['httpUser'] = httpUser;
}
if(httpPass) {
if(typeof httpPass !== 'undefined') {
payload['httpPass'] = httpPass;
}
@ -4115,15 +4163,15 @@
let payload = {};
if(file) {
if(typeof file !== 'undefined') {
payload['file'] = file;
}
if(read) {
if(typeof read !== 'undefined') {
payload['read'] = read;
}
if(write) {
if(typeof write !== 'undefined') {
payload['write'] = write;
}
@ -4187,11 +4235,11 @@
let payload = {};
if(read) {
if(typeof read !== 'undefined') {
payload['read'] = read;
}
if(write) {
if(typeof write !== 'undefined') {
payload['write'] = write;
}
@ -4451,11 +4499,11 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(roles) {
if(typeof roles !== 'undefined') {
payload['roles'] = roles;
}
@ -4514,7 +4562,7 @@
let payload = {};
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
@ -4603,8 +4651,8 @@
*
* Use the 'URL' parameter to redirect the user from the invitation email back
* to your app. When the user is redirected, use the [Update Team Membership
* Status](/docs/client/teams#updateMembershipStatus) endpoint to allow the
* user to accept the invitation to the team.
* Status](/docs/client/teams#teamsUpdateMembershipStatus) endpoint to allow
* the user to accept the invitation to the team.
*
* Please note that in order to avoid a [Redirect
* Attacks](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
@ -4640,19 +4688,19 @@
let payload = {};
if(email) {
if(typeof email !== 'undefined') {
payload['email'] = email;
}
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
if(roles) {
if(typeof roles !== 'undefined') {
payload['roles'] = roles;
}
if(url) {
if(typeof url !== 'undefined') {
payload['url'] = url;
}
@ -4728,11 +4776,11 @@
let payload = {};
if(userId) {
if(typeof userId !== 'undefined') {
payload['userId'] = userId;
}
if(secret) {
if(typeof secret !== 'undefined') {
payload['secret'] = secret;
}
@ -4809,15 +4857,15 @@
let payload = {};
if(email) {
if(typeof email !== 'undefined') {
payload['email'] = email;
}
if(password) {
if(typeof password !== 'undefined') {
payload['password'] = password;
}
if(name) {
if(typeof name !== 'undefined') {
payload['name'] = name;
}
@ -4947,7 +4995,7 @@
let payload = {};
if(prefs) {
if(typeof prefs !== 'undefined') {
payload['prefs'] = prefs;
}
@ -5057,7 +5105,7 @@
let payload = {};
if(status) {
if(typeof status !== 'undefined') {
payload['status'] = status;
}

View file

@ -6,7 +6,7 @@
controller: function(element) {
let input = window.document.createElement("input");
input.type = "checkbox";
input.className = "button switch";
input.className = "button switch " + element.className;
let syncA = function() {
let value = input.checked ? "true" : "false"

View file

@ -234,11 +234,11 @@
}
}
if (data[key]) {
if (typeof data[key] !== 'undefined') {
result = data[key];
}
if (!result) {
if (typeof result === 'undefined') {
result = "";
}

View file

@ -243,7 +243,7 @@ class Auth
*
* @return bool
*/
public static function isPreviliggedUser(array $roles): bool
public static function isPrivilegedUser(array $roles): bool
{
if(
array_key_exists('role:'.self::USER_ROLE_OWNER, $roles) ||

View file

@ -283,6 +283,9 @@ class MySQL extends Adapter
if (\is_array($prop['value'])) {
throw new Exception('Value can\'t be an array: '.\json_encode($prop['value']));
}
if (\is_bool($prop['value'])) {
$prop['value'] = (int) $prop['value'];
}
$st2->bindValue(':documentUid', $data['$id'], PDO::PARAM_STR);
$st2->bindValue(':documentRevision', $revision, PDO::PARAM_STR);

View file

@ -89,6 +89,12 @@ class Project extends Model
'default' => '',
'example' => '131102020',
])
->addRule('usersAuthLimit', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Max users allowed. 0 is unlimited.',
'default' => 0,
'example' => 100,
])
->addRule('platforms', [
'type' => Response::MODEL_PLATFORM,
'description' => 'List of Platforms.',
@ -127,6 +133,7 @@ class Project extends Model
;
$providers = Config::getParam('providers', []);
$auth = Config::getParam('auth', []);
foreach ($providers as $index => $provider) {
if (!$provider['enabled']) {
@ -150,6 +157,20 @@ class Project extends Model
])
;
}
foreach ($auth as $index => $method) {
$name = $method['name'] ?? '';
$key = $method['key'] ?? '';
$this
->addRule($key, [
'type' => self::TYPE_BOOLEAN,
'description' => $name.' auth method status',
'example' => true,
'default' => true,
])
;
}
}
/**

View file

@ -117,7 +117,7 @@ class HTTPTest extends Scope
$response['body'] = json_decode($response['body'], true);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertFalse(isset($response['body']['messages']));
$this->assertTrue(empty($response['body']));
unlink(realpath(__DIR__ . '/../../resources/swagger2.json'));
}
@ -145,7 +145,7 @@ class HTTPTest extends Scope
$response['body'] = json_decode($response['body'], true);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertFalse(isset($response['body']['messages']));
$this->assertTrue(empty($response['body']));
unlink(realpath(__DIR__ . '/../../resources/open-api3.json'));
}

View file

@ -302,6 +302,215 @@ class ProjectsConsoleClientTest extends Scope
return $data;
}
/**
* @depends testGetProjectUsage
*/
public function testUpdateProjectAuthStatus($data):array
{
$id = $data['projectId'] ?? '';
$auth = require('app/config/auth.php');
$originalEmail = uniqid().'user@localhost.test';
$originalPassword = 'password';
$originalName = 'User Name';
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), [
'email' => $originalEmail,
'password' => $originalPassword,
'name' => $originalName,
]);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), [
'email' => $originalEmail,
'password' => $originalPassword,
]);
$session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$id];
/**
* Test for SUCCESS
*/
foreach ($auth as $index => $method) {
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$id.'/auth/'.$index, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'status' => false,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$response = $this->client->call(Client::METHOD_GET, '/projects/'.$id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals(false, $response['body'][$method['key']]);
}
$email = uniqid().'user@localhost.test';
$password = 'password';
$name = 'User Name';
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), [
'email' => $email,
'password' => $password,
'name' => $name,
]);
$this->assertEquals($response['headers']['status-code'], 501);
$response = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $id,
'cookie' => 'a_session_'.$id.'='.$session,
]), [
'name' => 'Arsenal'
]);
$this->assertEquals(201, $response['headers']['status-code']);
$teamUid = $response['body']['$id'];
$response = $this->client->call(Client::METHOD_POST, '/teams/'.$teamUid.'/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $id,
'cookie' => 'a_session_'.$id.'=' . $session,
]), [
'email' => $email,
'name' => 'Friend User',
'roles' => ['admin', 'editor'],
'url' => 'http://localhost:5000/join-us#title'
]);
$this->assertEquals($response['headers']['status-code'], 501);
$response = $this->client->call(Client::METHOD_POST, '/account/jwt', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $id,
'cookie' => 'a_session_'.$id.'=' . $session,
]));
$this->assertEquals($response['headers']['status-code'], 501);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), [
'email' => $originalEmail,
'password' => $originalPassword,
]);
$this->assertEquals($response['headers']['status-code'], 501);
$response = $this->client->call(Client::METHOD_POST, '/account/anonymous', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), []);
$this->assertEquals($response['headers']['status-code'], 501);
// Cleanup
foreach ($auth as $index => $method) {
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$id.'/auth/'.$index, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'status' => true,
]);
}
return $data;
}
/**
* @depends testGetProjectUsage
*/
public function testUpdateProjectAuthLimit($data):array
{
$id = $data['projectId'] ?? '';
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$id.'/auth/limit', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$email = uniqid().'user@localhost.test';
$password = 'password';
$name = 'User Name';
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), [
'email' => $email,
'password' => $password,
'name' => $name,
]);
$this->assertEquals($response['headers']['status-code'], 501);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/'.$id.'/auth/limit', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 0,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
]), [
'email' => $email,
'password' => $password,
'name' => $name,
]);
$this->assertEquals($response['headers']['status-code'], 201);
return $data;
}
/**
* @depends testCreateProject
*/

View file

@ -163,22 +163,21 @@ class AuthTest extends TestCase
$this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false);
}
public function testIsPreviliggedUser()
public function testIsPrivilegedUser()
{
$this->assertEquals(false, Auth::isPreviliggedUser([]));
$this->assertEquals(false, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_GUEST => true]));
$this->assertEquals(false, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_MEMBER => true]));
$this->assertEquals(true, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_ADMIN => true]));
$this->assertEquals(true, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_DEVELOPER => true]));
$this->assertEquals(true, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_OWNER => true]));
$this->assertEquals(false, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_APP => true]));
$this->assertEquals(false, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_SYSTEM => true]));
$this->assertEquals(false, Auth::isPrivilegedUser([]));
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_GUEST => true]));
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_MEMBER => true]));
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_ADMIN => true]));
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_DEVELOPER => true]));
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER => true]));
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP => true]));
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_SYSTEM => true]));
$this->assertEquals(false, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_APP => true]));
$this->assertEquals(false, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
$this->assertEquals(true, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
$this->assertEquals(true, Auth::isPreviliggedUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_ADMIN => true, 'role:'.Auth::USER_ROLE_DEVELOPER => true]));
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_APP => true]));
$this->assertEquals(false, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_APP => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_GUEST => true]));
$this->assertEquals(true, Auth::isPrivilegedUser(['role:'.Auth::USER_ROLE_OWNER => true, 'role:'.Auth::USER_ROLE_ADMIN => true, 'role:'.Auth::USER_ROLE_DEVELOPER => true]));
}
public function testIsAppUser()