Passed
Push — master ( ef7cbb...41c1ec )
by Russell
11:52
created

VerifiableExtension::onAfterWrite()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 12
nop 0
dl 0
loc 21
rs 8.7624
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @author  Russell Michell 2018 <[email protected]>
5
 * @package silverstripe-verifiable
6
 */
7
8
namespace PhpTek\Verifiable\Verify;
9
10
use SilverStripe\ORM\DataExtension;
11
use PhpTek\Verifiable\ORM\Fieldtype\ChainpointProof;
12
13
/**
14
 * By attaching this extension to any {@link DataObject} subclass and declaring a
15
 * $verifiable_fields array in YML config, all subsequent database writes will
16
 * be passed through here via {@link $this->onAfterWrite()};
17
 *
18
 * This {@link DataExtension} also provides a single field to which all verified
19
 * and verifiable chainpoint proofs are stored in a queryable JSON-aware field.
20
 *
21
 * @todo Store the hash function used and if subsequent verifications
22
 *       fail because differing hash functions are used, throw an exception.
23
 */
24
class VerifiableExtension extends DataExtension
25
{
26
    /**
27
     * Declares a JSON-aware {@link DBField} where all chainpoint proofs are stored.
28
     *
29
     * @var array
30
     * @config
31
     */
32
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
33
        'Proof' => ChainpointProof::class,
34
    ];
35
36
    /**
37
     * Field values will be hashed and committed to the current backend.
38
     *
39
     * @var array
40
     * @config
41
     */
42
    private static $verifiable_fields = [];
0 ignored issues
show
introduced by
The private property $verifiable_fields is not used, and could be removed.
Loading history...
43
44
    /**
45
     * After each write data from our verifiable_fields is compiled into a string
46
     * and submitted as a hash to the current backend.
47
     *
48
     * Once written, we poll the backend to receive the full chainpoint proof
49
     * which we'll need for subsequent verification checks, also made against the
50
     * same backend.
51
     *
52
     * If only the "Proof" field has been written-to, or no-data is found in the
53
     * verifiable_fields, this should not constitute a write that we need to do
54
     * anything with, and it's therefore skipped.
55
     *
56
     * @return void
57
     */
58
    public function onAfterWrite()
59
    {
60
        parent::onAfterWrite();
61
62
        // Skip queueing-up another verification process if only the "Proof" field
63
        // is modified
64
        $verifiableFields = $this->getOwner()->config()->get('verifiable_fields');
65
        $skipWriteCount = 0;
66
67
        foreach ($verifiableFields as $field) {
68
            if (!$this->getOwner()->getField($field)) {
69
                $skipWriteCount++;
70
            }
71
        }
72
73
        $verifiable = $this->normaliseData();
74
        $doWrite = count($verifiable) && (count($verifiableFields) !== $skipWriteCount);
75
76
        if ($doWrite) {
77
            $this->verifiableService->write($verifiable);
0 ignored issues
show
Bug introduced by
The property verifiableService does not exist on PhpTek\Verifiable\Verify\VerifiableExtension. Did you mean verifiable_fields?
Loading history...
78
            $this->verifiableService->queueVerification($this->getOwner());
79
        }
80
    }
81
82
    /**
83
     * Normalise this model's data so it's suited to being hashed.
84
     *
85
     * @return array
86
     */
87
    public function normaliseData() : array
88
    {
89
        $fields = $this->getOwner()->config()->get('verifiable_fields');
90
        $verifiable = [];
91
92
        foreach ($fields as $field) {
93
            $verifiable[] = (string) $this->getOwner()->getField($field);
94
        }
95
96
        return $verifiable;
97
    }
98
99
    /**
100
     * Central to the whole package, this method is passed an array of fields
101
     * and their values, hashes them and will check that a chainpoint proof
102
     * exists in the local database. If unsuccessful, we return false.
103
     * Otherwise, we continue and consult the backend for the same proof. If one
104
     * is found both locally and in the backed, then the supplied data is said to
105
     * be verified. Note: See the $strict param to skip the local proof check.
106
     *
107
     * @param  array  $data   An array of data to verify against the current backend.
108
     * @param  bool   $strict True by default; That-is both the local database and
109
     *                        the backend are consulted for a valid proof. If set
110
     *                        to false, we bypass the local check and just consult
111
     *                        the backend directly.
112
     * @return bool           True if the backend verifies the proof, false otherwise.
113
     */
114
    public function verify(array $data, bool $strict = true) : bool
0 ignored issues
show
Unused Code introduced by
The parameter $strict is not used and could be removed. ( Ignorable by Annotation )

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

114
    public function verify(array $data, /** @scrutinizer ignore-unused */ bool $strict = true) : bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
115
    {
116
        $hash = $this->verifiableService->hash($data);
0 ignored issues
show
Bug introduced by
The property verifiableService does not exist on PhpTek\Verifiable\Verify\VerifiableExtension. Did you mean verifiable_fields?
Loading history...
117
        $proof = $this->getOwner()->dbObject('Proof');
118
119
        // 1). Get the locally stored chainpoint proof
120
        if (!$proof->match($hash)) {
121
            return false;
122
        }
123
124
        // 2). Send the local proof to the backend for verification
125
        if (!$this->verificationService->verify($proof)) {
0 ignored issues
show
Bug Best Practice introduced by
The property verificationService does not exist on PhpTek\Verifiable\Verify\VerifiableExtension. Did you maybe forget to declare it?
Loading history...
126
            return false;
127
        }
128
129
        // 3). Verification complete
130
        return true;
131
    }
132
133
}
134