Passed
Pull Request — master (#3)
by Jasper
03:15
created

EncryptedModel::originalIsEquivalent()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3.1406

Importance

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