Passed
Push — master ( a58ecc...7a06a7 )
by Ion
02:10
created

BaseModel::getOriginal()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 7
rs 10
1
<?php
2
3
namespace IonGhitun\MysqlEncryption\Models;
4
5
use Faker\Factory;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Arr;
8
9
/**
10
 * Class BaseModel
11
 *
12
 * @package IonGhitun\MysqlEncryption\Models
13
 */
14
class BaseModel extends Model
15
{
16
    /**
17
     * Elements should be the names of the fields
18
     *
19
     * @var array
20
     */
21
    protected $encrypted = [];
22
23
    /**
24
     * Elements should be pairs with key column name and value and array with type and optional parameters
25
     *
26
     * @example ['age' => ['randomDigit']]
27
     * @example ['age' => ['numberBetween', '18','50']]
28
     *
29
     * Available types: https://github.com/fzaninotto/Faker
30
     *
31
     * @var array
32
     */
33
    protected $anonymizable = [];
34
35
    /**
36
     * Get model attribute
37
     *
38
     * @param string $key
39
     *
40
     * @return false|mixed|string
41
     */
42
    public function getAttribute($key)
43
    {
44
        $value = parent::getAttribute($key);
45
46
        if (array_key_exists($key, $this->relations) || method_exists($this, $key)) {
47
            return $value;
48
        } else {
49
            $value = parent::getAttribute($key);
50
        }
51
52
        if (in_array($key, $this->encrypted)) {
53
            return $this->aesDecrypt($value);
54
        }
55
56
        return $value;
57
    }
58
59
    /**
60
     * Descrypt value
61
     *
62
     * @param $val
63
     * @param string $cypher
64
     * @param bool $mySqlKey
65
     *
66
     * @return false|string
67
     */
68
    private function aesDecrypt($val, $cypher = 'aes-128-ecb', $mySqlKey = true)
69
    {
70
        $secret = getenv('ENCRYPTION_KEY');
71
72
        $key = $mySqlKey ? $this->generateMysqlAesKey($secret) : $secret;
73
74
        return openssl_decrypt($val, $cypher, $key, 1);
75
    }
76
77
    /**
78
     * Generate encryption key
79
     *
80
     * @param $key
81
     *
82
     * @return string
83
     */
84
    private function generateMysqlAesKey($key)
85
    {
86
        $generatedKey = str_repeat(chr(0), 16);
87
88
        for ($i = 0, $len = strlen($key); $i < $len; $i++) {
89
            $generatedKey[$i % 16] = $generatedKey[$i % 16] ^ $key[$i];
90
        }
91
92
        return $generatedKey;
93
    }
94
95
    /**
96
     * Set model attribute
97
     *
98
     * @param string $key
99
     * @param mixed $value
100
     *
101
     * @return mixed
102
     */
103
    public function setAttribute($key, $value)
104
    {
105
        if (in_array($key, $this->encrypted)) {
106
            $value = $this->aesEncrypt($value);
107
        }
108
109
        return parent::setAttribute($key, $value);
110
    }
111
112
    /**
113
     * Encrypt value
114
     *
115
     * @param $val
116
     * @param string $cypher
117
     * @param bool $mySqlKey
118
     *
119
     * @return false|string
120
     */
121
    private function aesEncrypt($val, $cypher = 'aes-128-ecb', $mySqlKey = true)
122
    {
123
        $secret = getenv('ENCRYPTION_KEY');
124
125
        $key = $mySqlKey ? $this->generateMysqlAesKey($secret) : $secret;
126
127
        return openssl_encrypt($val, $cypher, $key, 1);
128
    }
129
130
    /**
131
     * Get attributes array
132
     *
133
     * @return array
134
     */
135
    public function attributesToArray()
136
    {
137
        $attributes = parent::attributesToArray();
138
139
        foreach ($this->encrypted as $key) {
140
            if (isset($attributes[$key])) {
141
                $attributes[$key] = $this->aesDecrypt($attributes[$key]);
142
            }
143
        }
144
145
        return $attributes;
146
    }
147
148
    /**
149
     * Get original not encrypted
150
     *
151
     * @param string|int|null $key
152
     * @param mixed|null $default
153
     *
154
     * @return array|false|mixed|string
155
     */
156
    public function getOriginal($key = null, $default = null)
157
    {
158
        if (in_array($key, $this->encrypted)) {
159
            return $this->aesDecrypt(Arr::get($this->original, $key, $default));
160
        }
161
162
        return Arr::get($this->original, $key, $default);
163
    }
164
165
    /**
166
     * Get encrypted columns
167
     *
168
     * @return array
169
     */
170
    public function getEncrypted()
171
    {
172
        return $this->encrypted;
173
    }
174
175
    /**
176
     * Get anonymizable columns
177
     *
178
     * @return array
179
     */
180
    public function getAnonymizable()
181
    {
182
        return $this->anonymizable;
183
    }
184
185
    /**
186
     * Anonymize model fields
187
     *
188
     * @param string|null $locale
189
     */
190
    public function anonymize($locale = null)
191
    {
192
        $faker = Factory::create($locale ?? (getenv('FAKER_LOCALE') ?? Factory::DEFAULT_LOCALE));
193
194
        foreach ($this->anonymizable as $field => $type) {
195
            if (in_array($field, $this->attributes)) {
196
                $method = $type[0];
197
198
                if (count($type) > 1) {
199
                    $this->$field = call_user_func([$faker, $method], array_slice($type, 1));
200
                } else {
201
                    $this->$field = $faker->$method;
202
                }
203
            }
204
        }
205
    }
206
207
    /**
208
     * where for encrypted columns
209
     *
210
     * @param $query
211
     * @param $column
212
     * @param $value
213
     *
214
     * @return mixed
215
     */
216
    public function scopeWhereEncrypted($query, $column, $value)
217
    {
218
        return $query->whereRaw('AES_DECRYPT(' . $column . ', "' . getenv("ENCRYPTION_KEY") . '") LIKE "' . $value . '" COLLATE utf8mb4_general_ci');
219
    }
220
221
    /**
222
     * orWhere for encrypted columns
223
     *
224
     * @param $query
225
     * @param $column
226
     * @param $value
227
     *
228
     * @return mixed
229
     */
230
    public function scopeOrWhereEncrypted($query, $column, $value)
231
    {
232
        return $query->orWhereRaw('AES_DECRYPT(' . $column . ', "' . getenv("ENCRYPTION_KEY") . '") LIKE "' . $value . '" COLLATE utf8mb4_general_ci');
233
    }
234
235
    /**
236
     * orderBy for encrypted columns
237
     *
238
     * @param $query
239
     * @param $column
240
     * @param $direction
241
     *
242
     * @return mixed
243
     */
244
    public function scopeOrderByEncrypted($query, $column, $direction)
245
    {
246
        return $query->orderByRaw('AES_DECRYPT(' . $column . ', "' . getenv("ENCRYPTION_KEY") . '") ' . $direction);
247
    }
248
}
249