appwrite/src/Appwrite/Migration/Version/V15.php

1033 lines
43 KiB
PHP
Raw Normal View History

2022-09-07 08:43:05 +00:00
<?php
namespace Appwrite\Migration\Version;
use Appwrite\Migration\Migration;
2022-09-08 16:46:18 +00:00
use Appwrite\OpenSSL\OpenSSL;
2022-09-07 08:43:05 +00:00
use Exception;
2022-09-08 16:46:18 +00:00
use Utopia\App;
2022-09-07 08:43:05 +00:00
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document;
2022-09-08 16:46:18 +00:00
use Utopia\Database\ID;
use Utopia\Database\Permission;
use Utopia\Database\Role;
2022-09-07 08:43:05 +00:00
class V15 extends Migration
{
/**
* @var \PDO $pdo
*/
private $pdo;
public function execute(): void
{
global $register;
$this->pdo = $register->get('db');
/**
* Disable SubQueries for Speed.
*/
2022-09-08 16:46:18 +00:00
foreach (['subQueryAttributes', 'subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships', 'subqueryVariables'] as $name) {
2022-09-07 08:43:05 +00:00
Database::addFilter($name, fn () => null, fn () => []);
}
Console::log('Migrating project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')');
Console::info('Migrating Collections');
$this->migrateCollections();
2022-09-08 16:46:18 +00:00
Console::info('Migrating Databases');
$this->migrateDatabases();
Console::info('Migrating Buckets');
$this->migrateBuckets();
Console::info('Migrating Documents');
$this->forEachDocument([$this, 'fixDocument']);
Console::info("Clean up 'write' Permissions");
foreach ($this->collections as $collection) {
if ($collection['$collection'] === Database::METADATA) {
$this->removeWritePermissions($collection['$id']);
}
}
}
protected function migrateBuckets(): void
{
foreach ($this->documentsIterator('buckets') as $bucket) {
$bucketTable = "bucket_{$bucket->getInternalId()}";
$this->createPermissionsColumn($bucketTable);
$this->migrateDateTimeAttribute($bucketTable, '_createdAt');
$this->migrateDateTimeAttribute($bucketTable, '_updatedAt');
$this->populatePermissionsAttribute(
document: $bucket,
addCreatePermission: true
);
if (!is_null($bucket->getAttribute('permission'))) {
$bucket->setAttribute('fileSecurity', $bucket->getAttribute('permissions') === 'document');
}
if (is_null($bucket->getAttribute('compression'))) {
$bucket->setAttribute('compression', 'none');
}
$this->projectDB->updateDocument('buckets', $bucket->getId(), $bucket);
Console::info("Migrating Files of {$bucket->getId()} ({$bucket->getAttribute('name')})");
foreach ($this->documentsIterator($bucketTable) as $file) {
$this->populatePermissionsAttribute(
document: $file,
table: $bucketTable,
addCreatePermission: false
);
$this->projectDB->updateDocument($bucketTable, $file->getId(), $file);
}
$this->removeWritePermissions($bucketTable);
}
try {
$this->projectDB->deleteAttribute('buckets', 'permission');
} catch (\Throwable $th) {
Console::warning("'permissions' from buckets: {$th->getMessage()}");
}
}
protected function migrateDatabases(): void
{
foreach ($this->documentsIterator('databases') as $database) {
$databaseTable = "database_{$database->getInternalId()}";
$this->createPermissionsColumn($databaseTable);
$this->migrateDateTimeAttribute($databaseTable, '_createdAt');
$this->migrateDateTimeAttribute($databaseTable, '_updatedAt');
$this->populatePermissionsAttribute(
document: $database,
table: 'databases',
addCreatePermission: false
);
$this->projectDB->updateDocument('databases', $database->getId(), $database);
try {
$this->createAttributeFromCollection($this->projectDB, $databaseTable, 'documentSecurity', 'collections');
} catch (\Throwable $th) {
Console::warning("'documentSecurity' from {$databaseTable}: {$th->getMessage()}");
}
Console::info("Migrating Collections of {$database->getId()} ({$database->getAttribute('name')})");
foreach ($this->documentsIterator($databaseTable) as $collection) {
$collectionTable = "{$databaseTable}_collection_{$collection->getInternalId()}";
$this->createPermissionsColumn($collectionTable);
$this->migrateDateTimeAttribute($collectionTable, '_createdAt');
$this->migrateDateTimeAttribute($collectionTable, '_updatedAt');
$this->populatePermissionsAttribute(
document: $collection,
table: $databaseTable,
addCreatePermission: true
);
if (!is_null($collection->getAttribute('permission'))) {
$collection->setAttribute('documentSecurity', $collection->getAttribute('permissions') === 'document');
}
$this->projectDB->updateDocument($databaseTable, $collection->getId(), $collection);
Console::info("Migrating Documents of {$collection->getId()} ({$collection->getAttribute('name')})");
foreach ($this->documentsIterator($collectionTable) as $document) {
$this->populatePermissionsAttribute(
document: $document,
table: $collectionTable,
addCreatePermission: false
);
$this->projectDB->updateDocument($collectionTable, $document->getId(), $document);
}
$this->removeWritePermissions($collectionTable);
}
$this->removeWritePermissions($databaseTable);
try {
$this->projectDB->deleteAttribute("database_{$database->getInternalId()}", 'permission');
} catch (\Throwable $th) {
Console::warning("'permission' from {$databaseTable}: {$th->getMessage()}");
}
}
}
/**
*
* @param string $table
* @return void
*/
protected function removeWritePermissions(string $table): void
{
try {
$this->pdo->prepare("DELETE FROM `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}_perms` WHERE _type = 'write'")->execute();
} catch (\Throwable $th) {
Console::warning("Remove 'write' permissions from {$table}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
}
/**
* Returns all columns from the Table.
* @param string $table
* @return array
* @throws \Exception
* @throws \PDOException
*/
protected function getSQLColumnTypes(string $table): array
{
$query = $this->pdo->prepare("SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '_{$this->project->getInternalId()}_{$table}' AND table_schema = '{$this->projectDB->getDefaultDatabase()}'");
$query->execute();
return array_reduce($query->fetchAll(), function (array $carry, array $item) {
$carry[$item['COLUMN_NAME']] = $item['DATA_TYPE'];
return $carry;
}, []);
}
/**
* Migrates all Integer colums for timestamps to DateTime
* @return void
* @throws \Exception
*/
protected function migrateDateTimeAttribute(string $table, string $attribute): void
{
$columns = $this->getSQLColumnTypes($table);
if ($columns[$attribute] === 'int') {
try {
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}` MODIFY {$attribute} VARCHAR(64)")->execute();
2022-09-08 16:46:18 +00:00
$this->pdo->prepare("UPDATE `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}` SET {$attribute} = IF({$attribute} = 0, NULL, FROM_UNIXTIME({$attribute}))")->execute();
$columns[$attribute] = 'varchar';
2022-09-07 08:43:05 +00:00
} catch (\Throwable $th) {
Console::warning($th->getMessage());
}
}
if ($columns[$attribute] === 'varchar') {
try {
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}` MODIFY {$attribute} DATETIME(3)")->execute();
} catch (\Throwable $th) {
Console::warning($th->getMessage());
}
}
2022-09-08 16:46:18 +00:00
/**
* Skip adding filter on internal attributes.
*/
if (!str_starts_with($attribute, '_')) {
try {
/**
* Add datetime filter.
*/
$this->projectDB->updateAttributeFilters($table, ID::custom($attribute), ['datetime']);
/**
* Change data type to DateTime.
*/
$this->projectDB->updateAttribute(
collection: $table,
id: $attribute,
type: Database::VAR_DATETIME,
signed: false
);
} catch (\Throwable $th) {
Console::warning("Add 'datetime' filter to '{$attribute}' from {$table}: {$th->getMessage()}");
}
}
$this->projectDB->deleteCachedCollection($table);
}
protected function createPermissionsColumn(string $table): void
{
$columns = $this->getSQLColumnTypes($table);
if (!array_key_exists('_permissions', $columns)) {
try {
$this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}` ADD `_permissions` MEDIUMTEXT DEFAULT NULL")->execute();
} catch (\Throwable $th) {
Console::warning("Add '_permissions' column to '{$table}': {$th->getMessage()}");
}
}
}
protected function populatePermissionsAttribute(Document &$document, ?string $table = null, bool $addCreatePermission = true): void
{
$table ??= $document->getCollection();
$query = $this->pdo->prepare("SELECT * FROM `{$this->projectDB->getDefaultDatabase()}`.`_{$this->project->getInternalId()}_{$table}_perms` WHERE _document = '{$document->getId()}'");
$query->execute();
$results = $query->fetchAll();
$permissions = [];
foreach ($results as $result) {
$type = $result['_type'];
$permission = $result['_permission'];
if ($type === 'write') {
$permissions[] = "update(\"{$permission}\")";
$permissions[] = "delete(\"{$permission}\")";
if ($addCreatePermission) {
$permissions[] = "create(\"{$permission}\")";
}
} else {
$permissions[] = "{$type}(\"{$permission}\")";
}
}
$document->setAttribute('$permissions', $permissions);
2022-09-07 08:43:05 +00:00
}
/**
* Migrate all Collections.
*
* @return void
*/
protected function migrateCollections(): void
{
foreach ($this->collections as $collection) {
$id = $collection['$id'];
Console::log("- {$id}");
$this->projectDB->setNamespace("_{$this->project->getInternalId()}");
switch ($id) {
case '_metadata':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
Console::log(' - create "cache" collection');
$this->createCollection('cache');
Console::log(' - create "variables" collection');
$this->createCollection('variables');
$this->projectDB->deleteCachedCollection($id);
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'abuse':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'attributes':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
break;
2022-09-07 08:43:05 +00:00
case 'audit':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'buckets':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
try {
/**
* Create 'compression' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'compression');
} catch (\Throwable $th) {
Console::warning("'compression' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'fileSecurity' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'fileSecurity');
} catch (\Throwable $th) {
Console::warning("'fileSecurity' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_enabled' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_enabled');
} catch (\Throwable $th) {
Console::warning("'_key_enabled' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_name' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_name');
} catch (\Throwable $th) {
Console::warning("'_key_name' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_fileSecurity' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_fileSecurity');
} catch (\Throwable $th) {
Console::warning("'_key_fileSecurity' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_maximumFileSize' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_maximumFileSize');
} catch (\Throwable $th) {
Console::warning("'_key_maximumFileSize' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_encryption' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_encryption');
} catch (\Throwable $th) {
Console::warning("'_key_encryption' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_antivirus' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_antivirus');
} catch (\Throwable $th) {
Console::warning("'_key_antivirus' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'builds':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'startTime');
$this->migrateDateTimeAttribute($id, 'endTime');
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'certificates':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'issueDate');
$this->migrateDateTimeAttribute($id, 'renewDate');
$this->migrateDateTimeAttribute($id, 'updated');
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'databases':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'deployments':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
try {
/**
* Create '_key_entrypoint' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_entrypoint');
} catch (\Throwable $th) {
Console::warning("'_key_entrypoint' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_size' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_size');
} catch (\Throwable $th) {
Console::warning("'_key_size' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_buildId' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_buildId');
} catch (\Throwable $th) {
Console::warning("'_key_buildId' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_activate' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_activate');
} catch (\Throwable $th) {
Console::warning("'_key_activate' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'domains':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'updated');
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'executions':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
try {
/**
* Create 'stdout' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'stdout');
} catch (\Throwable $th) {
Console::warning("'stdout' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_trigger' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_trigger');
} catch (\Throwable $th) {
Console::warning("'_key_trigger' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_status' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_status');
} catch (\Throwable $th) {
Console::warning("'_key_status' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_statusCode' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_statusCode');
} catch (\Throwable $th) {
Console::warning("'_key_statusCode' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_time' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_time');
} catch (\Throwable $th) {
Console::warning("'_key_time' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'functions':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'scheduleNext');
$this->migrateDateTimeAttribute($id, 'schedulePrevious');
/**
* Migrate function variables.
*/
Console::info("Migrating Variables");
foreach ($this->documentsIterator('functions') as $function) {
foreach ($function->getAttribute('vars', []) as $key => $value) {
$variableId = ID::unique();
$variable = new Document([
'$id' => $variableId,
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'functionId' => $function->getId(),
'functionInternalId' => $function->getInternalId(),
'key' => $key,
'value' => $value,
'search' => implode(' ', [$variableId, $key, $function->getId()])
]);
$this->projectDB->createDocument('variables', $variable);
}
$this->projectDB->deleteAttribute('functions', 'vars');
$this->createAttributeFromCollection($this->projectDB, 'functions', 'vars');
}
try {
/**
* Create '_key_name' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_name');
} catch (\Throwable $th) {
Console::warning("'_key_name' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_status' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_status');
} catch (\Throwable $th) {
Console::warning("'_key_status' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_runtime' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_runtime');
} catch (\Throwable $th) {
Console::warning("'_key_runtime' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_deployment' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_deployment');
} catch (\Throwable $th) {
Console::warning("'_key_deployment' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_schedule' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_schedule');
} catch (\Throwable $th) {
Console::warning("'_key_schedule' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_scheduleNext' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_scheduleNext');
} catch (\Throwable $th) {
Console::warning("'_key_scheduleNext' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_schedulePrevious' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_schedulePrevious');
} catch (\Throwable $th) {
Console::warning("'_key_schedulePrevious' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_timeout' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_timeout');
} catch (\Throwable $th) {
Console::warning("'_key_timeout' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'indexes':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'keys':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'expire');
try {
/**
* Create 'accessedAt' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'accessedAt');
} catch (\Throwable $th) {
Console::warning("'accessedAt' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'sdks' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'sdks');
} catch (\Throwable $th) {
Console::warning("'sdks' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_accessedAt' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_accessedAt');
} catch (\Throwable $th) {
Console::warning("'_key_accessedAt' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'memberships':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'invited');
$this->migrateDateTimeAttribute($id, 'joined');
try {
/**
* Create '_key_userId' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_userId');
} catch (\Throwable $th) {
Console::warning("'_key_userId' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_teamId' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_teamId');
} catch (\Throwable $th) {
Console::warning("'_key_teamId' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_invited' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_invited');
} catch (\Throwable $th) {
Console::warning("'_key_invited' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_joined' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_joined');
} catch (\Throwable $th) {
Console::warning("'_key_joined' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_confirm' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_confirm');
} catch (\Throwable $th) {
Console::warning("'_key_confirm' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'platforms':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'projects':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
try {
/**
* Create '_key_name' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_name');
} catch (\Throwable $th) {
Console::warning("'_key_name' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'realtime':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'timestamp');
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'sessions':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'expire');
$this->migrateDateTimeAttribute($id, 'providerAccessTokenExpiry');
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'stats':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'time');
try {
/**
* Re-Create '_key_metric' index
*/
@$this->projectDB->deleteIndex($id, '_key_metric');
$this->createIndexFromCollection($this->projectDB, $id, '_key_period_time');
} catch (\Throwable $th) {
Console::warning("'_key_period_time' from {$id}: {$th->getMessage()}");
}
try {
/**
* Re-Create '_key_metric_period' index
*/
@$this->projectDB->deleteIndex($id, '_key_metric_period');
$this->createIndexFromCollection($this->projectDB, $id, '_key_metric_period_time');
} catch (\Throwable $th) {
Console::warning("'_key_metric_period_time' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'teams':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
try {
/**
* Create '_key_name' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_name');
} catch (\Throwable $th) {
Console::warning("'_key_name' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_total' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_total');
} catch (\Throwable $th) {
Console::warning("'_key_total' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'tokens':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'expire');
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'users':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
2022-09-08 16:46:18 +00:00
$this->migrateDateTimeAttribute($id, 'registration');
$this->migrateDateTimeAttribute($id, 'passwordUpdate');
try {
/**
* Create 'hash' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'hash');
} catch (\Throwable $th) {
Console::warning("'hash' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'hashOptions' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'hashOptions');
} catch (\Throwable $th) {
Console::warning("'hashOptions' from {$id}: {$th->getMessage()}");
}
/**
* Update user password before adding encrypt filter.
*/
Console::info("Migrating Passwords");
foreach ($this->documentsIterator('users') as $user) {
/**
* Skip when no password.
*/
if (is_null($user->getAttribute('password'))) {
continue;
}
/**
* Skip when password is JSON.
*/
json_decode($user->getAttribute('password'));
if (json_last_error() === JSON_ERROR_NONE) {
continue;
}
/**
* Add default hash.
*/
$user->setAttribute('hash', 'bcrypt');
/**
* Add default hash options.
*/
$user->setAttribute('hashOptions', json_encode(['cost' => 8]));
/**
* Encrypt hashed password.
*/
$user->setAttribute('password', $this->encryptFilter($user->getAttribute('password')));
$this->projectDB->updateDocument('users', $user->getId(), $user);
}
try {
/**
* Add datetime filter to password.
*/
$this->projectDB->updateAttributeFilters($id, 'password', ['encrypt']);
} catch (\Throwable $th) {
Console::warning("Add 'encrypt' filter to 'password' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_name' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_name');
} catch (\Throwable $th) {
Console::warning("'_key_name' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_status' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_status');
} catch (\Throwable $th) {
Console::warning("'_key_status' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_passwordUpdate' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_passwordUpdate');
} catch (\Throwable $th) {
Console::warning("'_key_passwordUpdate' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_registration' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_registration');
} catch (\Throwable $th) {
Console::warning("'_key_registration' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_emailVerification' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_emailVerification');
} catch (\Throwable $th) {
Console::warning("'_key_emailVerification' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create '_key_phoneVerification' index
*/
$this->createIndexFromCollection($this->projectDB, $id, '_key_phoneVerification');
} catch (\Throwable $th) {
Console::warning("'_key_phoneVerification' from {$id}: {$th->getMessage()}");
}
2022-09-07 08:43:05 +00:00
break;
2022-09-08 16:46:18 +00:00
2022-09-07 08:43:05 +00:00
case 'webhooks':
2022-09-08 16:46:18 +00:00
$this->createPermissionsColumn($id);
2022-09-07 08:43:05 +00:00
$this->migrateDateTimeAttribute($id, '_createdAt');
$this->migrateDateTimeAttribute($id, '_updatedAt');
break;
default:
2022-09-09 09:38:54 +00:00
break;
2022-09-07 08:43:05 +00:00
}
usleep(50000);
}
}
/**
* Fix run on each document
*
* @param \Utopia\Database\Document $document
* @return \Utopia\Database\Document
*/
protected function fixDocument(Document $document)
{
switch ($document->getCollection()) {
2022-09-08 16:46:18 +00:00
case 'cache':
case 'variables':
return null;
2022-09-07 08:43:05 +00:00
case 'projects':
/**
2022-09-08 16:46:18 +00:00
* Populate permissions attribute.
2022-09-07 08:43:05 +00:00
*/
2022-09-08 16:46:18 +00:00
$this->populatePermissionsAttribute($document, addCreatePermission: false);
2022-09-07 08:43:05 +00:00
/**
2022-09-08 16:46:18 +00:00
* Bump version number.
2022-09-07 08:43:05 +00:00
*/
2022-09-08 16:46:18 +00:00
$document->setAttribute('version', '1.0.0-RC1');
2022-09-07 08:43:05 +00:00
break;
case 'users':
/**
2022-09-08 16:46:18 +00:00
* Populate permissions attribute.
2022-09-07 08:43:05 +00:00
*/
2022-09-08 16:46:18 +00:00
$this->populatePermissionsAttribute($document, addCreatePermission: false);
2022-09-09 09:38:54 +00:00
$document->setAttribute('$permissions', Permission::read(Role::any()), Document::SET_TYPE_APPEND);
break;
case 'sessions':
$userId = $document->getAttribute('userId');
$document
->setAttribute('$permissions', Permission::read(Role::user($userId)), Document::SET_TYPE_APPEND)
->setAttribute('$permissions', Permission::update(Role::user($userId)), Document::SET_TYPE_APPEND)
->setAttribute('$permissions', Permission::delete(Role::user($userId)), Document::SET_TYPE_APPEND);
2022-09-07 08:43:05 +00:00
break;
}
return $document;
}
2022-09-08 16:46:18 +00:00
protected function encryptFilter(string $value)
{
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
return json_encode([
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
'iv' => \bin2hex($iv),
'tag' => \bin2hex($tag ?? ''),
'version' => '1',
]);
}
2022-09-07 08:43:05 +00:00
}