EncryptedDBJson::scaffoldFormField()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 1
c 1
b 0
f 1
dl 0
loc 3
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
namespace LeKoala\Encrypt;
4
5
use SilverStripe\ORM\DataObject;
6
use SilverStripe\Forms\HiddenField;
7
use ParagonIE\CipherSweet\CipherSweet;
8
use ParagonIE\CipherSweet\JsonFieldMap;
9
use ParagonIE\CipherSweet\EncryptedJsonField;
10
11
/**
12
 * A simple extension over EncryptedDBText that supports json
13
 * as a datastructure
14
 * The data is stored in a text field
15
 *
16
 * If you want to access array stuff, you need to use
17
 * $model->dbObject('myField')->toArray() or any other method
18
 *
19
 * This field is a great way to store serialized encrypted data
20
 */
21
class EncryptedDBJson extends EncryptedDBText
22
{
23
24
    /**
25
     * @return ?string
26
     */
27
    public function getJsonMap()
28
    {
29
        if (array_key_exists('map', $this->options)) {
30
            return $this->options['map'];
31
        }
32
        return null;
33
    }
34
35
    /**
36
     * We cannot search on json fields
37
     *
38
     * @param string $title
39
     * @return HiddenField
40
     */
41
    public function scaffoldSearchField($title = null)
42
    {
43
        return HiddenField::create($this->getName());
44
    }
45
46
    /**
47
     * Json data is not easily displayed
48
     *
49
     * @param string $title
50
     * @param array<mixed> $params
51
     * @return HiddenField
52
     */
53
    public function scaffoldFormField($title = null, $params = null)
54
    {
55
        return HiddenField::create($this->getName());
56
    }
57
58
    /**
59
     * @return mixed
60
     */
61
    public function decode()
62
    {
63
        if (!$this->value) {
64
            return false;
65
        }
66
        return json_decode($this->value);
67
    }
68
69
    /**
70
     * @return array<string,mixed>
71
     */
72
    public function decodeArray()
73
    {
74
        if (!$this->value) {
75
            return [];
76
        }
77
        return json_decode($this->value, true);
78
    }
79
80
    /**
81
     * @return array<string,mixed>
82
     */
83
    public function toArray()
84
    {
85
        return $this->decodeArray();
86
    }
87
88
    /**
89
     * @return string
90
     */
91
    public function pretty()
92
    {
93
        if (!$this->value) {
94
            return '';
95
        }
96
        $decoded = json_decode($this->value);
97
        if (!$decoded) {
98
            return json_last_error_msg();
99
        }
100
        $result = json_encode($decoded, JSON_PRETTY_PRINT);
101
        if (!$result) {
102
            return json_last_error_msg();
103
        }
104
        return $result;
105
    }
106
107
    /**
108
     * @inheritDoc
109
     * @return void
110
     */
111
    public function saveInto($dataObject)
112
    {
113
        if ($this->value && is_array($this->value)) {
114
            $this->value = json_encode($this->value);
115
        }
116
        parent::saveInto($dataObject);
117
    }
118
119
    /**
120
     * Add a value
121
     *
122
     * @link https://stackoverflow.com/questions/7851590/array-set-value-using-dot-notation
123
     * @param string|array<string> $key
124
     * @param string $value
125
     * @return $this
126
     */
127
    public function addValue($key, $value)
128
    {
129
        $currentValue = $this->decodeArray();
130
131
        if (!is_array($key)) {
132
            $key = [$key];
133
        }
134
        $arr = &$currentValue;
135
        foreach ($key as $idx) {
136
            if (!isset($arr[$idx])) {
137
                $arr[$idx] = [];
138
            }
139
            $arr = &$arr[$idx];
140
        }
141
        $arr = $value;
142
        return $this->setValue($currentValue);
143
    }
144
145
    /**
146
     * Internally, the value is always a json string
147
     *
148
     * @param mixed $value
149
     * @param DataObject $record
150
     * @param boolean $markChanged
151
     * @return $this
152
     */
153
    public function setValue($value, $record = null, $markChanged = true)
154
    {
155
        $this->setEncryptionAad($record);
156
157
        // Not supported, we need decrypted values for methods to work properly
158
        // Return early if we keep encrypted value in memory
159
        // if (!EncryptHelper::getAutomaticDecryption()) {
160
        //     $this->value = $value;
161
        //     return $this;
162
        // }
163
164
        // Decrypt first if needed
165
        if ($this->getJsonMap() && $value && is_string($value)) {
166
            if (EncryptHelper::isJsonEncrypted($value)) {
167
                $aad = $this->encryptionAad;
168
                $value = json_encode($this->getEncryptedJsonField()->decryptJson($value, $aad));
169
            }
170
        }
171
        // Internally, we use a string
172
        if (is_array($value)) {
173
            $value = json_encode($value);
174
        }
175
176
        return parent::setValue($value, $record, $markChanged);
177
    }
178
179
    /**
180
     * @inheritDoc
181
     */
182
    public function prepValueForDB($value)
183
    {
184
        // We need an array to encrypt
185
        if ($this->getJsonMap() && $value && is_string($value)) {
186
            $value = $this->toArray();
187
        }
188
        if (is_array($value)) {
189
            if ($this->getJsonMap()) {
190
                $aad = $this->encryptionAad;
191
                $value = $this->getEncryptedJsonField()->encryptJson($value, $aad);
192
                return $value; // return early
193
            } else {
194
                $value = json_encode($value);
195
                if ($value === false) {
196
                    $value = '';
197
                }
198
            }
199
        }
200
        return parent::prepValueForDB($value);
201
    }
202
203
    /**
204
     * We return false because we can accept array and convert it to string
205
     * @return boolean
206
     */
207
    public function scalarValueOnly()
208
    {
209
        return false;
210
    }
211
212
    /**
213
     * @param CipherSweet $engine
214
     * @param JsonFieldMap $map
215
     * @return EncryptedJsonField
216
     */
217
    public function getEncryptedJsonField($engine = null, $map = null)
218
    {
219
        if ($engine === null) {
220
            $engine = EncryptHelper::getCipherSweet();
221
        }
222
        if ($map === null) {
223
            $mapString = $this->getJsonMap();
224
            $map = JsonFieldMap::fromString($mapString);
0 ignored issues
show
Bug introduced by
It seems like $mapString can also be of type null; however, parameter $string of ParagonIE\CipherSweet\JsonFieldMap::fromString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

224
            $map = JsonFieldMap::fromString(/** @scrutinizer ignore-type */ $mapString);
Loading history...
225
        }
226
        $encryptedField = EncryptedJsonField::create($engine, $map, $this->tableName, $this->name);
227
        $encryptedField->setStrictMode(false);
228
        return $encryptedField;
229
    }
230
}
231