Merge branch 'feat-custom-cf-hostnames' into lohanidamodar-patch-3

This commit is contained in:
Damodar Lohani
2024-11-10 11:38:42 +05:45
committed by GitHub
48 changed files with 4048 additions and 2936 deletions
+1
View File
@@ -2,6 +2,7 @@ _APP_ENV=development
_APP_EDITION=self-hosted
_APP_LOCALE=en
_APP_WORKER_PER_CORE=6
_APP_COMPRESSION_MIN_SIZE_BYTES=1024
_APP_CONSOLE_WHITELIST_ROOT=disabled
_APP_CONSOLE_WHITELIST_EMAILS=
_APP_CONSOLE_SESSION_ALERTS=enabled
+3 -3
View File
@@ -92,10 +92,10 @@ RUN chmod +x /usr/local/bin/doctor && \
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
# Enable Extensions
RUN if [ "$DEBUG" == "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
RUN if [ "$DEBUG" == "true" ]; then mkdir -p /tmp/xdebug; fi
RUN if [ "$DEBUG" = "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
RUN if [ "$DEBUG" = "true" ]; then mkdir -p /tmp/xdebug; fi
RUN if [ "$DEBUG" = "false" ]; then rm -rf /usr/src/code/dev; fi
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20220829/xdebug.so; fi
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20230831/xdebug.so; fi
EXPOSE 80
+41 -35
View File
@@ -4945,7 +4945,7 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 305,
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5033,7 +5033,7 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 304,
"weight": 305,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5150,7 +5150,7 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5226,7 +5226,7 @@
},
"x-appwrite": {
"method": "query",
"weight": 330,
"weight": 331,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5280,7 +5280,7 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 329,
"weight": 330,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5766,7 +5766,7 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 381,
"weight": 382,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5851,7 +5851,7 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 385,
"weight": 386,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5928,7 +5928,7 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 207,
"weight": 208,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6016,7 +6016,7 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 206,
"weight": 207,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6116,7 +6116,7 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 208,
"weight": 209,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6190,7 +6190,7 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 213,
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6281,7 +6281,7 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 214,
"weight": 215,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6350,7 +6350,7 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 210,
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6419,7 +6419,7 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 209,
"weight": 210,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6637,7 +6637,7 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 211,
"weight": 212,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6713,7 +6713,7 @@
},
"x-appwrite": {
"method": "list",
"weight": 218,
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6791,7 +6791,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 217,
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6878,7 +6878,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 219,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6942,7 +6942,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 221,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7018,7 +7018,7 @@
},
"x-appwrite": {
"method": "delete",
"weight": 223,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7069,7 +7069,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint.",
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Memberships List",
@@ -7084,7 +7084,7 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 225,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7172,7 +7172,7 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 224,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7270,7 +7270,7 @@
"tags": [
"teams"
],
"description": "Get a team member by the membership unique id. All team members have read access for this resource.",
"description": "Get a team member by the membership unique id. All team members have read access for this resource. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Membership",
@@ -7285,7 +7285,7 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 226,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7359,7 +7359,7 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 227,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7448,7 +7448,7 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 229,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7524,7 +7524,7 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 228,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7624,7 +7624,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 220,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7687,7 +7687,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 222,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -9266,12 +9266,12 @@
},
"userName": {
"type": "string",
"description": "User name.",
"description": "User name. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "John Doe"
},
"userEmail": {
"type": "string",
"description": "User email address.",
"description": "User email address. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "john@appwrite.io"
},
"teamId": {
@@ -9301,7 +9301,7 @@
},
"mfa": {
"type": "boolean",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise.",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console.",
"x-example": false
},
"roles": {
@@ -9826,7 +9826,7 @@
"name": {
"type": "string",
"description": "Target Name.",
"x-example": "Aegon apple token"
"x-example": "Apple iPhone 12"
},
"userId": {
"type": "string",
@@ -9848,6 +9848,11 @@
"type": "string",
"description": "The target identifier.",
"x-example": "token"
},
"expired": {
"type": "boolean",
"description": "Is the target expired.",
"x-example": false
}
},
"required": [
@@ -9857,7 +9862,8 @@
"name",
"userId",
"providerType",
"identifier"
"identifier",
"expired"
]
}
},
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+41 -35
View File
@@ -5101,7 +5101,7 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 305,
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5186,7 +5186,7 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 304,
"weight": 305,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5307,7 +5307,7 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5381,7 +5381,7 @@
},
"x-appwrite": {
"method": "query",
"weight": 330,
"weight": 331,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5457,7 +5457,7 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 329,
"weight": 330,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5981,7 +5981,7 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 381,
"weight": 382,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6070,7 +6070,7 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 385,
"weight": 386,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6145,7 +6145,7 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 207,
"weight": 208,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6230,7 +6230,7 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 206,
"weight": 207,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6324,7 +6324,7 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 208,
"weight": 209,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6396,7 +6396,7 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 213,
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6487,7 +6487,7 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 214,
"weight": 215,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6561,7 +6561,7 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 210,
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6635,7 +6635,7 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 209,
"weight": 210,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6836,7 +6836,7 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 211,
"weight": 212,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6910,7 +6910,7 @@
},
"x-appwrite": {
"method": "list",
"weight": 218,
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6987,7 +6987,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 217,
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7081,7 +7081,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 219,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7145,7 +7145,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 221,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7222,7 +7222,7 @@
},
"x-appwrite": {
"method": "delete",
"weight": 223,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7277,7 +7277,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint.",
"description": "Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Memberships List",
@@ -7288,7 +7288,7 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 225,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7373,7 +7373,7 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 224,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7479,7 +7479,7 @@
"tags": [
"teams"
],
"description": "Get a team member by the membership unique id. All team members have read access for this resource.",
"description": "Get a team member by the membership unique id. All team members have read access for this resource. Hide sensitive attributes from the response by toggling membership privacy in the Console.",
"responses": {
"200": {
"description": "Membership",
@@ -7490,7 +7490,7 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 226,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7562,7 +7562,7 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 227,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7650,7 +7650,7 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 229,
"weight": 230,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7724,7 +7724,7 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 228,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7822,7 +7822,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 220,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7885,7 +7885,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 222,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -9445,12 +9445,12 @@
},
"userName": {
"type": "string",
"description": "User name.",
"description": "User name. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "John Doe"
},
"userEmail": {
"type": "string",
"description": "User email address.",
"description": "User email address. Hide this attribute by toggling membership privacy in the Console.",
"x-example": "john@appwrite.io"
},
"teamId": {
@@ -9480,7 +9480,7 @@
},
"mfa": {
"type": "boolean",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise.",
"description": "Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console.",
"x-example": false
},
"roles": {
@@ -10008,7 +10008,7 @@
"name": {
"type": "string",
"description": "Target Name.",
"x-example": "Aegon apple token"
"x-example": "Apple iPhone 12"
},
"userId": {
"type": "string",
@@ -10030,6 +10030,11 @@
"type": "string",
"description": "The target identifier.",
"x-example": "token"
},
"expired": {
"type": "boolean",
"description": "Is the target expired.",
"x-example": false
}
},
"required": [
@@ -10039,7 +10044,8 @@
"name",
"userId",
"providerType",
"identifier"
"identifier",
"expired"
]
}
},
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -326,9 +326,9 @@ App::post('/v1/functions')
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (!empty($functionsDomain)) {
$ruleId = ID::unique();
$routeSubdomain = ID::unique();
$domain = "{$routeSubdomain}.{$functionsDomain}";
$ruleId = md5($domain);
$rule = Authorization::skip(
fn () => $dbForConsole->createDocument('rules', new Document([
+38
View File
@@ -111,6 +111,9 @@ App::post('/v1/projects')
'personalDataCheck' => false,
'mockNumbers' => [],
'sessionAlerts' => false,
'membershipsUserName' => false,
'membershipsUserEmail' => false,
'membershipsMfa' => false,
];
foreach ($auth as $method) {
@@ -648,6 +651,41 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/memberships-privacy')
->desc('Update project memberships privacy attributes')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateMembershipsPrivacy')
->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('userName', true, new Boolean(true), 'Set to true to show userName to members of a team.')
->param('userEmail', true, new Boolean(true), 'Set to true to show email to members of a team.')
->param('mfa', true, new Boolean(true), 'Set to true to show mfa to members of a team.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $userName, bool $userEmail, bool $mfa, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['membershipsUserName'] = $userName;
$auths['membershipsUserEmail'] = $userEmail;
$auths['membershipsMfa'] = $mfa;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/limit')
->desc('Update project users limit')
->groups(['api', 'projects'])
+3 -5
View File
@@ -11,7 +11,6 @@ use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
@@ -60,9 +59,8 @@ App::post('/v1/proxy/rules')
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.');
}
$document = $dbForConsole->findOne('rules', [
Query::equal('domain', [$domain]),
]);
$ruleId = md5($domain);
$document = $dbForConsole->getDocument('rules', $ruleId);
if (!$document->isEmpty()) {
if ($document->getAttribute('projectId') === $project->getId()) {
@@ -103,7 +101,7 @@ App::post('/v1/proxy/rules')
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.');
}
$ruleId = ID::unique();
$ruleId = md5($domain->get());
$rule = new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
+77 -34
View File
@@ -727,9 +727,9 @@ App::get('/v1/teams/:teamId/memberships')
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('project')
->inject('dbForProject')
->action(function (string $teamId, array $queries, string $search, Response $response, Database $dbForProject) {
->action(function (string $teamId, array $queries, string $search, Response $response, Document $project, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -790,27 +790,48 @@ App::get('/v1/teams/:teamId/memberships')
$memberships = array_filter($memberships, fn (Document $membership) => !empty($membership->getAttribute('userId')));
$memberships = array_map(function ($membership) use ($dbForProject, $team) {
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$membershipsPrivacy = [
'userName' => $project->getAttribute('auths', [])['membershipsUserName'] ?? true,
'userEmail' => $project->getAttribute('auths', [])['membershipsUserEmail'] ?? true,
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$mfa = $user->getAttribute('mfa', false);
if ($mfa) {
$totp = TOTP::getAuthenticatorFromUser($user);
$totpEnabled = $totp && $totp->getAttribute('verified', false);
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
$mfa = false;
$membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) {
return $privacy || $isPrivilegedUser || $isAppUser;
}, $membershipsPrivacy);
$memberships = array_map(function ($membership) use ($dbForProject, $team, $membershipsPrivacy) {
if ($membershipsPrivacy['mfa']) {
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$mfa = $user->getAttribute('mfa', false);
if ($mfa) {
$totp = TOTP::getAuthenticatorFromUser($user);
$totpEnabled = $totp && $totp->getAttribute('verified', false);
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
$mfa = false;
}
}
$membership->setAttribute('mfa', $mfa);
}
$membership
->setAttribute('mfa', $mfa)
->setAttribute('teamName', $team->getAttribute('name'))
->setAttribute('userName', $user->getAttribute('name'))
->setAttribute('userEmail', $user->getAttribute('email'))
;
if ($membershipsPrivacy['userName']) {
$membership->setAttribute('userName', $user->getAttribute('name'));
}
if ($membershipsPrivacy['userEmail']) {
$membership->setAttribute('userEmail', $user->getAttribute('email'));
}
$membership->setAttribute('teamName', $team->getAttribute('name'));
return $membership;
}, $memberships);
@@ -837,8 +858,9 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response')
->inject('project')
->inject('dbForProject')
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject) {
->action(function (string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -852,27 +874,48 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
}
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$membershipsPrivacy = [
'userName' => $project->getAttribute('auths', [])['membershipsUserName'] ?? true,
'userEmail' => $project->getAttribute('auths', [])['membershipsUserEmail'] ?? true,
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$mfa = $user->getAttribute('mfa', false);
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if ($mfa) {
$totp = TOTP::getAuthenticatorFromUser($user);
$totpEnabled = $totp && $totp->getAttribute('verified', false);
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
$membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) {
return $privacy || $isPrivilegedUser || $isAppUser;
}, $membershipsPrivacy);
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
$mfa = false;
if ($membershipsPrivacy['mfa']) {
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
$mfa = $user->getAttribute('mfa', false);
if ($mfa) {
$totp = TOTP::getAuthenticatorFromUser($user);
$totpEnabled = $totp && $totp->getAttribute('verified', false);
$emailEnabled = $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false);
$phoneEnabled = $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false);
if (!$totpEnabled && !$emailEnabled && !$phoneEnabled) {
$mfa = false;
}
}
$membership->setAttribute('mfa', $mfa);
}
$membership
->setAttribute('mfa', $mfa)
->setAttribute('teamName', $team->getAttribute('name'))
->setAttribute('userName', $user->getAttribute('name'))
->setAttribute('userEmail', $user->getAttribute('email'))
;
if ($membershipsPrivacy['userName']) {
$membership->setAttribute('userName', $user->getAttribute('name'));
}
if ($membershipsPrivacy['userEmail']) {
$membership->setAttribute('userEmail', $user->getAttribute('email'));
}
$membership->setAttribute('teamName', $team->getAttribute('name'));
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
});
+5 -12
View File
@@ -29,7 +29,6 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Domains\Domain;
use Utopia\DSN\DSN;
@@ -52,14 +51,9 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
$host = $request->getHostname() ?? '';
$route = Authorization::skip(
fn () => $dbForConsole->find('rules', [
Query::equal('domain', [$host]),
Query::limit(1)
])
)[0] ?? null;
$route = Authorization::skip(fn () => $dbForConsole->getDocument('rules', md5($host)));
if ($route === null) {
if ($route->isEmpty()) {
if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.');
}
@@ -518,19 +512,18 @@ App::init()
if (!empty($envDomain) && $envDomain !== 'localhost') {
$mainDomain = $envDomain;
} else {
$domainDocument = $dbForConsole->findOne('rules', [Query::orderAsc('$id')]);
$domainDocument = $dbForConsole->getDocument('rules', md5($envDomain));
$mainDomain = !$domainDocument->isEmpty() ? $domainDocument->getAttribute('domain') : $domain->get();
}
if ($mainDomain !== $domain->get()) {
Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.');
} else {
$domainDocument = $dbForConsole->findOne('rules', [
Query::equal('domain', [$domain->get()])
]);
$domainDocument = $dbForConsole->getDocument('rules', md5($domain->get()));
if ($domainDocument->isEmpty()) {
$domainDocument = new Document([
'$id' => md5($domain->get()),
'domain' => $domain->get(),
'resourceType' => 'api',
'status' => 'verifying',
+3 -2
View File
@@ -39,8 +39,7 @@ $http
->set([
'worker_num' => $workerNumber,
'open_http2_protocol' => true,
'http_compression' => true,
'http_compression_level' => 6,
'http_compression' => false,
'package_max_length' => $payloadSize,
'buffer_output_size' => $payloadSize,
]);
@@ -244,6 +243,8 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
}
$app = new App('UTC');
$app->setCompression(true);
$app->setCompressionMinSize(intval(System::getEnv('_APP_COMPRESSION_MIN_SIZE_BYTES', '1024'))); // 1KB
$pools = $register->get('pools');
App::setResource('pools', fn () => $pools);
+1
View File
@@ -73,6 +73,7 @@ $image = $this->getParam('image', '');
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_LOCALE
- _APP_COMPRESSION_MIN_SIZE_BYTES
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_SESSION_ALERTS
Generated
+80 -33
View File
@@ -1671,6 +1671,52 @@
},
"time": "2024-10-04T13:55:36+00:00"
},
{
"name": "utopia-php/compression",
"version": "0.1.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/compression.git",
"reference": "6062f70596415f8d5de40a589367b0eb2a435f98"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/compression/zipball/6062f70596415f8d5de40a589367b0eb2a435f98",
"reference": "6062f70596415f8d5de40a589367b0eb2a435f98",
"shasum": ""
},
"require": {
"php": ">=8.0"
},
"require-dev": {
"laravel/pint": "1.2.*",
"phpunit/phpunit": "^9.3",
"vimeo/psalm": "4.0.1"
},
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\Compression\\": "src/Compression"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A simple Compression library to handle file compression",
"keywords": [
"compression",
"framework",
"php",
"upf",
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/compression/issues",
"source": "https://github.com/utopia-php/compression/tree/0.1.2"
},
"time": "2024-11-08T14:59:54+00:00"
},
{
"name": "utopia-php/config",
"version": "0.2.2",
@@ -1926,20 +1972,21 @@
},
{
"name": "utopia-php/framework",
"version": "0.33.8",
"version": "0.33.11",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5"
"reference": "354ff0d23bfc6e82bea0fe8e89e115cff1af8466"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/a7f577540a25cb90896fef2b64767bf8d700f3c5",
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5",
"url": "https://api.github.com/repos/utopia-php/http/zipball/354ff0d23bfc6e82bea0fe8e89e115cff1af8466",
"reference": "354ff0d23bfc6e82bea0fe8e89e115cff1af8466",
"shasum": ""
},
"require": {
"php": ">=8.0"
"php": ">=8.0",
"utopia-php/compression": "0.1.*"
},
"require-dev": {
"laravel/pint": "^1.2",
@@ -1965,9 +2012,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.33.8"
"source": "https://github.com/utopia-php/http/tree/0.33.11"
},
"time": "2024-08-15T14:10:09+00:00"
"time": "2024-11-08T18:47:43+00:00"
},
{
"name": "utopia-php/image",
@@ -3513,16 +3560,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.12.0",
"version": "1.12.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
"shasum": ""
},
"require": {
@@ -3561,7 +3608,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
},
"funding": [
{
@@ -3569,7 +3616,7 @@
"type": "tidelift"
}
],
"time": "2024-06-12T14:39:25+00:00"
"time": "2024-11-08T17:47:46+00:00"
},
{
"name": "nikic/php-parser",
@@ -4004,16 +4051,16 @@
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "5.5.0",
"version": "5.5.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "54e10d44fc1a84e2598d26f70d4f6f1f233e228a"
"reference": "0c70d2c566e899666f367ab7b80986beb3581e6f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/54e10d44fc1a84e2598d26f70d4f6f1f233e228a",
"reference": "54e10d44fc1a84e2598d26f70d4f6f1f233e228a",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/0c70d2c566e899666f367ab7b80986beb3581e6f",
"reference": "0c70d2c566e899666f367ab7b80986beb3581e6f",
"shasum": ""
},
"require": {
@@ -4062,29 +4109,29 @@
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.5.0"
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.5.1"
},
"time": "2024-11-04T21:26:31+00:00"
"time": "2024-11-06T11:58:54+00:00"
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.9.0",
"version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d"
"reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/1fb5ba8d045f5dd984ebded5b1cc66f29459422d",
"reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a",
"reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a",
"shasum": ""
},
"require": {
"doctrine/deprecations": "^1.0",
"php": "^7.3 || ^8.0",
"phpdocumentor/reflection-common": "^2.0",
"phpstan/phpdoc-parser": "^1.18"
"phpstan/phpdoc-parser": "^1.18|^2.0"
},
"require-dev": {
"ext-tokenizer": "*",
@@ -4120,9 +4167,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.9.0"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0"
},
"time": "2024-11-03T20:11:34+00:00"
"time": "2024-11-09T15:12:26+00:00"
},
{
"name": "phpspec/prophecy",
@@ -6876,16 +6923,16 @@
},
{
"name": "twig/twig",
"version": "v3.14.0",
"version": "v3.14.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72"
"reference": "f405356d20fb43603bcadc8b09bfb676cb04a379"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/f405356d20fb43603bcadc8b09bfb676cb04a379",
"reference": "f405356d20fb43603bcadc8b09bfb676cb04a379",
"shasum": ""
},
"require": {
@@ -6939,7 +6986,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.14.0"
"source": "https://github.com/twigphp/Twig/tree/v3.14.1"
},
"funding": [
{
@@ -6951,7 +6998,7 @@
"type": "tidelift"
}
],
"time": "2024-09-09T17:55:12+00:00"
"time": "2024-11-06T18:17:38+00:00"
},
{
"name": "webmozart/glob",
@@ -7005,7 +7052,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
+1
View File
@@ -96,6 +96,7 @@ services:
- _APP_EDITION
- _APP_WORKER_PER_CORE
- _APP_LOCALE
- _APP_COMPRESSION_MIN_SIZE_BYTES
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_SESSION_ALERTS
+1 -1
View File
@@ -1 +1 @@
Get a team member by the membership unique id. All team members have read access for this resource.
Get a team member by the membership unique id. All team members have read access for this resource. Hide sensitive attributes from the response by toggling membership privacy in the Console.
+1 -1
View File
@@ -1 +1 @@
Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint.
Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. Hide sensitive attributes from the response by toggling membership privacy in the Console.
+4
View File
@@ -121,6 +121,10 @@ class Audit extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -112,6 +112,10 @@ class Build extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -74,6 +74,10 @@ class Certificate extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -108,6 +108,10 @@ class Database extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
try {
$dsn = new DSN($this->getProject()->getAttribute('database'));
} catch (\InvalidArgumentException) {
+4
View File
@@ -140,6 +140,10 @@ class Delete extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+22
View File
@@ -65,6 +65,24 @@ class Event
{
}
/**
* Set paused state for this event.
*/
public function setPaused(bool $paused): self
{
$this->paused = $paused;
return $this;
}
/**
* Get paused state for this event.
*/
public function getPaused(): bool
{
return $this->paused;
}
/**
* Set queue used for this event.
*
@@ -302,6 +320,10 @@ class Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -213,6 +213,10 @@ class Func extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
$events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null;
+4
View File
@@ -404,6 +404,10 @@ class Mail extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+4
View File
@@ -182,6 +182,10 @@ class Messaging extends Event
*/
public function trigger(): string | bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
+3
View File
@@ -75,6 +75,9 @@ class Migration extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
+1 -1
View File
@@ -32,7 +32,7 @@ class Realtime extends Event
*/
public function trigger(): string|bool
{
if (empty($this->event)) {
if ($this->paused || empty($this->event)) {
return false;
}
+4
View File
@@ -57,6 +57,10 @@ class Usage extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
'project' => $this->getProject(),
+4
View File
@@ -38,6 +38,10 @@ class UsageDump extends Event
*/
public function trigger(): string|bool
{
if ($this->paused) {
return false;
}
$client = new Client($this->queue, $this->connection);
return $client->enqueue([
@@ -520,9 +520,7 @@ class Certificates extends Action
private function updateDomainDocuments(string $certificateId, string $domain, bool $success, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions): void
{
$rule = $dbForConsole->findOne('rules', [
Query::equal('domain', [$domain]),
]);
$rule = $dbForConsole->getDocument('rules', md5($domain));
if (!$rule->isEmpty()) {
$rule->setAttribute('certificateId', $certificateId);
@@ -36,13 +36,13 @@ class Membership extends Model
])
->addRule('userName', [
'type' => self::TYPE_STRING,
'description' => 'User name.',
'description' => 'User name. Hide this attribute by toggling membership privacy in the Console.',
'default' => '',
'example' => 'John Doe',
])
->addRule('userEmail', [
'type' => self::TYPE_STRING,
'description' => 'User email address.',
'description' => 'User email address. Hide this attribute by toggling membership privacy in the Console.',
'default' => '',
'example' => 'john@appwrite.io',
])
@@ -78,7 +78,7 @@ class Membership extends Model
])
->addRule('mfa', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Multi factor authentication status, true if the user has MFA enabled or false otherwise.',
'description' => 'Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console.',
'default' => false,
'example' => false,
])
@@ -151,6 +151,24 @@ class Project extends Model
'default' => false,
'example' => true,
])
->addRule('authMembershipsUserName', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to show user names in the teams membership response.',
'default' => false,
'example' => true,
])
->addRule('authMembershipsUserEmail', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to show user emails in the teams membership response.',
'default' => false,
'example' => true,
])
->addRule('authMembershipsMfa', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to show user MFA status in the teams membership response.',
'default' => false,
'example' => true,
])
->addRule('oAuthProviders', [
'type' => Response::MODEL_AUTH_PROVIDER,
'description' => 'List of Auth Providers.',
@@ -348,6 +366,9 @@ class Project extends Model
$document->setAttribute('authPersonalDataCheck', $authValues['personalDataCheck'] ?? false);
$document->setAttribute('authMockNumbers', $authValues['mockNumbers'] ?? []);
$document->setAttribute('authSessionAlerts', $authValues['sessionAlerts'] ?? false);
$document->setAttribute('authMembershipsUserName', $authValues['membershipsUserName'] ?? true);
$document->setAttribute('authMembershipsUserEmail', $authValues['membershipsUserEmail'] ?? true);
$document->setAttribute('authMembershipsMfa', $authValues['membershipsMfa'] ?? true);
foreach ($auth as $index => $method) {
$key = $method['key'];
+9 -5
View File
@@ -179,9 +179,14 @@ class Client
default => http_build_query($params),
};
foreach ($headers as $i => $header) {
$headers[] = $i . ':' . $header;
unset($headers[$i]);
$formattedHeaders = [];
foreach ($headers as $key => $value) {
if (strtolower($key) === 'accept-encoding') {
curl_setopt($ch, CURLOPT_ENCODING, $value);
continue;
} else {
$formattedHeaders[] = $key . ': ' . $value;
}
}
curl_setopt($ch, CURLOPT_PATH_AS_IS, 1);
@@ -189,7 +194,7 @@ class Client
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_HTTPHEADER, $formattedHeaders);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders, &$cookies) {
@@ -220,7 +225,6 @@ class Client
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
$responseBody = curl_exec($ch);
$responseType = $responseHeaders['content-type'] ?? '';
$responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+137
View File
@@ -0,0 +1,137 @@
<?php
namespace Tests\E2E\General;
use Appwrite\ID;
use Appwrite\Permission;
use Appwrite\Role;
use CURLFile;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
class CompressionTest extends Scope
{
use ProjectCustom;
use SideServer;
public function testSmallResponse()
{
// with header
$response = $this->client->call(Client::METHOD_GET, '/ping', [
'accept-encoding' => 'gzip',
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('Pong!', $response['body']);
$this->assertLessThan(1024, strlen($response['body']));
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
// without header
$response = $this->client->call(Client::METHOD_GET, '/ping', [
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('Pong!', $response['body']);
$this->assertLessThan(1024, strlen($response['body']));
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
}
public function testLargeResponse()
{
// create an anonymous user
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
'x-appwrite-project' => $this->getProject()['$id'],
'content-type' => 'application/json',
], $this->getHeaders()), [
'userId' => ID::unique(),
'email' => 'test@localhost.test',
'password' => 'password',
'name' => 'User Name',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$userId = $response['body']['$id'];
// set prefs with 2000 bytes of data
$prefs = ["longValue" => str_repeat('a', 2000)];
$response = $this->client->call(Client::METHOD_PATCH, '/users/' . $userId . '/prefs', array_merge([
'x-appwrite-project' => $this->getProject()['$id'],
'content-type' => 'application/json',
], $this->getHeaders()), [
'prefs' => $prefs,
]);
$this->assertEquals(200, $response['headers']['status-code']);
// get prefs with compression
$response = $this->client->call(Client::METHOD_GET, '/users/' . $userId . '/prefs', array_merge([
'x-appwrite-project' => $this->getProject()['$id'],
'accept-encoding' => 'gzip',
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayHasKey('content-encoding', $response['headers'], 'Content encoding should be gzip, headers received: ' . json_encode($response['headers'], JSON_PRETTY_PRINT));
$this->assertLessThan(2000, intval($response['headers']['content-length']));
// get prefs without compression
$response = $this->client->call(Client::METHOD_GET, '/users/' . $userId . '/prefs', array_merge([
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertGreaterThanOrEqual(2000, intval($response['headers']['content-length']));
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
}
public function testImageResponse()
{
// create bucket
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'bucketId' => ID::unique(),
'name' => 'Test Bucket',
'fileSecurity' => true,
]);
$bucketId = $bucket['body']['$id'];
$this->assertEquals(201, $bucket['headers']['status-code']);
// upload image
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'fileId' => ID::unique(),
'file' => new CURLFile(realpath(__DIR__ . '/../../resources/logo.png'), 'image/png', 'logo.png'),
'permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$fileId = $file['body']['$id'];
// get image with header
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
'content-type' => 'application/json',
'accept-encoding' => 'gzip',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
// get image without
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayNotHasKey('content-encoding', $response['headers']);
}
}
@@ -1,12 +1,13 @@
<?php
namespace Tests\E2E\Services\Functions;
namespace Tests\E2E\Services\FunctionsSchedule;
use Appwrite\ID;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Tests\E2E\Services\Functions\FunctionsBase;
use Utopia\Database\Helpers\Role;
class FunctionsScheduleTest extends Scope
+8 -8
View File
@@ -30,8 +30,8 @@ trait TeamsBaseClient
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
$this->assertFalse($response['body']['memberships'][0]['mfa']);
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('userName', $response['body']['memberships'][0]);
$this->assertArrayHasKey('userEmail', $response['body']['memberships'][0]);
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
$this->assertContains('owner', $response['body']['memberships'][0]['roles']);
$this->assertContains('player', $response['body']['memberships'][0]['roles']);
@@ -96,8 +96,8 @@ trait TeamsBaseClient
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]);
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('userName', $response['body']['memberships'][0]);
$this->assertArrayHasKey('userEmail', $response['body']['memberships'][0]);
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
$this->assertContains('owner', $response['body']['memberships'][0]['roles']);
$this->assertContains('player', $response['body']['memberships'][0]['roles']);
@@ -112,8 +112,8 @@ trait TeamsBaseClient
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]);
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('userName', $response['body']['memberships'][0]);
$this->assertArrayHasKey('userEmail', $response['body']['memberships'][0]);
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
$this->assertContains('owner', $response['body']['memberships'][0]['roles']);
$this->assertContains('player', $response['body']['memberships'][0]['roles']);
@@ -157,8 +157,8 @@ trait TeamsBaseClient
$this->assertNotEmpty($response['body']['$id']);
$this->assertFalse($response['body']['mfa']);
$this->assertNotEmpty($response['body']['userId']);
$this->assertNotEmpty($response['body']['userName']);
$this->assertNotEmpty($response['body']['userEmail']);
$this->assertArrayHasKey('userName', $response['body']);
$this->assertArrayHasKey('userEmail', $response['body']);
$this->assertNotEmpty($response['body']['teamId']);
$this->assertNotEmpty($response['body']['teamName']);
$this->assertCount(1, $response['body']['roles']);
+62 -1
View File
@@ -29,7 +29,7 @@ trait TeamsBaseServer
* Test for FAILURE
*/
return [];
return $data;
}
/**
@@ -60,6 +60,67 @@ trait TeamsBaseServer
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['joined'])); // is null in DB
$this->assertEquals(true, $response['body']['confirm']);
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/auth/memberships-privacy', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'userName' => false,
'userEmail' => false,
'mfa' => false,
]);
$this->assertEquals(200, $response['headers']['status-code']);
/**
* Test that sensitive fields are not hidden, as we are on console
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
// Assert that sensitive fields are present
$this->assertNotEmpty($response['body']['memberships'][0]['userName']);
$this->assertNotEmpty($response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('mfa', $response['body']['memberships'][0]);
/**
* Update project settings to show sensitive fields
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $this->getProject()['$id'] . '/auth/memberships-privacy', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'userName' => true,
'userEmail' => true,
'mfa' => true,
]);
$this->assertEquals(200, $response['headers']['status-code']);
/**
* Test that sensitive fields are shown
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
// Assert that sensitive fields are present
$this->assertNotEmpty($response['body']['memberships'][0]['userName']);
$this->assertNotEmpty($response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('mfa', $response['body']['memberships'][0]);
/**
* Test for FAILURE
*/
@@ -14,6 +14,77 @@ class TeamsCustomClientTest extends Scope
use ProjectCustom;
use SideClient;
/**
* @depends testGetTeamMemberships
*/
public function testGetMembershipPrivacy($data)
{
$teamUid = $data['teamUid'] ?? '';
$projectId = $this->getProject()['$id'];
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $projectId . '/auth/memberships-privacy', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'userName' => false,
'userEmail' => false,
'mfa' => false,
]);
$this->assertEquals(200, $response['headers']['status-code']);
/**
* Test that sensitive fields are hidden
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
// Assert that sensitive fields are not present
$this->assertEmpty($response['body']['memberships'][0]['userName']);
$this->assertEmpty($response['body']['memberships'][0]['userEmail']);
$this->assertFalse($response['body']['memberships'][0]['mfa']);
/**
* Update project settings to show sensitive fields
*/
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $projectId . '/auth/memberships-privacy', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), [
'userName' => true,
'userEmail' => true,
'mfa' => true,
]);
$this->assertEquals(200, $response['headers']['status-code']);
/**
* Test that sensitive fields are shown
*/
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertIsInt($response['body']['total']);
$this->assertNotEmpty($response['body']['memberships'][0]['$id']);
// Assert that sensitive fields are present
$this->assertNotEmpty($response['body']['memberships'][0]['userName']);
$this->assertNotEmpty($response['body']['memberships'][0]['userEmail']);
$this->assertArrayHasKey('mfa', $response['body']['memberships'][0]);
}
/**
* @depends testUpdateTeamMembership
*/
@@ -62,6 +62,7 @@ services:
- redis
# - clamav
environment:
- _APP_COMPRESSION_MIN_SIZE_BYTES
- _APP_ENV
- _APP_OPTIONS_ABUSE
- _APP_OPTIONS_ROUTER_PROTECTION