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