mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
This commit is contained in:
@@ -30,6 +30,7 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Authorization\Input;
|
||||
use Utopia\Database\Validator\Roles;
|
||||
use Utopia\Http\Http;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
@@ -169,8 +170,10 @@ Http::init()
|
||||
|
||||
// Handle special app role case
|
||||
if ($apiKey->getRole() === User::ROLE_APPS) {
|
||||
// Disable authorization checks for API keys
|
||||
$authorization->setDefaultStatus(false);
|
||||
// Disable authorization checks for project API keys
|
||||
if (($apiKey->getType() === API_KEY_STANDARD || $apiKey->getType() === API_KEY_DYNAMIC) && $apiKey->getProjectId() === $project->getId()) {
|
||||
$authorization->setDefaultStatus(false);
|
||||
}
|
||||
|
||||
$user = new User([
|
||||
'$id' => '',
|
||||
@@ -247,6 +250,35 @@ Http::init()
|
||||
|
||||
$queueForAudits->setUser($user);
|
||||
}
|
||||
|
||||
// Apply permission
|
||||
if ($apiKey->getType() === API_KEY_ORGANIZATION) {
|
||||
$authorization->addRole(Role::team($team->getId())->toString());
|
||||
$authorization->addRole(Role::team($team->getId(), 'owner')->toString());
|
||||
} elseif ($apiKey->getType() === API_KEY_ACCOUNT) {
|
||||
$authorization->addRole(Role::user($user->getId())->toString());
|
||||
$authorization->addRole(Role::users()->toString());
|
||||
|
||||
if ($user->getAttribute('emailVerification', false) || $user->getAttribute('phoneVerification', false)) {
|
||||
$authorization->addRole(Role::user($user->getId(), Roles::DIMENSION_VERIFIED)->toString());
|
||||
$authorization->addRole(Role::users(Roles::DIMENSION_VERIFIED)->toString());
|
||||
} else {
|
||||
$authorization->addRole(Role::user($user->getId(), Roles::DIMENSION_UNVERIFIED)->toString());
|
||||
$authorization->addRole(Role::users(Roles::DIMENSION_UNVERIFIED)->toString());
|
||||
}
|
||||
|
||||
foreach (\array_filter($user->getAttribute('memberships', []), fn ($membership) => ($membership['confirm'] ?? false) === true) as $nodeMembership) {
|
||||
$authorization->addRole(Role::team($nodeMembership['teamId'])->toString());
|
||||
$authorization->addRole(Role::member($nodeMembership->getId())->toString());
|
||||
foreach (($nodeMembership['roles'] ?? []) as $nodeRole) {
|
||||
$authorization->addRole(Role::team($nodeMembership['teamId'], $nodeRole)->toString());
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($user->getAttribute('labels', []) as $nodeLabel) {
|
||||
$authorization->addRole('label:' . $nodeLabel);
|
||||
}
|
||||
}
|
||||
} // Admin User Authentication
|
||||
elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) {
|
||||
$teamId = $team->getId();
|
||||
@@ -498,6 +530,7 @@ Http::init()
|
||||
$queueForMessaging->setProject($project);
|
||||
$queueForFunctions->setProject($project);
|
||||
$queueForBuilds->setProject($project);
|
||||
$queueForMails->setProject($project);
|
||||
|
||||
/* Auto-set platforms */
|
||||
$queueForFunctions->setPlatform($platform);
|
||||
|
||||
@@ -1298,6 +1298,7 @@ Http::setResource('team', function (Document $project, Database $dbForPlatform,
|
||||
} else {
|
||||
$route = $utopia->match($request);
|
||||
$path = !empty($route) ? $route->getPath() : $request->getURI();
|
||||
$orgHeader = $request->getHeader('x-appwrite-organization', '');
|
||||
if (str_starts_with($path, '/v1/projects/:projectId')) {
|
||||
$uri = $request->getURI();
|
||||
$pid = explode('/', $uri)[3];
|
||||
@@ -1312,6 +1313,8 @@ Http::setResource('team', function (Document $project, Database $dbForPlatform,
|
||||
|
||||
$team = $authorization->skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
|
||||
return $team;
|
||||
} elseif (!empty($orgHeader)) {
|
||||
return $authorization->skip(fn () => $dbForPlatform->getDocument('teams', $orgHeader));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -748,6 +748,7 @@ services:
|
||||
depends_on:
|
||||
- redis
|
||||
- maildev
|
||||
- ${_APP_DB_HOST:-mariadb}
|
||||
# - smtp
|
||||
environment:
|
||||
- _APP_ENV
|
||||
@@ -760,6 +761,12 @@ services:
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_SMTP_HOST
|
||||
- _APP_SMTP_PORT
|
||||
- _APP_SMTP_SECURE
|
||||
|
||||
@@ -932,16 +932,6 @@ class Builds extends Action
|
||||
$this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform, $queueForRealtime, $platform);
|
||||
}
|
||||
|
||||
/** Screenshot site */
|
||||
if ($resource->getCollection() === 'sites') {
|
||||
$queueForScreenshots
|
||||
->setDeploymentId($deployment->getId())
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
Console::log('Site screenshot queued');
|
||||
}
|
||||
|
||||
/** Set auto deploy */
|
||||
$activateBuild = false;
|
||||
if ($deployment->getAttribute('activate') === true) {
|
||||
@@ -1023,6 +1013,16 @@ class Builds extends Action
|
||||
Console::log('Deployment activated');
|
||||
}
|
||||
|
||||
/** Screenshot site */
|
||||
if ($resource->getCollection() === 'sites') {
|
||||
$queueForScreenshots
|
||||
->setDeploymentId($deployment->getId())
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
Console::log('Site screenshot queued');
|
||||
}
|
||||
|
||||
$this->afterDeploymentSuccess(
|
||||
$project,
|
||||
$deployment,
|
||||
|
||||
@@ -6,6 +6,7 @@ use Appwrite\Template\Template;
|
||||
use Exception;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Swoole\Runtime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Queue\Message;
|
||||
@@ -32,6 +33,7 @@ class Mails extends Action
|
||||
$this
|
||||
->desc('Mails worker')
|
||||
->inject('message')
|
||||
->inject('project')
|
||||
->inject('register')
|
||||
->inject('log')
|
||||
->callback($this->action(...));
|
||||
@@ -53,7 +55,7 @@ class Mails extends Action
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function action(Message $message, Registry $register, Log $log): void
|
||||
public function action(Message $message, Document $project, Registry $register, Log $log): void
|
||||
{
|
||||
Runtime::setHookFlags(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_TCP);
|
||||
$payload = $message->getPayload() ?? [];
|
||||
|
||||
@@ -735,6 +735,7 @@ class Migrations extends Action
|
||||
];
|
||||
|
||||
$queueForMails
|
||||
->setProject($project)
|
||||
->setSubject($subject)
|
||||
->setPreview($preview)
|
||||
->setBody($emailBody)
|
||||
|
||||
@@ -253,6 +253,7 @@ class Webhooks extends Action
|
||||
->setParam('{{year}}', date("Y"));
|
||||
|
||||
$queueForMails
|
||||
->setProject($project)
|
||||
->setSubject($subject)
|
||||
->setPreview($preview)
|
||||
->setBody($body->render());
|
||||
|
||||
@@ -456,6 +456,8 @@ class Response extends SwooleResponse
|
||||
|
||||
foreach ($data[$key] as $index => $item) {
|
||||
if ($item instanceof Document) {
|
||||
$ruleType = null;
|
||||
|
||||
if (\is_array($rule['type'])) {
|
||||
foreach ($rule['type'] as $type) {
|
||||
$condition = false;
|
||||
@@ -474,7 +476,7 @@ class Response extends SwooleResponse
|
||||
$ruleType = $rule['type'];
|
||||
}
|
||||
|
||||
if (!self::hasModel($ruleType)) {
|
||||
if ($ruleType === null || !self::hasModel($ruleType)) {
|
||||
throw new Exception('Missing model for rule: ' . $ruleType);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ class AttributeList extends Model
|
||||
Response::MODEL_ATTRIBUTE_POINT,
|
||||
Response::MODEL_ATTRIBUTE_LINE,
|
||||
Response::MODEL_ATTRIBUTE_POLYGON,
|
||||
Response::MODEL_ATTRIBUTE_VARCHAR,
|
||||
Response::MODEL_ATTRIBUTE_TEXT,
|
||||
Response::MODEL_ATTRIBUTE_MEDIUMTEXT,
|
||||
Response::MODEL_ATTRIBUTE_LONGTEXT,
|
||||
Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute
|
||||
],
|
||||
'description' => 'List of attributes.',
|
||||
|
||||
@@ -73,6 +73,10 @@ class Collection extends Model
|
||||
Response::MODEL_ATTRIBUTE_POINT,
|
||||
Response::MODEL_ATTRIBUTE_LINE,
|
||||
Response::MODEL_ATTRIBUTE_POLYGON,
|
||||
Response::MODEL_ATTRIBUTE_VARCHAR,
|
||||
Response::MODEL_ATTRIBUTE_TEXT,
|
||||
Response::MODEL_ATTRIBUTE_MEDIUMTEXT,
|
||||
Response::MODEL_ATTRIBUTE_LONGTEXT,
|
||||
Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute
|
||||
],
|
||||
'description' => 'Collection attributes.',
|
||||
|
||||
@@ -514,6 +514,63 @@ class DatabasesStringTypesTest extends Scope
|
||||
/**
|
||||
* @depends testCreateLongtextAttribute
|
||||
*/
|
||||
public function testListStringTypeAttributes(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
$collectionId = $data['collectionId'];
|
||||
|
||||
// Wait for attributes to be created
|
||||
sleep(2);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
$attributes = $response['body']['attributes'];
|
||||
$types = array_column($attributes, 'type');
|
||||
|
||||
$this->assertContains('varchar', $types);
|
||||
$this->assertContains('text', $types);
|
||||
$this->assertContains('mediumtext', $types);
|
||||
$this->assertContains('longtext', $types);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testListStringTypeAttributes
|
||||
*/
|
||||
public function testGetCollectionWithStringTypeAttributes(array $data): array
|
||||
{
|
||||
$databaseId = $data['databaseId'];
|
||||
$collectionId = $data['collectionId'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
$attributes = $response['body']['attributes'];
|
||||
$types = array_column($attributes, 'type');
|
||||
|
||||
$this->assertContains('varchar', $types);
|
||||
$this->assertContains('text', $types);
|
||||
$this->assertContains('mediumtext', $types);
|
||||
$this->assertContains('longtext', $types);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testGetCollectionWithStringTypeAttributes
|
||||
*/
|
||||
public function testUpdateVarcharAttribute(array $data): array
|
||||
{
|
||||
$this->markTestSkipped('Skipped until utopia-php/database updateAttribute supports VARCHAR type');
|
||||
|
||||
@@ -5377,7 +5377,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
]);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/** Test oauth2 with devKey and now get oauth2 is disabled */
|
||||
/** Test oauth2 with devKey and now flow works with untrusted URL too */
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
@@ -5385,8 +5385,37 @@ class ProjectsConsoleClientTest extends Scope
|
||||
], [
|
||||
'success' => 'https://example.com',
|
||||
'failure' => 'https://example.com'
|
||||
]);
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
], followRedirects: false);
|
||||
|
||||
$this->assertEquals(301, $response['headers']['status-code']);
|
||||
$this->assertArrayHasKey('location', $response['headers']);
|
||||
|
||||
$location = $response['headers']['location'];
|
||||
|
||||
|
||||
$locationClient = new Client();
|
||||
$locationClient->setEndpoint('');
|
||||
$locationClient->addHeader('x-appwrite-dev-key', $devKey['secret']);
|
||||
|
||||
$response = $locationClient->call(Client::METHOD_GET, $location, followRedirects: false);
|
||||
|
||||
$this->assertEquals(301, $response['headers']['status-code']);
|
||||
$this->assertArrayHasKey('location', $response['headers']);
|
||||
|
||||
$location = $response['headers']['location'];
|
||||
$this->assertStringStartsWith('http://appwrite:/v1/account/sessions/oauth2/callback/mock/', $response['headers']['location']);
|
||||
|
||||
$response = $locationClient->call(Client::METHOD_GET, $location, followRedirects: false);
|
||||
$this->assertEquals(301, $response['headers']['status-code']);
|
||||
$this->assertArrayHasKey('location', $response['headers']);
|
||||
|
||||
$location = $response['headers']['location'];
|
||||
$this->assertStringStartsWith('http://appwrite:/v1/account/sessions/oauth2/mock/redirect', $response['headers']['location']);
|
||||
|
||||
$response = $locationClient->call(Client::METHOD_GET, $location, followRedirects: false);
|
||||
|
||||
$this->assertEquals(301, $response['headers']['status-code']);
|
||||
$this->assertSame('https://example.com/#', $response['headers']['location']);
|
||||
|
||||
/** Ensure any hostname is allowed */
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [
|
||||
|
||||
@@ -279,6 +279,12 @@ services:
|
||||
- _APP_ENV
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_SMTP_HOST
|
||||
- _APP_SMTP_PORT
|
||||
|
||||
|
||||
Reference in New Issue
Block a user