1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace JWX\JWK; |
4
|
|
|
|
5
|
|
|
use JWX\JWK\Parameter\RegisteredJWKParameter; |
6
|
|
|
|
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* Represents a JWK set structure. |
10
|
|
|
* |
11
|
|
|
* @link https://tools.ietf.org/html/rfc7517#section-5 |
12
|
|
|
*/ |
13
|
|
|
class JWKSet implements \Countable, \IteratorAggregate |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* JWK objects. |
17
|
|
|
* |
18
|
|
|
* @var JWK[] $_jwks |
19
|
|
|
*/ |
20
|
|
|
protected $_jwks; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Additional members. |
24
|
|
|
* |
25
|
|
|
* @var array $_additional |
26
|
|
|
*/ |
27
|
|
|
protected $_additional; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* JWK mappings. |
31
|
|
|
* |
32
|
|
|
* @var array |
33
|
|
|
*/ |
34
|
|
|
private $_mappings = array(); |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Constructor |
38
|
|
|
* |
39
|
|
|
* @param JWK ...$jwks |
40
|
|
|
*/ |
41
|
6 |
|
public function __construct(JWK ...$jwks) { |
42
|
6 |
|
$this->_jwks = $jwks; |
43
|
6 |
|
$this->_additional = array(); |
44
|
6 |
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Initialize from an array representing a JSON object. |
48
|
|
|
* |
49
|
|
|
* @param array $members |
50
|
|
|
* @throws \UnexpectedValueException |
51
|
|
|
* @return self |
52
|
|
|
*/ |
53
|
3 |
|
public static function fromArray(array $members) { |
54
|
3 |
|
if (!isset($members["keys"]) || !is_array($members["keys"])) { |
55
|
1 |
|
throw new \UnexpectedValueException( |
56
|
1 |
|
"JWK Set must have a 'keys' member."); |
57
|
|
|
} |
58
|
2 |
|
$jwks = array_map( |
59
|
2 |
|
function ($jwkdata) { |
60
|
2 |
|
return JWK::fromArray($jwkdata); |
61
|
2 |
|
}, $members["keys"]); |
62
|
2 |
|
unset($members["keys"]); |
63
|
2 |
|
$obj = new self(...$jwks); |
64
|
2 |
|
$obj->_additional = $members; |
65
|
2 |
|
return $obj; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Initialize from a JSON string. |
70
|
|
|
* |
71
|
|
|
* @param string $json |
72
|
|
|
* @throws \UnexpectedValueException |
73
|
|
|
* @return self |
74
|
|
|
*/ |
75
|
3 |
View Code Duplication |
public static function fromJSON($json) { |
|
|
|
|
76
|
3 |
|
$members = json_decode($json, true, 32, JSON_BIGINT_AS_STRING); |
77
|
3 |
|
if (!is_array($members)) { |
78
|
1 |
|
throw new \UnexpectedValueException("Invalid JSON."); |
79
|
|
|
} |
80
|
2 |
|
return self::fromArray($members); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Get all JWK's in a set. |
85
|
|
|
* |
86
|
|
|
* @return JWK[] |
87
|
|
|
*/ |
88
|
1 |
|
public function keys() { |
89
|
1 |
|
return $this->_jwks; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Get JWK by key ID. |
94
|
|
|
* |
95
|
|
|
* @param string $id |
96
|
|
|
* @return JWK|null Null if not found |
97
|
|
|
*/ |
98
|
8 |
|
protected function _getKeyByID($id) { |
99
|
8 |
|
$map = $this->_getMapping(RegisteredJWKParameter::PARAM_KEY_ID); |
100
|
8 |
|
return isset($map[$id]) ? $map[$id] : null; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Check whether set has a JWK with a given key ID. |
105
|
|
|
* |
106
|
|
|
* @param string $id |
107
|
|
|
* @return bool |
108
|
|
|
*/ |
109
|
4 |
|
public function hasKeyID($id) { |
110
|
4 |
|
return $this->_getKeyByID($id) !== null; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Get a JWK by a key ID. |
115
|
|
|
* |
116
|
|
|
* @param string $id |
117
|
|
|
* @throws \LogicException |
118
|
|
|
* @return JWK |
119
|
|
|
*/ |
120
|
5 |
|
public function keyByID($id) { |
121
|
5 |
|
$jwk = $this->_getKeyByID($id); |
122
|
5 |
|
if (!$jwk) { |
123
|
1 |
|
throw new \LogicException("No key ID $id."); |
124
|
|
|
} |
125
|
4 |
|
return $jwk; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Get mapping from parameter values of given parameter name to JWK. |
130
|
|
|
* |
131
|
|
|
* Later duplicate value shall override earlier JWK. |
132
|
|
|
* |
133
|
|
|
* @param string $name Parameter name |
134
|
|
|
* @return array |
135
|
|
|
*/ |
136
|
8 |
|
protected function _getMapping($name) { |
137
|
8 |
|
if (!isset($this->_mappings[$name])) { |
138
|
4 |
|
$mapping = array(); |
139
|
4 |
|
foreach ($this->_jwks as $jwk) { |
140
|
4 |
|
if ($jwk->has($name)) { |
141
|
4 |
|
$key = (string) $jwk->get($name)->value(); |
142
|
4 |
|
$mapping[$key] = $jwk; |
143
|
4 |
|
} |
144
|
4 |
|
} |
145
|
4 |
|
$this->_mappings[$name] = $mapping; |
146
|
4 |
|
} |
147
|
8 |
|
return $this->_mappings[$name]; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Convert to JSON. |
152
|
|
|
* |
153
|
|
|
* @return string |
154
|
|
|
*/ |
155
|
1 |
|
public function toJSON() { |
156
|
1 |
|
$data = $this->_additional; |
157
|
1 |
|
$data["keys"] = $this->_jwks; |
158
|
1 |
|
return json_encode((object) $data, JSON_UNESCAPED_SLASHES); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Get the number of keys. |
163
|
|
|
* |
164
|
|
|
* @see Countable::count() |
165
|
|
|
*/ |
166
|
2 |
|
public function count() { |
167
|
2 |
|
return count($this->_jwks); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Get iterator for JWK objects. |
172
|
|
|
* |
173
|
|
|
* @see IteratorAggregate::getIterator() |
174
|
|
|
* @return \ArrayIterator |
175
|
|
|
*/ |
176
|
1 |
|
public function getIterator() { |
177
|
1 |
|
return new \ArrayIterator($this->_jwks); |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.