Completed
Push — master ( 6948b2...1bd4d3 )
by Marcel
02:23
created

Verifier::setMonitor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
namespace UMA\Psr\Http\Message\HMAC;
4
5
use Psr\Http\Message\MessageInterface;
6
use UMA\Psr\Http\Message\Internal\HashCalculator;
7
use UMA\Psr\Http\Message\Internal\HeaderValidator;
8
use UMA\Psr\Http\Message\Monitor\BlindMonitor;
9
use UMA\Psr\Http\Message\Monitor\MonitorInterface;
10
use UMA\Psr\Http\Message\Serializer\MessageSerializer;
11
12
class Verifier
13
{
14
    /**
15
     * @var HashCalculator
16
     */
17
    private $calculator;
18
19
    /**
20
     * @var null|int
21
     */
22
    private $maxDelay = null;
23
24
    /**
25
     * @var MonitorInterface
26
     */
27
    private $monitor;
28
29
    /**
30
     * @var HeaderValidator
31
     */
32
    private $validator;
33
34 93
    public function __construct()
35
    {
36 93
        $this->calculator = new HashCalculator();
37 93
        $this->monitor = new BlindMonitor();
38 93
        $this->validator = (new HeaderValidator())
39 93
            ->addRule(Specification::AUTH_HEADER, Specification::AUTH_REGEXP)
40 93
            ->addRule(Specification::SIGN_HEADER, Specification::SIGN_REGEXP);
41 93
    }
42
43
    /**
44
     * @param int $maxDelay
45
     *
46
     * @return Verifier
47
     */
48 1
    public function setMaximumDelay($maxDelay)
49
    {
50 1
        $this->maxDelay = $maxDelay;
51
52 1
        return $this;
53
    }
54
55
    /**
56
     * @param MonitorInterface $monitor
57
     *
58
     * @return Verifier
59
     */
60 1
    public function setMonitor(MonitorInterface $monitor)
61
    {
62 1
        $this->monitor = $monitor;
63
64 1
        return $this;
65
    }
66
67
    /**
68
     * @param MessageInterface $message
69
     * @param string           $secret
70
     *
71
     * @return bool Signature verification outcome.
72
     *
73
     * @throws \InvalidArgumentException When $message is an implementation of
74
     *                                   MessageInterface that cannot be
75
     *                                   serialized and thus neither verified.
76
     */
77 93
    public function verify(MessageInterface $message, $secret)
78
    {
79 93
        if (false === $matches = $this->validator->conforms($message)) {
80 91
            return false;
81
        }
82
83 72
        $clientSideSignature = $matches[Specification::AUTH_HEADER][1];
84
85 72
        $serverSideSignature = $this->calculator
86 72
            ->hmac(MessageSerializer::serialize($this->withoutUnsignedHeaders($message)), $secret);
87
88
        if (
89 72
            !hash_equals($serverSideSignature, $clientSideSignature) ||
90 72
            $this->delayed($message) ||
91 72
            $this->monitor->seen($message)
92 72
        ) {
93 72
            return false;
94
        }
95
96 72
        return true;
97
    }
98
99
    /**
100
     * @param MessageInterface $message
101
     *
102
     * @return MessageInterface
103
     */
104 72
    private function withoutUnsignedHeaders(MessageInterface $message)
105
    {
106 72
        $signedHeaders = array_filter(explode(',', $message->getHeaderLine(Specification::SIGN_HEADER)));
107
108 72
        foreach ($message->getHeaders() as $name => $value) {
109 72
            if (!in_array(mb_strtolower($name), $signedHeaders)) {
110 72
                $message = $message->withoutHeader($name);
111 72
            }
112 72
        }
113
114 72
        return $message;
115
    }
116
117
    /**
118
     * @param MessageInterface $message
119
     *
120
     * @return bool
121
     */
122 72
    private function delayed(MessageInterface $message)
123
    {
124 72
        $currentTimestamp = (new \DateTime('now', new \DateTimeZone('GMT')))->getTimestamp();
125 72
        $messageTimestamp = (new \DateTime($message->getHeaderLine(Specification::DATE_HEADER)))->getTimestamp();
126
127 72
        return is_int($this->maxDelay) && $currentTimestamp - $messageTimestamp > $this->maxDelay;
128
    }
129
}
130