Failed Conditions
Push — v7 ( f4c774...0a4d93 )
by Florent
61:29 queued 49:09
created

HeaderCheckerManager::checkHeaders()   D

Complexity

Conditions 10
Paths 26

Size

Total Lines 34
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 24
nc 26
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\Checker;
15
16
use Jose\Component\Core\JWTInterface;
17
use Jose\Component\Encryption\JWE;
18
use Jose\Component\Signature\JWS;
19
20
/**
21
 * Class HeaderCheckerManager.
22
 */
23
final class HeaderCheckerManager
24
{
25
    /**
26
     * @var HeaderCheckerInterface[]
27
     */
28
    private $checkers = [];
29
30
    /**
31
     * HeaderCheckerManager constructor.
32
     *
33
     * @param HeaderCheckerInterface[] $checkers
34
     */
35
    private function __construct(array $checkers)
36
    {
37
        foreach ($checkers as $checker) {
38
            $this->add($checker);
39
        }
40
    }
41
42
    /**
43
     * @param HeaderCheckerInterface[] $checkers
44
     *
45
     * @return HeaderCheckerManager
46
     */
47
    public static function create(array $checkers): HeaderCheckerManager
48
    {
49
        return new self($checkers);
50
    }
51
52
    /**
53
     * @param HeaderCheckerInterface $checker
54
     *
55
     * @return HeaderCheckerManager
56
     */
57
    private function add(HeaderCheckerInterface $checker): HeaderCheckerManager
58
    {
59
        $this->checkers[$checker->supportedHeader()] = $checker;
60
61
        return $this;
62
    }
63
64
    /**
65
     * @param JWTInterface $jwt
66
     * @param int          $component
67
     */
68
    public function check(JWTInterface $jwt, int $component)
69
    {
70
        switch (true) {
71
            case $jwt instanceof JWS:
72
                $this->checkJWS($jwt, $component);
73
74
                break;
75
            case $jwt instanceof JWE:
76
                $this->checkJWE($jwt, $component);
77
78
                break;
79
            default:
80
                throw new \InvalidArgumentException('Unsupported argument');
81
        }
82
    }
83
84
    /**
85
     * @param JWS $jws
86
     * @param int $signature
87
     */
88
    public function checkJWS(JWS $jws, int $signature)
89
    {
90
        if ($signature > $jws->countSignatures()) {
91
            throw new \InvalidArgumentException('Unknown signature index.');
92
        }
93
        $protected = $jws->getSignature($signature)->getProtectedHeaders();
94
        $headers = $jws->getSignature($signature)->getHeaders();
95
        $this->checkDuplicatedHeaderParameters($protected, $headers);
96
        $this->checkHeaders($protected, $headers);
97
    }
98
99
    /**
100
     * @param JWE $jwe
101
     * @param int $recipient
102
     */
103
    public function checkJWE(JWE $jwe, int $recipient)
104
    {
105
        if ($recipient > $jwe->countRecipients()) {
106
            throw new \InvalidArgumentException('Unknown recipient index.');
107
        }
108
        $protected = $jwe->getSharedProtectedHeaders();
109
        $headers = $jwe->getSharedHeaders();
110
        $recipient = $jwe->getRecipient($recipient)->getHeaders();
111
        $this->checkDuplicatedHeaderParameters($protected, $headers);
112
        $this->checkDuplicatedHeaderParameters($protected, $recipient);
113
        $this->checkDuplicatedHeaderParameters($headers, $recipient);
114
        $unprotected = array_merge(
115
            $headers,
116
            $recipient
117
        );
118
        $this->checkHeaders($protected, $unprotected);
119
    }
120
121
    /**
122
     * @param array $header1
123
     * @param array $header2
124
     */
125
    private function checkDuplicatedHeaderParameters(array $header1, array $header2)
126
    {
127
        $inter = array_intersect_key($header1, $header2);
128
        if (!empty($inter)) {
129
            throw new \InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', json_encode(array_keys($inter))));
130
        }
131
    }
132
133
    /**
134
     * @param array $protected
135
     * @param array $headers
136
     */
137
    private function checkHeaders(array $protected, array $headers)
138
    {
139
        $checkedHeaders = [];
140
        foreach ($this->checkers as $header => $checker) {
141
            if ($checker->protectedHeaderOnly()) {
142
                if (array_key_exists($header, $protected)) {
143
                    $checker->checkHeader($protected[$header]);
144
                    $checkedHeaders[] = $header;
145
                } else {
146
                    throw new \InvalidArgumentException(sprintf('The header "%s" must be protected.', $header));
147
                }
148
            } else {
149
                if (array_key_exists($header, $protected)) {
150
                    $checker->checkHeader($protected[$header]);
151
                    $checkedHeaders[] = $header;
152
                } elseif (array_key_exists($header, $headers)) {
153
                    $checker->checkHeader($headers[$header]);
154
                    $checkedHeaders[] = $header;
155
                }
156
            }
157
        }
158
159
        if (array_key_exists('crit', $protected)) {
160
            if (!is_array($protected['crit'])) {
161
                throw new \InvalidArgumentException('The header "crit" mus be a list of header parameters.');
162
            }
163
            $diff = array_diff($protected['crit'], $checkedHeaders);
164
            if (!empty($diff)) {
165
                throw new \InvalidArgumentException(sprintf('One or more headers are marked as critical, but they are missing or have not been checked: %s.', json_encode(array_values($diff))));
166
            }
167
        } elseif (array_key_exists('crit', $headers)) {
168
            throw new \InvalidArgumentException('The header parameter "crit" must be protected.');
169
        }
170
    }
171
}
172