Skip to content

Commit 4d877db

Browse files
committed
LNA-512: Added teh ability to remove an encryption property from the table if it it set to null
1 parent bc36b22 commit 4d877db

File tree

4 files changed

+170
-139
lines changed

4 files changed

+170
-139
lines changed

src/Helper/Encryptor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ final class Encryptor
2222
private static function getKey(?string $manualKey = null): string
2323
{
2424
// get the key from the config or .env
25-
$baseKey = config('wazza-db-encrypt.key');
25+
$baseKey = config('db-encrypt.key');
2626
if (empty($baseKey)) {
2727
throw new RuntimeException('Encryption key is not set in config or .env.');
2828
}

src/Http/Controllers/DnEncryptController.php renamed to src/Http/Controllers/DbEncryptController.php

Lines changed: 109 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,13 @@
1313
use Exception;
1414

1515
/**
16-
* Sync Class CrmController
17-
* Example: (new CrmController())->setModel($user)->execute();
16+
* DB Encrypt / Decrypt Controller
1817
*
1918
* @package Wazza\DbEncrypt\Http\Controllers
2019
* @version 1.0.0
21-
* @todo convert the log class to be injected into the controller instead of using the facade
2220
*/
2321

24-
class DnEncryptController extends BaseController
22+
class DbEncryptController extends BaseController
2523
{
2624
/**
2725
* The model to sync
@@ -93,6 +91,16 @@ public function setModel(?Model $model = null)
9391
*/
9492
public function setEncryptedProperties(array $propertyMapping = []): self
9593
{
94+
// Check for conflicts: encrypted property must not exist as a column in the model's table
95+
$table = $this->model ? $this->model->getTable() : null;
96+
$schema = $this->model ? $this->model->getConnection()->getSchemaBuilder() : null;
97+
if ($table && $schema) {
98+
foreach ($propertyMapping as $prop) {
99+
if ($schema->hasColumn($table, $prop)) {
100+
throw new Exception("Cannot specify encrypted property '{$prop}' because it already exists as a column in the model's table '{$table}'.");
101+
}
102+
}
103+
}
96104
// set the property mappings
97105
$this->encryptedProperties = $propertyMapping;
98106
$this->logger->infoLow('DB Encrypt Mapping: `' . json_encode($this->encryptedProperties) . '`');
@@ -133,42 +141,82 @@ public function isModelDefined(): bool
133141
}
134142

135143
/**
136-
* Encrypt all of the model defined ($this->encryptedProperties) properties.
137-
* If $property is provided, only that property will be encrypted.
144+
* Encrypt all properties of the model.
145+
* This method will encrypt all properties defined in the model's encrypted properties.
138146
*
139-
* @param mixed $property The property to encrypt. If null, all properties will be encrypted.
140147
* @return void
141148
* @throws Exception
142149
*/
143-
public function encrypt($property = null)
150+
public function encryptAll()
144151
{
145152
// check if the model is set
146153
if (!$this->isModelDefined()) {
147154
throw new Exception('Model is not set. Please set the model using the `setModel` method.');
148155
}
149156

150-
// check if the property is set
151-
if ($property !== null && !in_array($property, $this->encryptedProperties)) {
152-
throw new Exception('Property `' . $property . '` is not defined in the encrypted properties.');
157+
// encrypt all properties
158+
$this->encrypt();
159+
}
160+
161+
/**
162+
* Encrypt a specific property of the model.
163+
* This method will only encrypt the property if it is defined in the model's encrypted properties.
164+
*
165+
* @param string $property
166+
* @return void
167+
* @throws Exception
168+
*/
169+
public function encryptProperty(string $property)
170+
{
171+
// check if the model is set
172+
if (!$this->isModelDefined()) {
173+
throw new Exception('Model is not set. Please set the model using the `setModel` method.');
153174
}
154175

155-
// encrypt the properties
156-
$this->logger->infoLow('Encrypting properties for model: ' . get_class($this->model) . ', property: ' . ($property ?? 'all'));
176+
// encrypt the property
177+
$this->encrypt($property);
178+
}
157179

158-
// loop through the encrypted properties
180+
/**
181+
* Encrypt all of the model defined ($this->encryptedProperties) properties.
182+
* If $property is provided, only that property will be encrypted.
183+
*
184+
* @param mixed $property The property to encrypt. If null, all properties will be encrypted.
185+
* @return void
186+
* @throws Exception
187+
*/
188+
public function encrypt(
189+
$property = null
190+
) {
191+
if (!$this->isModelDefined()) {
192+
throw new Exception('Model is not set. Please set the model using the `setModel` method.');
193+
}
194+
if ($property !== null && !in_array($property, $this->encryptedProperties, true)) {
195+
throw new Exception('Property `' . $property . '` is not defined in the encrypted properties.');
196+
}
197+
$this->logger->infoLow('Encrypting properties for model: ' . $this->model->getTable() . ', property: ' . ($property ?? 'all'));
159198
foreach ($this->encryptedProperties as $prop) {
160-
// making sure the provided property to encrypt (if any) is defined in the model encrypted properties list
161199
if ($property === null || $prop === $property) {
162-
// check if the property exists in the model
163-
if (property_exists($this->model, $prop)) {
164-
// encrypt the property
200+
if (array_key_exists($prop, $this->model->getAttributes())) {
201+
// Use Eloquent attribute check
165202
$value = $this->model->{$prop};
166-
$encryptedValue = Encryptor::encrypt($value);
203+
if ($value === null || $value === '') {
204+
// if the value was encrypted, hard delete it form the encrypted_attributes table
205+
EncryptedAttributes::where([
206+
'object_type' => $this->model->getTable(),
207+
'object_id' => $this->model->getKey(),
208+
'attribute' => $prop,
209+
])->delete();
167210

168-
// save the encrypted value in the EncryptedAttributes model
211+
// done
212+
continue;
213+
}
214+
215+
// Encrypt the value and store it in the encrypted_attributes table
216+
$encryptedValue = Encryptor::encrypt($value);
169217
EncryptedAttributes::updateOrCreate(
170218
[
171-
'object_type' => get_class($this->model),
219+
'object_type' => $this->model->getTable(),
172220
'object_id' => $this->model->getKey(),
173221
'attribute' => $prop,
174222
],
@@ -178,15 +226,50 @@ public function encrypt($property = null)
178226
]
179227
);
180228

181-
// log the encryption
182-
$this->logger->infoLow('Encrypted property: ' . $prop . ', value: ' . $value);
229+
// Do NOT log the actual value
230+
$this->logger->infoLow('Encrypted property: ' . $prop . ' [value hidden for security]');
183231
} else {
184-
throw new Exception('Property `' . $prop . '` does not exist in the model.');
232+
continue;
185233
}
186234
}
187235
}
236+
$this->logger->infoLow('Encryption completed for model: ' . $this->model->getTable() . ', properties: ' . json_encode($this->encryptedProperties));
237+
}
188238

189-
// log the encryption
190-
$this->logger->infoLow('Encryption completed for model: ' . get_class($this->model) . ', properties: ' . json_encode($this->encryptedProperties));
239+
/**
240+
* Decrypt all of the model defined ($this->encryptedProperties) properties.
241+
* If $property is provided, only that property will be decrypted.
242+
*
243+
* @param mixed $property The property to decrypt. If null, all properties will be decrypted.
244+
* @return void
245+
* @throws Exception
246+
*/
247+
public function decrypt($property = null)
248+
{
249+
if (!$this->isModelDefined()) {
250+
throw new Exception('Model is not set. Please set the model using the `setModel` method.');
251+
}
252+
if ($property !== null && !in_array($property, $this->encryptedProperties, true)) {
253+
throw new Exception('Property `' . $property . '` is not defined in the encrypted properties.');
254+
}
255+
$this->logger->infoLow('Decrypting properties for model: ' . $this->model->getTable() . ', property: ' . ($property ?? 'all'));
256+
foreach ($this->encryptedProperties as $prop) {
257+
if ($property === null || $prop === $property) {
258+
$encryptedAttribute = EncryptedAttributes::where([
259+
'object_type' => $this->model->getTable(),
260+
'object_id' => $this->model->getKey(),
261+
'attribute' => $prop,
262+
])->first();
263+
if ($encryptedAttribute && $encryptedAttribute->encrypted_value) {
264+
$decryptedValue = Encryptor::decrypt($encryptedAttribute->encrypted_value);
265+
$this->model->{$prop} = $decryptedValue;
266+
$this->logger->infoLow('Decrypted property: ' . $prop . ' - success.');
267+
} else {
268+
$this->model->{$prop} = null;
269+
$this->logger->infoLow('Decrypted property: ' . $prop . ' - not found, set to null.');
270+
}
271+
}
272+
}
273+
$this->logger->infoLow('Decryption completed for model: ' . $this->model->getTable() . ', properties: ' . json_encode($this->encryptedProperties));
191274
}
192275
}

src/Providers/DbEncryptServiceProvider.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace Wazza\DbEncrypt\Providers;
44

55
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
6-
use Wazza\DbEncrypt\Http\Controllers\DnEncryptController;
6+
use Wazza\DbEncrypt\Http\Controllers\DbEncryptController;
77

88
class DbEncryptServiceProvider extends BaseServiceProvider
99
{
@@ -39,23 +39,23 @@ public function register(): void
3939
);
4040

4141
// Register the singleton service the package provides.
42-
$this->app->singleton(DnEncryptController::class, function () {
43-
return new DnEncryptController();
42+
$this->app->singleton(DbEncryptController::class, function () {
43+
return new DbEncryptController();
4444
});
4545

4646
/*
4747
You can use the above registered singleton service in your application like this:
48-
$dnEncryptController = app(DnEncryptController::class);
48+
$dnEncryptController = app(DbEncryptController::class);
4949
$dnEncryptController->someMethod();
5050
5151
You can also use dependency injection in your controllers or other services.
5252
For example:
53-
public function __construct(DnEncryptController $dnEncryptController)
53+
public function __construct(DbEncryptController $dnEncryptController)
5454
{
5555
$this->dnEncryptController = $dnEncryptController;
5656
}
5757
58-
This allows you to access the methods of DnEncryptController within your class.
58+
This allows you to access the methods of DbEncryptController within your class.
5959
You can also register other services or bindings as needed.
6060
$this->app->bind('some.service', function ($app) {
6161
return new SomeService();

0 commit comments

Comments
 (0)