Skip to content

Commit bc36b22

Browse files
committed
develop/v2.3.0: Added encryption logic
1 parent 1873cd5 commit bc36b22

File tree

4 files changed

+334
-2
lines changed

4 files changed

+334
-2
lines changed

src/Helper/Encryptor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Wazzac\DbEncrypt\Helper;
3+
namespace Wazza\DbEncrypt\Helper;
44

55
use RuntimeException;
66
use InvalidArgumentException;

src/Http/Controllers/DnEncryptController.php

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Wazza\DbEncrypt\Http\Controllers\BaseController;
66
use Wazza\DbEncrypt\Models\EncryptedAttributes;
7+
use Wazza\DbEncrypt\Helper\Encryptor;
78
use Illuminate\Support\Facades\App;
89
use Illuminate\Database\Eloquent\Model;
910
use Illuminate\Contracts\Container\BindingResolutionException;
@@ -22,6 +23,26 @@
2223

2324
class DnEncryptController extends BaseController
2425
{
26+
/**
27+
* The model to sync
28+
*
29+
* @var \Illuminate\Database\Eloquent\Model
30+
*/
31+
private $model;
32+
33+
/**
34+
* Property to define the Model properties that will be encrypted.
35+
* Structure:
36+
* $encryptedProperties = [
37+
* 'social_security_number',
38+
* 'business_address',
39+
* 'business_city',
40+
* ];
41+
*
42+
* @var array
43+
*/
44+
private $encryptedProperties = [];
45+
2546
/**
2647
* Create a new CrmController instance and define the log identifier (blank will create a new one)
2748
*
@@ -36,6 +57,136 @@ public function __construct(?string $logIdentifier = null)
3657
// parent constructor
3758
parent::__construct($logIdentifier);
3859

39-
// ...
60+
// clear the properties
61+
$this->model = null;
62+
$this->encryptedProperties = [];
63+
}
64+
65+
/**
66+
* Set the model to process.
67+
* Any property that is defined in the model will be encrypted in the `encrypted_attributes` table
68+
*
69+
* @param \Illuminate\Database\Eloquent\Model|null $model
70+
* @return $this
71+
*/
72+
public function setModel(?Model $model = null)
73+
{
74+
// -- set the model
75+
$this->model = $model;
76+
77+
// -- log the model set (and set the append to model type)
78+
$this->logger->setLogIdentifier('[' . get_class($this->model) . ']', true);
79+
$this->logger->infoLow('DB Encrypt Model set. Class: `' . get_class($this->model) . '`, Table: `' . $this->model->getTable() . '`.');
80+
81+
// -- set the properties in the model that will be encrypted
82+
$this->setEncryptedProperties($model->encryptedProperties ?? []);
83+
84+
// done
85+
return $this;
86+
}
87+
88+
/**
89+
* Set the property mapping for the CRM provider
90+
*
91+
* @param array $propertyMapping
92+
* @return $this
93+
*/
94+
public function setEncryptedProperties(array $propertyMapping = []): self
95+
{
96+
// set the property mappings
97+
$this->encryptedProperties = $propertyMapping;
98+
$this->logger->infoLow('DB Encrypt Mapping: `' . json_encode($this->encryptedProperties) . '`');
99+
return $this;
100+
}
101+
102+
/**
103+
* Get the encrypted properties defined in the model.
104+
*
105+
* @return array
106+
*/
107+
public function getEncryptedProperties(): array
108+
{
109+
// return the encrypted properties
110+
return $this->encryptedProperties;
111+
}
112+
113+
/**
114+
* Get the model that is set.
115+
*
116+
* @return \Illuminate\Database\Eloquent\Model|null
117+
*/
118+
public function getModel(): ?Model
119+
{
120+
// return the model
121+
return $this->model;
122+
}
123+
124+
/**
125+
* Check if the model is defined.
126+
*
127+
* @return bool
128+
*/
129+
public function isModelDefined(): bool
130+
{
131+
// check if the model is defined
132+
return $this->model instanceof Model;
133+
}
134+
135+
/**
136+
* Encrypt all of the model defined ($this->encryptedProperties) properties.
137+
* If $property is provided, only that property will be encrypted.
138+
*
139+
* @param mixed $property The property to encrypt. If null, all properties will be encrypted.
140+
* @return void
141+
* @throws Exception
142+
*/
143+
public function encrypt($property = null)
144+
{
145+
// check if the model is set
146+
if (!$this->isModelDefined()) {
147+
throw new Exception('Model is not set. Please set the model using the `setModel` method.');
148+
}
149+
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.');
153+
}
154+
155+
// encrypt the properties
156+
$this->logger->infoLow('Encrypting properties for model: ' . get_class($this->model) . ', property: ' . ($property ?? 'all'));
157+
158+
// loop through the encrypted properties
159+
foreach ($this->encryptedProperties as $prop) {
160+
// making sure the provided property to encrypt (if any) is defined in the model encrypted properties list
161+
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
165+
$value = $this->model->{$prop};
166+
$encryptedValue = Encryptor::encrypt($value);
167+
168+
// save the encrypted value in the EncryptedAttributes model
169+
EncryptedAttributes::updateOrCreate(
170+
[
171+
'object_type' => get_class($this->model),
172+
'object_id' => $this->model->getKey(),
173+
'attribute' => $prop,
174+
],
175+
[
176+
'hash_index' => Encryptor::hash($value),
177+
'encrypted_value' => $encryptedValue,
178+
]
179+
);
180+
181+
// log the encryption
182+
$this->logger->infoLow('Encrypted property: ' . $prop . ', value: ' . $value);
183+
} else {
184+
throw new Exception('Property `' . $prop . '` does not exist in the model.');
185+
}
186+
}
187+
}
188+
189+
// log the encryption
190+
$this->logger->infoLow('Encryption completed for model: ' . get_class($this->model) . ', properties: ' . json_encode($this->encryptedProperties));
40191
}
41192
}

