2021-11-04 16:28:41 +01:00
|
|
|
<?php
|
|
|
|
App::uses('AppModel', 'Model');
|
|
|
|
App::uses('JsonTool', 'Tools');
|
2021-11-05 16:50:19 +01:00
|
|
|
App::uses('BetterSecurity', 'Tools');
|
2021-11-04 16:28:41 +01:00
|
|
|
|
2021-11-05 10:49:16 +01:00
|
|
|
/**
|
|
|
|
* Class for ondemand encryption of JSON serialized value
|
|
|
|
*/
|
|
|
|
class EncryptedValue implements JsonSerializable
|
|
|
|
{
|
|
|
|
const ENCRYPTED_MAGIC = "\x1F\x1D";
|
|
|
|
|
|
|
|
/** @var string */
|
|
|
|
private $value;
|
|
|
|
|
|
|
|
public function __construct($value)
|
|
|
|
{
|
|
|
|
$this->value = $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
* @throws JsonException
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
public function decrypt()
|
|
|
|
{
|
2021-11-05 16:50:19 +01:00
|
|
|
$decrypt = BetterSecurity::decrypt($this->value, Configure::read('Security.encryption_key'));
|
2021-11-05 10:49:16 +01:00
|
|
|
return JsonTool::decode($decrypt);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function __toString()
|
|
|
|
{
|
|
|
|
return $this->decrypt();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function jsonSerialize()
|
|
|
|
{
|
|
|
|
return $this->decrypt();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-04 16:28:41 +01:00
|
|
|
class SystemSetting extends AppModel
|
|
|
|
{
|
|
|
|
public $actsAs = [
|
|
|
|
'SysLogLogable.SysLogLogable' => [
|
|
|
|
'userModel' => 'User',
|
|
|
|
'userKey' => 'user_id',
|
|
|
|
'change' => 'full'
|
|
|
|
],
|
|
|
|
'AuditLog'
|
|
|
|
];
|
|
|
|
|
|
|
|
public $primaryKey = 'setting';
|
|
|
|
|
2021-11-05 10:49:16 +01:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
2021-11-04 16:28:41 +01:00
|
|
|
public function getSettings()
|
|
|
|
{
|
|
|
|
$settings = $this->find('list', [
|
|
|
|
'fields' => ['SystemSetting.setting', 'SystemSetting.value'],
|
|
|
|
]);
|
2021-11-05 10:49:16 +01:00
|
|
|
return array_map([$this, 'decode'], $settings);
|
2021-11-04 16:28:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-11-05 10:49:16 +01:00
|
|
|
* @param string $setting Setting name
|
2021-11-04 16:28:41 +01:00
|
|
|
* @param mixed $value
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
public function setSetting($setting, $value)
|
|
|
|
{
|
2021-11-05 10:49:16 +01:00
|
|
|
$value = JsonTool::encode($value);
|
|
|
|
|
|
|
|
// If encryption is enabled and setting name contains `password` or `apikey` string, encrypt value to protect it
|
|
|
|
$key = Configure::read('Security.encryption_key');
|
2021-11-05 21:30:17 +01:00
|
|
|
if ($key && self::isSensitive($setting)) {
|
2021-11-05 16:50:19 +01:00
|
|
|
$value = EncryptedValue::ENCRYPTED_MAGIC . BetterSecurity::encrypt($value, $key);
|
2021-11-05 10:49:16 +01:00
|
|
|
}
|
|
|
|
|
2021-11-04 16:28:41 +01:00
|
|
|
$valid = $this->save(['SystemSetting' => [
|
|
|
|
'setting' => $setting,
|
2021-11-05 10:49:16 +01:00
|
|
|
'value' => $value,
|
2021-11-04 16:28:41 +01:00
|
|
|
]]);
|
|
|
|
if (!$valid) {
|
|
|
|
throw new Exception("Could not save system setting `$setting` because of validation errors: " . JsonTool::encode($this->validationErrors));
|
|
|
|
}
|
|
|
|
}
|
2021-11-05 10:49:16 +01:00
|
|
|
|
2021-11-05 21:51:31 +01:00
|
|
|
/**
|
|
|
|
* @param string|null $old Old (or current) encryption key.
|
|
|
|
* @param string|null $new New encryption key. If empty, encrypted values will be decrypted.
|
|
|
|
* @throws JsonException
|
|
|
|
*/
|
|
|
|
public function reencrypt($old, $new)
|
|
|
|
{
|
|
|
|
$settings = $this->find('list', [
|
|
|
|
'fields' => ['SystemSetting.setting', 'SystemSetting.value'],
|
|
|
|
]);
|
|
|
|
$toSave = [];
|
|
|
|
foreach ($settings as $setting => $value) {
|
|
|
|
if (!self::isSensitive($setting)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (substr($value, 0, 2) === EncryptedValue::ENCRYPTED_MAGIC) {
|
|
|
|
$value = BetterSecurity::decrypt(substr($value, 2), $old);
|
|
|
|
}
|
|
|
|
if (!empty($new)) {
|
|
|
|
$value = EncryptedValue::ENCRYPTED_MAGIC . BetterSecurity::encrypt($value, $new);
|
|
|
|
}
|
|
|
|
$toSave[] = ['SystemSetting' => [
|
|
|
|
'setting' => $setting,
|
|
|
|
'value' => $value,
|
|
|
|
]];
|
|
|
|
}
|
|
|
|
return $this->saveMany($toSave);
|
|
|
|
}
|
|
|
|
|
2021-11-05 10:49:16 +01:00
|
|
|
/**
|
|
|
|
* @param string $value
|
|
|
|
* @return EncryptedValue|mixed
|
|
|
|
* @throws JsonException
|
|
|
|
*/
|
|
|
|
private function decode($value)
|
|
|
|
{
|
|
|
|
if (substr($value, 0, 2) === EncryptedValue::ENCRYPTED_MAGIC) {
|
|
|
|
return new EncryptedValue(substr($value, 2));
|
|
|
|
} else {
|
|
|
|
return JsonTool::decode($value);
|
|
|
|
}
|
|
|
|
}
|
2021-11-05 21:30:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $setting
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function isSensitive($setting)
|
|
|
|
{
|
|
|
|
return strpos($setting, 'password') !== false || strpos($setting, 'apikey') !== false;
|
|
|
|
}
|
2021-11-04 16:28:41 +01:00
|
|
|
}
|