Compare commits

...
Author SHA1 Message Date
Damodar Lohani 0ead2a7f36 Merge branch 'master' of https://github.com/appwrite/appwrite into feat-project-level-secrets 2022-09-19 12:54:51 +00:00
Damodar Lohani 4cbef8614e Merge branch 'master' of https://github.com/appwrite/appwrite into feat-project-level-secrets 2022-07-15 01:24:27 +00:00
Damodar Lohani d7a94cc4c6 Merge branch 'feat-project-level-secrets' of https://github.com/appwrite/appwrite into feat-project-level-secrets 2022-07-14 04:53:03 +00:00
Damodar Lohani 3b62798667 Merge branch 'master' of https://github.com/appwrite/appwrite into feat-project-level-secrets 2022-07-14 04:52:42 +00:00
Damodar Lohani adc8be01d0 fix realtime 2022-06-30 01:33:48 +00:00
Damodar Lohani 951a553344 composer update 2022-06-30 01:29:51 +00:00
Damodar Lohani 6c738b0dcc unused import 2022-06-29 06:06:43 +00:00
Damodar Lohani 6248225949 linter fixes 2022-06-29 06:02:20 +00:00
Damodar Lohani fab93f755a fix linters 2022-06-29 06:00:44 +00:00
Damodar Lohani 7bd4e08b68 Merge branch 'master' of https://github.com/appwrite/appwrite into feat-pls-merge 2022-06-29 05:52:18 +00:00
Damodar Lohani 77b38e3a5e Merge branch '0.15.x' of https://github.com/appwrite/appwrite into feat-sync-0.15 2022-06-03 05:59:44 +00:00
Damodar Lohani 7de544957d fix realtime issues 2022-06-02 04:15:46 +00:00
Damodar Lohani f403b8235a secret generation extracted to open SSL 2022-05-23 06:44:18 +00:00
Damodar Lohani 58d00055d9 refactor jwt secrets 2022-05-23 06:33:59 +00:00
Damodar Lohani bcc0e0df18 fix attribute name 2022-05-23 06:12:06 +00:00
Damodar Lohani 43332fbc05 Merge branch 'master' of https://github.com/appwrite/appwrite into feat-project-level-secrets 2022-05-23 02:09:02 +00:00
8f22a72e41 Update app/config/collections.php
Co-authored-by: Eldad A. Fux <eldad.fux@gmail.com>
2022-05-23 07:26:05 +05:45
Damodar Lohani f2482ab88d fix tests on 2022-05-11 14:13:55 +00:00
Damodar Lohani c0a616c009 fix tests 2022-05-08 09:16:39 +00:00
Damodar Lohani 8718437304 open ssl version is no longer required 2022-05-08 08:30:01 +00:00
Damodar Lohani 44f81d9453 refactor 2022-05-08 08:27:55 +00:00
Damodar Lohani 6000177291 add json filter 2022-05-08 08:18:23 +00:00
Damodar Lohani c90986068d update projects collection 2022-05-08 08:17:02 +00:00
Damodar Lohani acad5815b5 fix functions and realtime 2022-05-08 08:03:33 +00:00
Damodar Lohani afa02062a3 refactor encode and decode function 2022-05-08 06:27:57 +00:00
Damodar Lohani c4a722620f composer update 2022-05-08 04:53:07 +00:00
Damodar Lohani 3d43a9759a Merge branch 'master' of https://github.com/appwrite/appwrite into feat-project-level-secrets 2022-05-08 04:38:20 +00:00
Damodar Lohani ea3544579b storage encryption new key per file 2022-05-06 10:20:33 +00:00
Damodar Lohani aa92551369 update secrets data type 2022-05-06 09:59:16 +00:00
Damodar Lohani 87f8188665 update project level encryption decryption filter 2022-05-06 09:51:21 +00:00
Damodar Lohani 967c751926 encrypt jwt using jwt secret 2022-03-30 08:01:01 +00:00
Damodar Lohani 369a42c3d0 graceful when project not found 2022-03-30 07:30:33 +00:00
Damodar Lohani 6286bcaf46 fix for console 2022-03-30 06:51:59 +00:00
Damodar Lohani 77e95466e5 getting empty project - during realtime test 2022-03-30 06:50:01 +00:00
Damodar Lohani 60ab3a801b increase secret column size 2022-03-30 06:31:05 +00:00
Damodar Lohani feee2bf473 make it work when project doesn't exist 2022-03-29 12:46:32 +00:00
Damodar Lohani 057d56621f convert bin to hex 2022-03-29 12:20:48 +00:00
Damodar Lohani 33100df180 fix secret for version 2022-03-29 11:46:54 +00:00
Damodar Lohani 2c00148431 fix error 2022-03-29 10:01:37 +00:00
Damodar Lohani d4897b2eea update composer 2022-03-29 09:08:48 +00:00
Damodar Lohani e9b553fa85 set instance filters in workers 2022-03-29 08:59:45 +00:00
Damodar Lohani c923e2e41b filters in realtime 2022-03-29 08:56:39 +00:00
Damodar Lohani 89d4c82d58 set proper instance level filters in realtime 2022-03-28 11:19:24 +00:00
Damodar Lohani c09bf14d29 remove unnecessary check 2022-03-28 11:04:41 +00:00
Damodar Lohani 7ca0696aee fix missing code 2022-03-28 10:05:10 +00:00
Damodar Lohani c4e3de8bb3 using dev database 2022-03-28 08:52:38 +00:00
Damodar Lohani 5750f0927b remove comments 2022-03-28 08:52:32 +00:00
Damodar Lohani 7489ec40de encrypt files with storage secrets as well 2022-03-28 08:52:15 +00:00
Damodar Lohani 5f052645ec set instance level filters to use database secrets 2022-03-28 08:35:20 +00:00
Damodar Lohani 3d7f48ea0c generate keys during project creation 2022-03-27 04:46:16 +00:00
Damodar Lohani 8898cfa58e project level secrets data structure 2022-03-27 04:33:57 +00:00
9 changed files with 225 additions and 46 deletions
+25 -3
View File
@@ -633,6 +633,28 @@ $collections = [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('databaseSecrets'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => [],
'array' => false,
'filters' => ['json', 'encrypt'],
],
[
'$id' => ID::custom('jwtSecrets'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => [],
'array' => false,
'filters' => ['encrypt'],
],
[
'$id' => ID::custom('services'),
'type' => Database::VAR_STRING,
@@ -3301,15 +3323,15 @@ $collections = [
'filters' => [],
],
[
'$id' => ID::custom('openSSLVersion'),
'$id' => ID::custom('fileSecret'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 64,
'size' => 512,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
'filters' => ['encrypt'],
],
[
'$id' => ID::custom('openSSLCipher'),
+3 -3
View File
@@ -1239,9 +1239,9 @@ App::post('/v1/account/jwt')
->label('abuse-key', 'url:{url},userId:{userId}')
->inject('response')
->inject('user')
->inject('project')
->inject('dbForProject')
->action(function (Response $response, Document $user, Database $dbForProject) {
->action(function (Response $response, Document $user, Document $project, Database $dbForProject) {
$sessions = $user->getAttribute('sessions', []);
$current = new Document();
@@ -1256,7 +1256,7 @@ App::post('/v1/account/jwt')
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
}
$jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = new JWT($project->getAttribute('jwtSecrets'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
+1 -1
View File
@@ -1034,7 +1034,7 @@ App::post('/v1/functions/:functionId/executions')
}
if (!$current->isEmpty()) {
$jwtObj = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwtObj = new JWT($project->getAttribute('jwtSecrets'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = $jwtObj->encode([
'userId' => $user->getId(),
'sessionId' => $current->getId(),
+4
View File
@@ -28,6 +28,7 @@ use Utopia\Database\Validator\UID;
use Utopia\Domains\Domain;
use Utopia\Registry\Registry;
use Appwrite\Extend\Exception;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
@@ -119,6 +120,8 @@ App::post('/v1/projects')
'keys' => null,
'domains' => null,
'auths' => $auths,
'databaseSecrets' => [\uniqid() => OpenSSL::secretString()],
'jwtSecrets' => OpenSSL::secretString(),
'search' => implode(' ', [$projectId, $name]),
]));
/** @var array $collections */
@@ -864,6 +867,7 @@ App::post('/v1/projects/:projectId/keys')
'name' => $name,
'scopes' => $scopes,
'expire' => $expire,
'secret' => Auth::tokenGenerator(),
'sdks' => [],
'accessedAt' => null,
'secret' => \bin2hex(\random_bytes(128)),
+22 -21
View File
@@ -528,9 +528,16 @@ App::post('/v1/storage/buckets/:bucketId/files')
if (empty($data)) {
$data = $deviceFiles->read($path);
}
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
$fileSecret = OpenSSL::secretString();
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
$tag = null;
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $fileSecret, 0, $iv, $tag);
$openSSLCipher = OpenSSL::CIPHER_AES_128_GCM;
$openSSLTag = \bin2hex($tag ?? '');
$openSSLIV = \bin2hex($iv);
}
if (!empty($data)) {
@@ -542,13 +549,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
$sizeActual = $deviceFiles->getFileSize($path);
$fileHash = $deviceFiles->getFileHash($path);
if ($bucket->getAttribute('encryption', true) && $fileSize <= APP_STORAGE_READ_BUFFER) {
$openSSLVersion = '1';
$openSSLCipher = OpenSSL::CIPHER_AES_128_GCM;
$openSSLTag = \bin2hex($tag);
$openSSLIV = \bin2hex($iv);
}
try {
if ($file->isEmpty()) {
$doc = new Document([
@@ -565,12 +565,12 @@ App::post('/v1/storage/buckets/:bucketId/files')
'comment' => '',
'chunksTotal' => $chunks,
'chunksUploaded' => $chunksUploaded,
'openSSLVersion' => $openSSLVersion,
'openSSLCipher' => $openSSLCipher,
'openSSLTag' => $openSSLTag,
'openSSLIV' => $openSSLIV,
'openSSLCipher' => $openSSLCipher ?? null,
'openSSLTag' => $openSSLTag ?? null,
'openSSLIV' => $openSSLIV ?? null,
'search' => implode(' ', [$fileId, $fileName]),
'metadata' => $metadata,
'fileSecret' => $fileSecret ?? null,
]);
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
@@ -581,12 +581,12 @@ App::post('/v1/storage/buckets/:bucketId/files')
->setAttribute('mimeType', $mimeType)
->setAttribute('sizeActual', $sizeActual)
->setAttribute('algorithm', $algorithm)
->setAttribute('openSSLVersion', $openSSLVersion)
->setAttribute('openSSLCipher', $openSSLCipher)
->setAttribute('openSSLTag', $openSSLTag)
->setAttribute('openSSLIV', $openSSLIV)
->setAttribute('openSSLCipher', $openSSLCipher ?? null)
->setAttribute('openSSLTag', $openSSLTag ?? null)
->setAttribute('openSSLIV', $openSSLIV ?? null)
->setAttribute('metadata', $metadata)
->setAttribute('chunksUploaded', $chunksUploaded);
->setAttribute('chunksUploaded', $chunksUploaded)
->setAttribute('fileSecret', $fileSecret ?? null);
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
}
@@ -616,6 +616,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
'chunksUploaded' => $chunksUploaded,
'search' => implode(' ', [$fileId, $fileName]),
'metadata' => $metadata,
'fileSecret' => $fileSecret ?? null,
]);
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
@@ -890,7 +891,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
$file->getAttribute('fileSecret'),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
@@ -1033,7 +1034,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
$file->getAttribute('fileSecret'),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
@@ -1183,7 +1184,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
$file->getAttribute('fileSecret'),
0,
\hex2bin($file->getAttribute('openSSLIV')),
\hex2bin($file->getAttribute('openSSLTag'))
+63 -8
View File
@@ -425,7 +425,7 @@ Database::addFilter(
'method' => OpenSSL::CIPHER_AES_128_GCM,
'iv' => \bin2hex($iv),
'tag' => \bin2hex($tag ?? ''),
'version' => '1',
'version' => 'v1',
]);
},
function (mixed $value) {
@@ -853,7 +853,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
$jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = new JWT($project->getAttribute('jwtSecrets'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
try {
$payload = $jwt->decode($authJWT);
@@ -917,6 +917,8 @@ App::setResource('console', function () {
'legalCity' => '',
'legalAddress' => '',
'legalTaxId' => '',
'databaseSecrets' => ['v1' => App::getEnv('_APP_OPENSSL_KEY_V1')],
'jwtSecrets' => App::getEnv('_APP_OPENSSL_KEY_V1'),
'auths' => [
'limit' => (App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
],
@@ -925,25 +927,78 @@ App::setResource('console', function () {
]);
}, []);
App::setResource('dbForProject', function ($db, $cache, Document $project) {
$cache = new Cache(new RedisCache($cache));
function encode(mixed $value, array $secrets): string
{
$version = array_key_last($secrets);
$key = $secrets[$version];
$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' => $version,
]);
}
$database = new Database(new MariaDB($db), $cache);
function decode(?string $value, array $secrets): mixed
{
if (is_null($value)) {
return null;
}
$value = json_decode($value, true);
$version = $value['version'];
$key = $secrets[$version];
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
}
App::setResource('dbForProject', function ($db, $cache, $project) {
$filters = [];
if (!$project->isEmpty()) {
$secrets = $project->getAttribute('databaseSecrets');
$filters['encrypt'] = [
'encode' => function ($value) use ($secrets) {
return encode($value, $secrets);
},
'decode' => function ($value) use ($secrets) {
return decode($value, $secrets);
}
];
}
$cache = new Cache(new RedisCache($cache));
$database = new Database(new MariaDB($db), $cache, $filters);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace("_{$project->getInternalId()}");
return $database;
}, ['db', 'cache', 'project']);
App::setResource('dbForConsole', function ($db, $cache) {
App::setResource('dbForConsole', function ($db, $cache, $console) {
$filters = [];
if (!$console->isEmpty()) {
$secrets = $console->getAttribute('databaseSecrets');
$filters['encrypt'] = [
'encode' => function ($value) use ($secrets) {
return encode($value, $secrets);
},
'decode' => function ($value) use ($secrets) {
return decode($value, $secrets);
}
];
}
$cache = new Cache(new RedisCache($cache));
$database = new Database(new MariaDB($db), $cache);
$database = new Database(new MariaDB($db), $cache, $filters);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace('_console');
return $database;
}, ['db', 'cache']);
}, ['db', 'cache', 'console']);
App::setResource('deviceLocal', function () {
+53 -7
View File
@@ -3,6 +3,7 @@
use Appwrite\Auth\Auth;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Network\Validator\Origin;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Utopia\Response;
use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
@@ -95,7 +96,41 @@ $logError = function (Throwable $error, string $action) use ($register) {
$server->error($logError);
function getDatabase(Registry &$register, string $namespace)
function getFilters(Document $project): array
{
if (!$project->isEmpty()) {
$secrets = $project->getAttribute('databaseSecrets');
$filters['encrypt'] = [
'encode' => function ($value) use ($secrets) {
$version = array_key_last($secrets);
$key = $secrets[$version];
$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' => $version,
]);
},
'decode' => function ($value) use ($secrets) {
if (is_null($value)) {
return null;
}
$value = json_decode($value, true);
$version = $value['version'];
$key = $secrets[$version];
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
}
];
}
return $filters;
}
function getDatabase(Registry &$register, string $namespace, ?Document $project = null)
{
$attempts = 0;
@@ -115,6 +150,13 @@ function getDatabase(Registry &$register, string $namespace)
throw new Exception('Collection not ready');
}
if (!is_null($project)) {
$filters = getFilters($project);
$database = new Database(new MariaDB($db), $cache, $filters);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace($namespace);
}
break; // leave loop if successful
} catch (\Throwable $e) {
Console::warning("Database not ready. Retrying connection ({$attempts})...");
@@ -307,7 +349,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId]));
[$consoleDatabase, $returnConsoleDatabase] = getDatabase($register, '_console');
$project = Authorization::skip(fn() => $consoleDatabase->getDocument('projects', $projectId));
[$database, $returnDatabase] = getDatabase($register, "_{$project->getInternalId()}");
[$database, $returnDatabase] = getDatabase($register, "_{$project->getInternalId()}", $project);
$user = $database->getDocument('users', $userId);
@@ -491,13 +533,17 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
$cache = new Cache(new RedisCache($redis));
$database = new Database(new MariaDB($db), $cache);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace("_console");
$projectId = $realtime->connections[$connection]['projectId'];
if ($projectId !== 'console') {
$project = Authorization::skip(fn() => $database->getDocument('projects', $projectId));
$database->setNamespace("_{$project->getInternalId()}");
$projectId = $realtime->connections[$connection]['projectId'];
$database->setNamespace("_console");
$project = Authorization::skip(fn() => $database->getDocument('projects', $projectId));
if ($projectId != 'console') {
$filters = getFilters($project);
$database = new Database(new MariaDB($db), $cache, $filters);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
}
$database->setNamespace("_{$project->getInternalId()}");
/*
* Abuse Check
+16
View File
@@ -59,4 +59,20 @@ class OpenSSL
{
return \openssl_random_pseudo_bytes($length, $crypto_strong);
}
/**
* Secret String
*
* Generate random encryption secret
*
* @param int $length
*
* @return string
*
* @throws \Exception
*/
public static function secretString(int $length = 128): string
{
return \bin2hex(self::randomPseudoBytes($length));
}
}
+38 -3
View File
@@ -2,6 +2,7 @@
namespace Appwrite\Resque;
use Appwrite\OpenSSL\OpenSSL;
use Utopia\App;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Redis as RedisCache;
@@ -223,12 +224,46 @@ abstract class Worker
$cache = new Cache(new RedisCache($register->get('cache')));
$database = new Database(new MariaDB($register->get('db')), $cache);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace($namespace); // Main DB
if (!empty($projectId) && !$database->getDocument('projects', $projectId)->isEmpty()) {
throw new \Exception("Project does not exist: {$projectId}");
if (!empty($projectId)) {
$database->setNamespace("_console");
$project = $database->getDocument('projects', $projectId);
if (!$project->isEmpty()) {
$secrets = $project->getAttribute('databaseSecrets');
$filters['encrypt'] = [
'encode' => function ($value) use ($secrets) {
$version = array_key_last($secrets);
$key = $secrets[$version];
$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' => $version,
]);
},
'decode' => function ($value) use ($secrets) {
if (is_null($value)) {
return null;
}
$value = json_decode($value, true);
$version = $value['version'];
$key = $secrets[$version];
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
}
];
$database = new Database(new MariaDB($register->get('db')), $cache, $filters);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
}
}
$database->setNamespace($namespace); // Main DB
if ($type === self::DATABASE_CONSOLE && !$database->exists($database->getDefaultDatabase(), '_metadata')) {
throw new \Exception('Console project not ready');
}