Passed
Push — master ( 7f61ea...38db0f )
by Russell
13:01
created

VerifiableExtension::normaliseData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 9.4285
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\Core\DataExtension;
0 ignored issues
show
Bug introduced by
The type SilverStripe\Core\DataExtension was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use PhpTek\Verifiable\Verify\ChainpointProof;
12
13
/**
14
 * By attaching to any {@link DataObject} subclass, including {@link SiteTree}
15
 * subclasses, and declaring a $verifiable_fields array in YML config, all subsequent
16
 * database writes will be passed through here via onAfterWrite();
17
 *
18
 * This {@link DataExtension} also provides a single field to which all verifiable
19
 * chainpoint proofs are stored in a queryable JSON-aware field.
20
 *
21
 * @todo Flag to API users that confirmation has not yet occurred.
22
 * @todo Store the hash function used and if subsequent verifications
23
 *       fail because differing hash functions are used, throw an exception
24
 */
25
class VerifiableExtension extends DataExtension
26
{
27
    /**
28
     * Declare a field on this owner where all chainpoint proofs should be stored.
29
     *
30
     * @var array
31
     * @config
32
     */
33
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
34
        'Proof' => ChainpointProof::class,
35
    ];
36
37
    /**
38
     * These field's values will be hashed and committed to the current backend.
39
     *
40
     * @var array
41
     * @config
42
     */
43
    private static $verifiable_fields = [];
0 ignored issues
show
introduced by
The private property $verifiable_fields is not used, and could be removed.
Loading history...
44
45
    /**
46
     * After each write, the desired field's data is compiled into a string
47
     * and submitted as a hash to the currently configured backend.
48
     *
49
     * Once written, we poll the backend to receive the chainpoint proof
50
     * which we'll need for subsequent verification checks made against the
51
     * backend.
52
     *
53
     * @return void
54
     */
55
    public function onAfterWrite()
56
    {
57
        parent::onAfterWrite();
58
59
        $verifiable = $this->normaliseData();
60
61
        if (count($verifiable)) {
0 ignored issues
show
Bug introduced by
$verifiable of type string is incompatible with the type Countable|array expected by parameter $var of count(). ( Ignorable by Annotation )

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

61
        if (count(/** @scrutinizer ignore-type */ $verifiable)) {
Loading history...
62
            $this->verifiableService->write($verifiable);
63
            $this->verifiableService->queuePing($this->getOwner());
64
        }
65
    }
66
67
    /**
68
     * Normalise this model's data such that it is best suited to being hashed.
69
     *
70
     * @return array
71
     */
72
    public function normaliseData() : string
73
    {
74
        $fields = $this->getOwner()->config()->get('verifiable_fields');
75
        $verifiable = [];
76
77
        foreach ($fields as $field) {
78
            $verifiable[] = (string) $this->getOwner()->getField($field);
79
        }
80
81
        return $verifiable;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $verifiable returns the type array|string[] which is incompatible with the type-hinted return string.
Loading history...
82
    }
83
84
85
    /**
86
     * Central to the whole package, this method is passed an array of fields
87
     * and their values, hashes them and will check that a chainpoint proof
88
     * exists in the local database. If unsuccessful, we return false.
89
     * Otherwise, we continue and consult the backend for the same proof. If one
90
     * is found both locally and in the backed, then the supplied data is said to
91
     * be verified. Note: See the $strict param to skip the local proof check.
92
     *
93
     * @param  array  $data   An array of data to verify against the current backend.
94
     * @param  bool   $strict True by default; That-is both the local database and
95
     *                        the backend are consulted for a valid proof. If set
96
     *                        to false, we bypass the local check and just consult
97
     *                        the backend directly.
98
     * @return bool           True if the backend verifies the proof, false otherwise.
99
     */
100
    public function verify(array $data, bool $strict = true) : bool
101
    {
102
        $hash = $this->verifiableService->hash($data);
103
        $proof = $this->getOwner()->dbObject('Proof');
104
105
        // 1). Get the locally stored chainpoint proof
106
        if (!$proof->match($hash)) {
107
            return false;
108
        }
109
110
        // 2). Send the local proof to the backend for verification
111
        if (!$this->verificationService->verify($proof)) {
112
            return false;
113
        }
114
115
        // 3). Verification complete
116
        return true;
117
    }
118
119
}
120
121