src/Providers/DbEncryptServiceProvider.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ public function register(): void
4242
$this->app->singleton(DnEncryptController::class, function () {
4343
return new DnEncryptController();
4444
});
45+
46+
/*
47+
You can use the above registered singleton service in your application like this:
48+
$dnEncryptController = app(DnEncryptController::class);
49+
$dnEncryptController->someMethod();
50+
51+
You can also use dependency injection in your controllers or other services.
52+
For example:
53+
public function __construct(DnEncryptController $dnEncryptController)
54+
{
55+
$this->dnEncryptController = $dnEncryptController;
56+
}
57+
58+
This allows you to access the methods of DnEncryptController within your class.
59+
You can also register other services or bindings as needed.
60+
$this->app->bind('some.service', function ($app) {
61+
return new SomeService();
62+
});
63+
*/
4564
}
4665

4766
/**
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<?php
2+
3+
namespace Wazza\DbEncrypt\Traits;
4+
5+
use Wazza\DbEncrypt\Http\Controllers\DnEncryptController;
6+
7+
/**
8+
* Include this trait in your model to enable database encryption functionality.
9+
*
10+
* public function save(array $options = [])
11+
* {
12+
* parent::save($options);
13+
* // Call the encryptAttributes method to encrypt the model's attributes.
14+
* $this->encryptAttributes();
15+
* }
16+
*/
17+
trait HasEncryptedAttributes
18+
{
19+
/**
20+
* Encrypt the model's attributes using the DnEncryptController.
21+
*
22+
* @return void
23+
*/
24+
public function encryptAttributes(): void
25+
{
26+
// Get the current model instance the trait is called from
27+
$model = $this;
28+
29+
if (!$model instanceof \Illuminate\Database\Eloquent\Model) {
30+
throw new \InvalidArgumentException('The encryptAttributes method can only be called from an Eloquent model instance.');
31+
}
32+
33+
// Initiate a database encryption process
34+
$dnEncryptController = app(DnEncryptController::class);
35+
$dnEncryptController->setModel($model);
36+
$dnEncryptController->encrypt();
37+
}
38+
39+
/**
40+
* Decrypt the model's attributes using the DnEncryptController.
41+
*
42+
* @return void
43+
*/
44+
public function decryptAttributes(): void
45+
{
46+
// Get the current model instance the trait is called from
47+
$model = $this;
48+
49+
if (!$model instanceof \Illuminate\Database\Eloquent\Model) {
50+
throw new \InvalidArgumentException('The decryptAttributes method can only be called from an Eloquent model instance.');
51+
}
52+
53+
// Initiate a database decryption process
54+
$dnEncryptController = app(DnEncryptController::class);
55+
$dnEncryptController->setModel($model);
56+
$dnEncryptController->decrypt();
57+
}
58+
59+
/**
60+
* Get the encryption status of the model's attributes.
61+
*
62+
* @return bool
63+
*/
64+
public function isEncrypted(): bool
65+
{
66+
// Get the current model instance the trait is called from
67+
$model = $this;
68+
69+
if (!$model instanceof \Illuminate\Database\Eloquent\Model) {
70+
throw new \InvalidArgumentException('The isEncrypted method can only be called from an Eloquent model instance.');
71+
}
72+
73+
// Check if the model's attributes are encrypted
74+
$dnEncryptController = app(DnEncryptController::class);
75+
return $dnEncryptController->isEncrypted($model);
76+
}
77+
78+
/**
79+
* Decrypt the model's attributes before saving.
80+
*
81+
* @param array $options
82+
* @return void
83+
*/
84+
public function save(array $options = []): void
85+
{
86+
// Decrypt the attributes before saving
87+
$this->decryptAttributes();
88+
89+
// Call the parent save method
90+
parent::save($options);
91+
92+
// Encrypt the attributes after saving
93+
$this->encryptAttributes();
94+
}
95+
96+
/**
97+
* Delete the model and its encrypted attributes.
98+
*
99+
* @return void
100+
*/
101+
public function delete(): void
102+
{
103+
// Get the current model instance the trait is called from
104+
$model = $this;
105+
106+
if (!$model instanceof \Illuminate\Database\Eloquent\Model) {
107+
throw new \InvalidArgumentException('The delete method can only be called from an Eloquent model instance.');
108+
}
109+
110+
// Delete the encrypted attributes using the DnEncryptController
111+
$dnEncryptController = app(DnEncryptController::class);
112+
$dnEncryptController->deleteEncryptedAttributes($model);
113+
114+
// Call the parent delete method
115+
parent::delete();
116+
}
117+
118+
/**
119+
* Restore the model and its encrypted attributes.
120+
*
121+
* @return void
122+
*/
123+
public function restore(): void
124+
{
125+
// Get the current model instance the trait is called from
126+
$model = $this;
127+
128+
if (!$model instanceof \Illuminate\Database\Eloquent\Model) {
129+
throw new \InvalidArgumentException('The restore method can only be called from an Eloquent model instance.');
130+
}
131+
132+
// Restore the encrypted attributes using the DnEncryptController
133+
$dnEncryptController = app(DnEncryptController::class);
134+
$dnEncryptController->restoreEncryptedAttributes($model);
135+
136+
// Call the parent restore method
137+
parent::restore();
138+
}
139+
140+
/**
141+
* Force delete the model and its encrypted attributes.
142+
*
143+
* @return void
144+
*/
145+
public function forceDelete(): void
146+
{
147+
// Get the current model instance the trait is called from
148+
$model = $this;
149+
150+
if (!$model instanceof \Illuminate\Database\Eloquent\Model) {
151+
throw new \InvalidArgumentException('The forceDelete method can only be called from an Eloquent model instance.');
152+
}
153+
154+
// Force delete the encrypted attributes using the DnEncryptController
155+
$dnEncryptController = app(DnEncryptController::class);
156+
$dnEncryptController->forceDeleteEncryptedAttributes($model);
157+
158+
// Call the parent forceDelete method
159+
parent::forceDelete();
160+
}
161+
162+
}

0 commit comments

Comments
 (0)