Added built-in DB encryption
This commit is contained in:
parent
03ec10a0b8
commit
8b878c87ff
|
@ -1212,6 +1212,15 @@ $collections = [
|
|||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'Status',
|
||||
'key' => 'status',
|
||||
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'Name',
|
||||
|
@ -1238,15 +1247,7 @@ $collections = [
|
|||
'default' => '',
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'Trigger',
|
||||
'key' => 'trigger',
|
||||
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
'filter' => ['json', 'encrypt']
|
||||
],
|
||||
[
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
|
@ -1266,6 +1267,24 @@ $collections = [
|
|||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'Previous',
|
||||
'key' => 'previous',
|
||||
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'Next',
|
||||
'key' => 'next',
|
||||
'type' => Database::SYSTEM_VAR_TYPE_NUMERIC,
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$collection' => Database::SYSTEM_COLLECTION_RULES,
|
||||
'label' => 'Timeout',
|
||||
|
|
42
app/init.php
42
app/init.php
|
@ -23,6 +23,7 @@ use Appwrite\Database\Document;
|
|||
use Appwrite\Database\Validator\Authorization;
|
||||
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
|
||||
use Appwrite\Database\Adapter\Redis as RedisAdapter;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
|
||||
const APP_NAME = 'Appwrite';
|
||||
|
@ -32,7 +33,7 @@ const APP_EMAIL_SECURITY = 'security@localhost.test'; // Default security email
|
|||
const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s';
|
||||
const APP_MODE_ADMIN = 'admin';
|
||||
const APP_PAGING_LIMIT = 15;
|
||||
const APP_CACHE_BUSTER = 126;
|
||||
const APP_CACHE_BUSTER = 130;
|
||||
const APP_VERSION_STABLE = '0.5.3';
|
||||
const APP_STORAGE_UPLOADS = '/storage/uploads';
|
||||
const APP_STORAGE_CACHE = '/storage/cache';
|
||||
|
@ -229,6 +230,43 @@ stream_context_set_default([ // Set global user agent and http settings
|
|||
],
|
||||
]);
|
||||
|
||||
/**
|
||||
* DB Filters
|
||||
*/
|
||||
Database::addFilter('json',
|
||||
function($value) {
|
||||
if(!is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return json_encode($value);
|
||||
},
|
||||
function($value) {
|
||||
return json_decode($value, true);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('encrypt',
|
||||
function($value) use ($request) {
|
||||
$key = $request->getServer('_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',
|
||||
]);
|
||||
},
|
||||
function($value) use ($request) {
|
||||
$value = json_decode($value, true);
|
||||
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$value['version']);
|
||||
|
||||
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
* Auth & Project Scope
|
||||
*/
|
||||
|
@ -308,4 +346,4 @@ $register->get('smtp')
|
|||
? urldecode($request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'))
|
||||
: sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name')
|
||||
)
|
||||
);
|
||||
);
|
|
@ -51,6 +51,11 @@ class Database
|
|||
const SYSTEM_VAR_TYPE_URL = 'url';
|
||||
const SYSTEM_VAR_TYPE_KEY = 'key';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
static protected $filters = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
@ -174,7 +179,7 @@ class Database
|
|||
*
|
||||
* @return Document
|
||||
*/
|
||||
public function getDocument($id, $mock = true)
|
||||
public function getDocument($id, $mock = true, $decode = true)
|
||||
{
|
||||
if (is_null($id)) {
|
||||
return new Document([]);
|
||||
|
@ -187,6 +192,8 @@ class Database
|
|||
return new Document([]);
|
||||
}
|
||||
|
||||
$document = ($decode) ? $this->decode($document) : $document;
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
|
@ -209,12 +216,18 @@ class Database
|
|||
}
|
||||
|
||||
$validator = new Structure($this);
|
||||
|
||||
$document = $this->encode($document);
|
||||
|
||||
if (!$validator->isValid($document)) {
|
||||
throw new StructureException($validator->getDescription()); // var_dump($validator->getDescription()); return false;
|
||||
}
|
||||
|
||||
$document = new Document($this->adapter->createDocument($document->getArrayCopy(), $unique));
|
||||
|
||||
$document = $this->decode($document);
|
||||
|
||||
return new Document($this->adapter->createDocument($data, $unique));
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,13 +261,19 @@ class Database
|
|||
throw new AuthorizationException($validator->getDescription()); // var_dump($validator->getDescription()); return false;
|
||||
}
|
||||
|
||||
$new = $this->encode($new);
|
||||
|
||||
$validator = new Structure($this);
|
||||
|
||||
if (!$validator->isValid($new)) { // Make sure updated structure still apply collection rules (if any)
|
||||
throw new StructureException($validator->getDescription()); // var_dump($validator->getDescription()); return false;
|
||||
}
|
||||
|
||||
return new Document($this->adapter->updateDocument($data));
|
||||
$new = new Document($this->adapter->updateDocument($new->getArrayCopy()));
|
||||
|
||||
$new = $this->decode($new);
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -380,6 +399,107 @@ class Database
|
|||
return $this->mocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Attribute Filter
|
||||
*
|
||||
* @param string $name
|
||||
* @param callable $encode
|
||||
* @param callable $decode
|
||||
*
|
||||
* return $this
|
||||
*/
|
||||
static public function addFilter(string $name, callable $encode, callable $decode)
|
||||
{
|
||||
self::$filters[$name] = [
|
||||
'encode' => $encode,
|
||||
'decode' => $decode,
|
||||
];
|
||||
}
|
||||
|
||||
public function encode(Document $document):Document
|
||||
{
|
||||
$collection = $this->getDocument($document->getCollection(), true , false);
|
||||
$rules = $collection->getAttribute('rules', []);
|
||||
|
||||
foreach ($rules as $key => $rule) {
|
||||
$key = $rule->getAttribute('key', null);
|
||||
$filters = $rule->getAttribute('filter', null);
|
||||
$value = $document->getAttribute($key, null);
|
||||
|
||||
if(($value !== null) && is_array($filters)) {
|
||||
foreach ($filters as $filter) {
|
||||
$value = $this->encodeAttribute($filter, $value);
|
||||
$document->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
public function decode(Document $document):Document
|
||||
{
|
||||
$collection = $this->getDocument($document->getCollection(), true , false);
|
||||
$rules = $collection->getAttribute('rules', []);
|
||||
|
||||
foreach ($rules as $key => $rule) {
|
||||
$key = $rule->getAttribute('key', null);
|
||||
$filters = $rule->getAttribute('filter', null);
|
||||
$value = $document->getAttribute($key, null);
|
||||
|
||||
if(($value !== null) && is_array($filters)) {
|
||||
foreach (array_reverse($filters) as $filter) {
|
||||
$value = $this->decodeAttribute($filter, $value);
|
||||
$document->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode Attribute
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
static protected function encodeAttribute(string $name, $value)
|
||||
{
|
||||
if(!isset(self::$filters[$name])) {
|
||||
throw new Exception('Filter not found');
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::$filters[$name]['encode']($value);
|
||||
} catch (\Throwable $th) {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode Attribute
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
static protected function decodeAttribute(string $name, $value)
|
||||
{
|
||||
if(!isset(self::$filters[$name])) {
|
||||
throw new Exception('Filter not found');
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::$filters[$name]['decode']($value);
|
||||
} catch (\Throwable $th) {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Last Modified.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue