Failed Conditions
Push — v7 ( 69552d...3dace9 )
by Florent
03:23
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
        $header = $checker->supportedHeader();
60
        if (array_key_exists($header, $this->checkers)) {
61
            throw new \InvalidArgumentException(sprintf('The header checker "%s" is already supported.', $header));
62
        }
63
64
        $this->checkers[$header] = $checker;
65
66
        return $this;
67
    }
68
69
    /**
70
     * @param JWTInterface $jwt
71
     * @param int          $component
72
     */
73
    public function check(JWTInterface $jwt, int $component)
74
    {
75
        switch (true) {
76
            case $jwt instanceof JWS:
77
                $this->checkJWS($jwt, $component);
78
79
                break;
80
            case $jwt instanceof JWE:
81
                $this->checkJWE($jwt, $component);
82
83
                break;
84
            default:
85
                throw new \InvalidArgumentException('Unsupported argument');
86
        }
87
    }
88
89
    /**
90
     * @param JWS $jws
91
     * @param int $signature
92
     */
93
    public function checkJWS(JWS $jws, int $signature)
94
    {
95
        if ($signature > $jws->countSignatures()) {
96
            throw new \InvalidArgumentException('Unknown signature index.');
97
        }
98
        $protected = $jws->getSignature($signature)->getProtectedHeaders();
99
        $headers = $jws->getSignature($signature)->getHeaders();
100
        $this->checkDuplicatedHeaderParameters($protected, $headers);
101
        $this->checkHeaders($protected, $headers);
102
    }
103
104
    /**
105
     * @param JWE $jwe
106
     * @param int $recipient
107
     */
108
    public function checkJWE(JWE $jwe, int $recipient)
109
    {
110
        if ($recipient > $jwe->countRecipients()) {
111
            throw new \InvalidArgumentException('Unknown recipient index.');
112
        }
113
        $protected = $jwe->getSharedProtectedHeaders();
114
        $headers = $jwe->getSharedHeaders();
115
        $recipient = $jwe->getRecipient($recipient)->getHeaders();
116
        $this->checkDuplicatedHeaderParameters($protected, $headers);
117
        $this->checkDuplicatedHeaderParameters($protected, $recipient);
118
        $this->checkDuplicatedHeaderParameters($headers, $recipient);
119
        $unprotected = array_merge(
120
            $headers,
121
            $recipient
122
        );
123
        $this->checkHeaders($protected, $unprotected);
124
    }
125
126
    /**
127
     * @param array $header1
128
     * @param array $header2
129
     */
130
    private function checkDuplicatedHeaderParameters(array $header1, array $header2)
131
    {
132
        $inter = array_intersect_key($header1, $header2);
133
        if (!empty($inter)) {
134
            throw new \InvalidArgumentException(sprintf('The header contains duplicated entries: %s.', implode(', ', array_keys($inter))));
135
        }
136
    }
137
138
    /**
139
     * @param array $protected
140
     * @param array $headers
141
     */
142
    private function checkHeaders(array $protected, array $headers)
143
    {
144
        $checkedHeaders = [];
145
        foreach ($this->checkers as $header => $checker) {
146
            if ($checker->protectedHeaderOnly()) {
147
                if (array_key_exists($header, $protected)) {
148
                    $checker->checkHeader($protected[$header]);
149
                    $checkedHeaders[] = $header;
150
                } else {
151
                    throw new \InvalidArgumentException(sprintf('The header "%s" must be protected.', $header));
152
                }
153
            } else {
154
                if (array_key_exists($header, $protected)) {
155
                    $checker->checkHeader($protected[$header]);
156
                    $checkedHeaders[] = $header;
157
                } elseif (array_key_exists($header, $headers)) {
158
                    $checker->checkHeader($headers[$header]);
159
                    $checkedHeaders[] = $header;
160
                }
161
            }
162
        }
163
164
        if (array_key_exists('crit', $protected)) {
165
            if (!is_array($protected['crit'])) {
166
                throw new \InvalidArgumentException('The header "crit" mus be a list of header parameters.');
167
            }
168
            $diff = array_diff($protected['crit'], $checkedHeaders);
169
            if (!empty($diff)) {
170
                throw new \InvalidArgumentException(sprintf('One or more headers are marked as critical, but they are missing or have not been checked: %s.', implode(', ', array_values($diff))));
171
            }
172
        } elseif (array_key_exists('crit', $headers)) {
173
            throw new \InvalidArgumentException('The header parameter "crit" must be protected.');
174
        }
175
    }
176
}
177