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