EncryptedModel   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Test Coverage

Coverage 81.58%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 36
dl 0
loc 148
ccs 31
cts 38
cp 0.8158
rs 10
c 3
b 0
f 0
wmc 17

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setRawAttributes() 0 3 1
A decryptAttributes() 0 12 4
A encryptAttributes() 0 9 3
A performUpdate() 0 31 4
A performInsert() 0 44 5
1
<?php
2
3
namespace Swis\Laravel\Encrypted;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Str;
8
9
class EncryptedModel extends Model
10
{
11
    /**
12
     * The attributes that should be stored encrypted.
13
     *
14
     * @var array
15
     */
16
    protected $encrypted = [];
17
18
    /**
19
     * {@inheritdoc}
20
     *
21
     * @param array $attributes
22
     * @param bool  $sync
23
     *
24
     * @return $this
25
     */
26 8
    public function setRawAttributes(array $attributes, $sync = false)
27
    {
28 8
        return parent::setRawAttributes($this->decryptAttributes($attributes), $sync);
29
    }
30
31
    /**
32
     * {@inheritdoc}
33
     *
34
     * @param \Illuminate\Database\Eloquent\Builder $query
35
     *
36
     * @return bool
37
     */
38 2
    protected function performInsert(Builder $query)
39
    {
40 2
        if ($this->fireModelEvent('creating') === false) {
41
            return false;
42
        }
43
44
        // First we'll need to create a fresh query instance and touch the creation and
45
        // update timestamps on this model, which are maintained by us for developer
46
        // convenience. After, we will just continue saving these model instances.
47 2
        if ($this->usesTimestamps()) {
48
            $this->updateTimestamps();
49
        }
50
51
        // If the model has an incrementing key, we can use the "insertGetId" method on
52
        // the query builder, which will give us back the final inserted ID for this
53
        // table from the database. Not all tables have to be incrementing though.
54
        // But before we insert the attributes, we will encrypt them.
55 2
        $attributes = $this->encryptAttributes($this->getAttributes());
56
57 2
        if ($this->getIncrementing()) {
58 2
            $this->insertAndSetId($query, $attributes);
59
        }
60
61
        // If the table isn't incrementing we'll simply insert these attributes as they
62
        // are. These attribute arrays must contain an "id" column previously placed
63
        // there by the developer as the manually determined key for these models.
64
        else {
65
            if (empty($attributes)) {
66
                return true;
67
            }
68
69
            $query->insert($attributes);
70
        }
71
72
        // We will go ahead and set the exists property to true, so that it is set when
73
        // the created event is fired, just in case the developer tries to update it
74
        // during the event. This will allow them to do so and run an update here.
75 2
        $this->exists = true;
76
77 2
        $this->wasRecentlyCreated = true;
78
79 2
        $this->fireModelEvent('created', false);
80
81 2
        return true;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     *
87
     * @param \Illuminate\Database\Eloquent\Builder $query
88
     *
89
     * @return bool
90
     */
91 2
    protected function performUpdate(Builder $query)
92
    {
93
        // If the updating event returns false, we will cancel the update operation so
94
        // developers can hook Validation systems into their models and cancel this
95
        // operation if the model does not pass validation. Otherwise, we update.
96 2
        if ($this->fireModelEvent('updating') === false) {
97
            return false;
98
        }
99
100
        // First we need to create a fresh query instance and touch the creation and
101
        // update timestamp on the model which are maintained by us for developer
102
        // convenience. Then we will just continue saving the model instances.
103 2
        if ($this->usesTimestamps()) {
104
            $this->updateTimestamps();
105
        }
106
107
        // Once we have run the update operation, we will fire the "updated" event for
108
        // this model instance. This will allow developers to hook into these after
109
        // models are updated, giving them a chance to do any special processing.
110
        // But before we update the attributes, we will encrypt them.
111 2
        $dirty = $this->encryptAttributes($this->getDirty());
112
113 2
        if (count($dirty) > 0) {
114 2
            $this->setKeysForSaveQuery($query)->update($dirty);
115
116 2
            $this->syncChanges();
117
118 2
            $this->fireModelEvent('updated', false);
119
        }
120
121 2
        return true;
122
    }
123
124
    /**
125
     * @param array $attributes
126
     *
127
     * @return array
128
     */
129 8
    private function decryptAttributes(array $attributes): array
130
    {
131 8
        foreach ($this->encrypted as $key) {
132
            // We only try to decrypt the attribute if the value starts with 'eyJpdiI6'
133
            // because the return value of \Illuminate\Encryption\Encrypter is a
134
            // Base64-encoded JSON string and this always starts with that.
135 8
            if (isset($attributes[$key]) && Str::startsWith($attributes[$key], 'eyJpdiI6')) {
136 2
                $attributes[$key] = app('encrypted-data.encrypter')->decrypt($attributes[$key]);
137
            }
138
        }
139
140 8
        return $attributes;
141
    }
142
143
    /**
144
     * @param array $attributes
145
     *
146
     * @return array
147
     */
148 4
    private function encryptAttributes(array $attributes): array
149
    {
150 4
        foreach ($this->encrypted as $key) {
151 4
            if (isset($attributes[$key])) {
152 4
                $attributes[$key] = app('encrypted-data.encrypter')->encrypt($attributes[$key]);
153
            }
154
        }
155
156 4
        return $attributes;
157
    }
158
}
159