mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-09-13 13:57:34 -04:00
* refactor: standardize function and variable names to camelCase and improve naming consistency across files * refactor(config): remove spaces around `=` in configuration files for improved consistency and formatting as is required by .env formatting rules. * refactor(security): extract `.env` key management logic into reusable `writeEnvKey` helper, add throttle key provisioning logic, and streamline encryption key updates * fix(migration): improve error handling in CI3 to CI4 encryption data migration - Secure `up` and `convertCI3EncryptedData` methods with detailed exception handling for script execution and data saving. * fix(migration): ensure empty string is correctly handled in CI3 to CI4 encryption data conversion * refactor(security): enhance `.env` management with durable writes, better locking, and helper abstraction - Update `writeEnvKey` to return a success flag and handle file locks robustly. - Introduce `atomicWriteFile` for atomic writes to prevent partial file updates. - Add `applyEnvKeyReplacement` to streamline `.env` key insertion and updates. - Improve throttle key provisioning with validation and runtime persistence safeguards. * refactor(security): implement dedicated `.env` file locking for robust and cross-platform safe write operations - Add `lockEnvFile` and `unlockEnvFile` helpers to manage `.env` mutex files. - Refactor `.env` write logic to use lock helpers, improving reliability and preventing race conditions. - Enhance `atomicWriteFile` for better handling of file overwrites on Windows and POSIX systems. * fix(migration): improve encryption error handling during CI3 to CI4 data conversion - Add conditional checks for `checkEncryption` to prevent failed key persistence. - Introduce `abortEncryptionConversion` for cleanup on failure. - Update `writeEnvKey` to handle and return errors gracefully. * refactor(security): improve `atomicWriteFile` for better file locking and cross-platform durability - Replace `uniqid` with `bin2hex(random_bytes())` for more secure temp file naming. - Add explicit file permissions and locking for safe concurrent writes. - Enhance error handling to ensure atomicity on both Windows and POSIX systems. * Add env temp files to gitignore so they don't get tracked. --------- Signed-off-by: objecttothis <17935339+objecttothis@users.noreply.github.com>
148 lines
4.3 KiB
PHP
148 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace App\Database\Migrations;
|
|
|
|
use App\Models\Appconfig;
|
|
use CodeIgniter\Database\Exceptions\DatabaseException;
|
|
use CodeIgniter\Database\Forge;
|
|
use CodeIgniter\Database\Migration;
|
|
use CodeIgniter\HTTP\Exceptions\RedirectException;
|
|
use Config\Encryption;
|
|
use Config\Services;
|
|
use ReflectionException;
|
|
|
|
class ConvertToCI4 extends Migration
|
|
{
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public function __construct(?Forge $forge = null)
|
|
{
|
|
parent::__construct($forge);
|
|
helper('security');
|
|
}
|
|
|
|
/**
|
|
* Perform a migration step.
|
|
*/
|
|
public function up(): void
|
|
{
|
|
helper('migration');
|
|
|
|
if (!executeScript(APPPATH . 'Database/Migrations/sqlscripts/3.4.0_CI4Conversion.sql')) {
|
|
throw new DatabaseException('Migration script 3.4.0_CI4Conversion.sql failed. Check logs for details.');
|
|
}
|
|
|
|
$existingKey = config('Encryption')->key;
|
|
|
|
if (!empty($existingKey) && strlen($existingKey) < 64) {
|
|
$this->convertCI3EncryptedData();
|
|
} else {
|
|
if (!checkEncryption()) {
|
|
abortEncryptionConversion();
|
|
throw new DatabaseException('Failed to persist encryption key. Check logs for details.');
|
|
}
|
|
}
|
|
|
|
removeBackup();
|
|
}
|
|
|
|
/**
|
|
* Revert a migration step.
|
|
*/
|
|
public function down(): void {}
|
|
|
|
/**
|
|
* @throws ReflectionException
|
|
*/
|
|
private function convertCI3EncryptedData(): void
|
|
{
|
|
$appConfig = model(Appconfig::class);
|
|
|
|
$ci3EncryptedData = [
|
|
'clcdesq_api_key' => '',
|
|
'clcdesq_api_url' => '',
|
|
'mailchimp_api_key' => '',
|
|
'mailchimp_list_id' => '',
|
|
'smtp_pass' => ''
|
|
];
|
|
|
|
foreach ($ci3EncryptedData as $key => $value) {
|
|
$ci3EncryptedData[$key] = $appConfig->get_value($key);
|
|
}
|
|
|
|
$decryptedData = $this->decryptCI3Data($ci3EncryptedData);
|
|
|
|
if (!checkEncryption()) {
|
|
abortEncryptionConversion();
|
|
throw new DatabaseException('Failed to persist encryption key. Check logs for details.');
|
|
}
|
|
|
|
$ci4EncryptedData = $this->encryptData($decryptedData);
|
|
|
|
$success = empty(array_diff_assoc($decryptedData, $this->decryptData($ci4EncryptedData)));
|
|
if (!$success) {
|
|
abortEncryptionConversion();
|
|
throw new RedirectException('login'); // TODO: Need to figure out how to pass the error to the Login controller so that it gets displayed.
|
|
}
|
|
|
|
if (!$appConfig->batch_save($ci4EncryptedData)) {
|
|
abortEncryptionConversion();
|
|
throw new DatabaseException('Failed to save converted encryption data. Check logs for details.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decrypts CI3 encrypted data and returns the plaintext values.
|
|
*
|
|
* @param array $encryptedData Data encrypted using CI3 methodology.
|
|
* @return array Plaintext, unencrypted data.
|
|
*/
|
|
private function decryptCI3Data(array $encryptedData): array
|
|
{
|
|
$config = new Encryption();
|
|
$config->driver = 'OpenSSL';
|
|
$config->key = config('Encryption')->key;
|
|
$config->cipher = 'AES-128-CBC';
|
|
$config->rawData = false;
|
|
$config->encryptKeyInfo = 'encryption';
|
|
$config->authKeyInfo = 'authentication';
|
|
|
|
$encrypter = Services::encrypter($config);
|
|
|
|
return array_map(function ($value) use ($encrypter) {
|
|
return !empty($value) ? $encrypter->decrypt($value) : '';
|
|
}, $encryptedData);
|
|
}
|
|
|
|
/**
|
|
* Encrypts data using CI4 algorithms.
|
|
*
|
|
* @param array $plainData Data to be encrypted.
|
|
* @return array Encrypted data.
|
|
*/
|
|
private function encryptData(array $plainData): array
|
|
{
|
|
$encrypter = Services::encrypter();
|
|
|
|
return array_map(function ($value) use ($encrypter) {
|
|
return $value !== '' ? $encrypter->encrypt($value) : '';
|
|
}, $plainData);
|
|
}
|
|
|
|
/**
|
|
* Decrypts data using CI4 algorithms.
|
|
*
|
|
* @param array $encryptedData Data to be decrypted.
|
|
* @return array Decrypted data.
|
|
*/
|
|
private function decryptData(array $encryptedData): array
|
|
{
|
|
$encrypter = Services::encrypter();
|
|
|
|
return array_map(function ($value) use ($encrypter) {
|
|
return !empty($value) ? $encrypter->decrypt($value) : '';
|
|
}, $encryptedData);
|
|
}
|
|
}
|