mirror of
https://github.com/Bubka/2FAuth.git
synced 2024-11-23 00:33:18 +01:00
Refactor db encryption feature
This commit is contained in:
parent
776b2b5093
commit
531cd74758
@ -18,20 +18,13 @@ class SettingController extends Controller
|
||||
*/
|
||||
protected SettingServiceInterface $settingService;
|
||||
|
||||
/**
|
||||
* The Settings Service instance.
|
||||
*/
|
||||
protected DbEncryptionService $dbEncryptionService;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
*/
|
||||
public function __construct(SettingServiceInterface $SettingServiceInterface, DbEncryptionService $dbEncryptionService)
|
||||
public function __construct(SettingServiceInterface $SettingServiceInterface)
|
||||
{
|
||||
$this->settingService = $SettingServiceInterface;
|
||||
$this->dbEncryptionService = $dbEncryptionService;
|
||||
}
|
||||
|
||||
|
||||
@ -106,20 +99,7 @@ public function update(SettingUpdateRequest $request, $settingName)
|
||||
{
|
||||
$validated = $request->validated();
|
||||
|
||||
// The useEncryption setting impacts records in DB so we delegate the work to the
|
||||
// dedicated db encryption service
|
||||
if( $settingName === 'useEncryption')
|
||||
{
|
||||
try {
|
||||
$this->dbEncryptionService->setTo($validated['value']);
|
||||
}
|
||||
catch(DbEncryptionException $ex) {
|
||||
return response()->json([
|
||||
'message' => $ex->getMessage()
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
else $this->settingService->set($settingName, $validated['value']);
|
||||
$this->settingService->set($settingName, $validated['value']);
|
||||
|
||||
return response()->json([
|
||||
'key' => $settingName,
|
||||
|
@ -71,6 +71,10 @@ public function render($request, Throwable $exception)
|
||||
return response()->json([
|
||||
'message' => 'not a valid base32 encoded secret'], 400);
|
||||
}
|
||||
if ($exception instanceof DbEncryptionException) {
|
||||
return response()->json([
|
||||
'message' => $exception->getMessage()], 400);
|
||||
}
|
||||
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
@ -2,12 +2,17 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Throwable;
|
||||
use Exception;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use App\Exceptions\DbEncryptionException;
|
||||
|
||||
class AppstractOptionsService implements SettingServiceInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@ -44,6 +49,11 @@ public function set($setting, $value = null) : void
|
||||
$settings = is_array($setting) ? $setting : [$setting => $value];
|
||||
|
||||
foreach ($settings as $setting => $value) {
|
||||
if( $setting === 'useEncryption')
|
||||
{
|
||||
$this->setEncryptionTo($value);
|
||||
}
|
||||
|
||||
$settings[$setting] = $this->replaceBoolean($value);
|
||||
}
|
||||
|
||||
@ -98,4 +108,84 @@ private function restoreType($value)
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable or Disable encryption of 2FAccounts sensible data
|
||||
*
|
||||
* @return void
|
||||
* @throws DbEncryptionException Something failed, everything have been rolled back
|
||||
*/
|
||||
private function setEncryptionTo(bool $state) : void
|
||||
{
|
||||
// We don't want the records to be encrypted/decrypted multiple successive times
|
||||
$isInUse = $this->get('useEncryption');
|
||||
|
||||
if ($isInUse === !$state) {
|
||||
if ($this->updateRecords($state)) {
|
||||
if ($state) {
|
||||
Log::notice('Sensible data are now encrypted');
|
||||
}
|
||||
else Log::notice('Sensible data are now decrypted');
|
||||
}
|
||||
else {
|
||||
Log::warning('Some data cannot be encrypted/decrypted, the useEncryption setting remain unchanged');
|
||||
throw new DbEncryptionException($state === true ? __('errors.error_during_encryption') : __('errors.error_during_decryption'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt/Decrypt accounts in database
|
||||
*
|
||||
* @param boolean $encrypted Whether the record should be encrypted or not
|
||||
* @return boolean Whether the operation completed successfully
|
||||
*/
|
||||
private function updateRecords(bool $encrypted) : bool
|
||||
{
|
||||
$success = true;
|
||||
$twofaccounts = DB::table('twofaccounts')->get();
|
||||
|
||||
$twofaccounts->each(function ($item, $key) use(&$success, $encrypted) {
|
||||
try {
|
||||
$item->legacy_uri = $encrypted ? Crypt::encryptString($item->legacy_uri) : Crypt::decryptString($item->legacy_uri);
|
||||
$item->account = $encrypted ? Crypt::encryptString($item->account) : Crypt::decryptString($item->account);
|
||||
$item->secret = $encrypted ? Crypt::encryptString($item->secret) : Crypt::decryptString($item->secret);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$success = false;
|
||||
// Exit the each iteration
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if ($success) {
|
||||
// The whole collection has now its sensible data encrypted/decrypted
|
||||
// We update the db using a transaction that can rollback everything if an error occured
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$twofaccounts->each(function ($item, $key) {
|
||||
DB::table('twofaccounts')
|
||||
->where('id', $item->id)
|
||||
->update([
|
||||
'legacy_uri' => $item->legacy_uri,
|
||||
'account' => $item->account,
|
||||
'secret' => $item->secret
|
||||
]);
|
||||
});
|
||||
|
||||
DB::commit();
|
||||
return true;
|
||||
}
|
||||
// @codeCoverageIgnoreStart
|
||||
catch (Throwable $ex) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Throwable;
|
||||
use Exception;
|
||||
use App\Exceptions\DbEncryptionException;
|
||||
use App\Services\SettingServiceInterface;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DbEncryptionService
|
||||
{
|
||||
|
||||
/**
|
||||
* The Settings Service instance.
|
||||
*/
|
||||
protected SettingServiceInterface $settingService;
|
||||
|
||||
|
||||
/**
|
||||
* Settings service constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(SettingServiceInterface $SettingServiceInterface)
|
||||
{
|
||||
$this->settingService = $SettingServiceInterface;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable or Disable encryption of 2FAccounts sensible data
|
||||
*
|
||||
* @return void
|
||||
* @throws DbEncryptionException Something failed, everything have been rolledback
|
||||
*/
|
||||
public function setTo(bool $state) : void
|
||||
{
|
||||
// We don't want the records to be encrypted/decrypted multiple successive times
|
||||
$isInUse = $this->settingService->get('useEncryption');
|
||||
|
||||
if ($isInUse === !$state) {
|
||||
if ($this->updateRecords($state)) {
|
||||
$this->settingService->set('useEncryption', $state);
|
||||
|
||||
if ($state) {
|
||||
Log::notice('Sensible data are now encrypted');
|
||||
}
|
||||
else Log::notice('Sensible data are now decrypted');
|
||||
}
|
||||
else {
|
||||
Log::warning('Some data cannot be encrypted/decrypted, the useEncryption setting remain unchanged');
|
||||
throw new DbEncryptionException($state === true ? __('errors.error_during_encryption') : __('errors.error_during_decryption'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt/Decrypt accounts in database
|
||||
*
|
||||
* @param boolean $encrypted Whether the record should be encrypted or not
|
||||
* @return boolean Whether the operation completed successfully
|
||||
*/
|
||||
private function updateRecords(bool $encrypted) : bool
|
||||
{
|
||||
$success = true;
|
||||
$twofaccounts = DB::table('twofaccounts')->get();
|
||||
|
||||
$twofaccounts->each(function ($item, $key) use(&$success, $encrypted) {
|
||||
try {
|
||||
$item->legacy_uri = $encrypted ? Crypt::encryptString($item->legacy_uri) : Crypt::decryptString($item->legacy_uri);
|
||||
$item->account = $encrypted ? Crypt::encryptString($item->account) : Crypt::decryptString($item->account);
|
||||
$item->secret = $encrypted ? Crypt::encryptString($item->secret) : Crypt::decryptString($item->secret);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$success = false;
|
||||
// Exit the each iteration
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if ($success) {
|
||||
// The whole collection has now its sensible data encrypted/decrypted
|
||||
// We update the db using a transaction that can rollback everything if an error occured
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$twofaccounts->each(function ($item, $key) {
|
||||
DB::table('twofaccounts')
|
||||
->where('id', $item->id)
|
||||
->update([
|
||||
'legacy_uri' => $item->legacy_uri,
|
||||
'account' => $item->account,
|
||||
'secret' => $item->secret
|
||||
]);
|
||||
});
|
||||
|
||||
DB::commit();
|
||||
return true;
|
||||
}
|
||||
// @codeCoverageIgnoreStart
|
||||
// Dont now how to fake that :(
|
||||
catch (Throwable $ex) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user