1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace JWX\JWK; |
4
|
|
|
|
5
|
|
|
use JWX\JWK\Parameter\JWKParameter; |
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
|
55 |
|
public function __construct(JWK ...$jwks) { |
42
|
55 |
|
$this->_jwks = $jwks; |
43
|
55 |
|
$this->_additional = array(); |
44
|
55 |
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Reset internal cache variables on clone. |
48
|
|
|
*/ |
49
|
2 |
|
public function __clone() { |
50
|
2 |
|
$this->_mappings = array(); |
51
|
2 |
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Initialize from an array representing a JSON object. |
55
|
|
|
* |
56
|
|
|
* @param array $members |
57
|
|
|
* @throws \UnexpectedValueException |
58
|
|
|
* @return self |
59
|
|
|
*/ |
60
|
3 |
|
public static function fromArray(array $members) { |
61
|
3 |
|
if (!isset($members["keys"]) || !is_array($members["keys"])) { |
62
|
1 |
|
throw new \UnexpectedValueException( |
63
|
1 |
|
"JWK Set must have a 'keys' member."); |
64
|
|
|
} |
65
|
2 |
|
$jwks = array_map( |
66
|
|
|
function ($jwkdata) { |
67
|
2 |
|
return JWK::fromArray($jwkdata); |
68
|
2 |
|
}, $members["keys"]); |
69
|
2 |
|
unset($members["keys"]); |
70
|
2 |
|
$obj = new self(...$jwks); |
71
|
2 |
|
$obj->_additional = $members; |
72
|
2 |
|
return $obj; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Initialize from a JSON string. |
77
|
|
|
* |
78
|
|
|
* @param string $json |
79
|
|
|
* @throws \UnexpectedValueException |
80
|
|
|
* @return self |
81
|
|
|
*/ |
82
|
3 |
View Code Duplication |
public static function fromJSON($json) { |
|
|
|
|
83
|
3 |
|
$members = json_decode($json, true, 32, JSON_BIGINT_AS_STRING); |
84
|
3 |
|
if (!is_array($members)) { |
85
|
1 |
|
throw new \UnexpectedValueException("Invalid JSON."); |
86
|
|
|
} |
87
|
2 |
|
return self::fromArray($members); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Get self with keys added. |
92
|
|
|
* |
93
|
|
|
* @param JWK ...$keys JWK objects |
94
|
|
|
* @return self |
95
|
|
|
*/ |
96
|
1 |
|
public function withKeys(JWK ...$keys) { |
97
|
1 |
|
$obj = clone $this; |
98
|
1 |
|
$obj->_jwks = array_merge($obj->_jwks, $keys); |
99
|
1 |
|
return $obj; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Get all JWK's in a set. |
104
|
|
|
* |
105
|
|
|
* @return JWK[] |
106
|
|
|
*/ |
107
|
1 |
|
public function keys() { |
108
|
1 |
|
return $this->_jwks; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Get the first JWK in the set. |
113
|
|
|
* |
114
|
|
|
* @throws \LogicException |
115
|
|
|
* @return JWK |
116
|
|
|
*/ |
117
|
5 |
|
public function first() { |
118
|
5 |
|
if (!count($this->_jwks)) { |
119
|
1 |
|
throw new \LogicException("No keys."); |
120
|
|
|
} |
121
|
4 |
|
return $this->_jwks[0]; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Get JWK by key ID. |
126
|
|
|
* |
127
|
|
|
* @param string $id |
128
|
|
|
* @return JWK|null Null if not found |
129
|
|
|
*/ |
130
|
18 |
|
protected function _getKeyByID($id) { |
131
|
18 |
|
$map = $this->_getMapping(JWKParameter::PARAM_KEY_ID); |
132
|
18 |
|
return isset($map[$id]) ? $map[$id] : null; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Check whether set has a JWK with a given key ID. |
137
|
|
|
* |
138
|
|
|
* @param string $id |
139
|
|
|
* @return bool |
140
|
|
|
*/ |
141
|
13 |
|
public function hasKeyID($id) { |
142
|
13 |
|
return $this->_getKeyByID($id) !== null; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Get a JWK by a key ID. |
147
|
|
|
* |
148
|
|
|
* @param string $id |
149
|
|
|
* @throws \LogicException |
150
|
|
|
* @return JWK |
151
|
|
|
*/ |
152
|
12 |
|
public function keyByID($id) { |
153
|
12 |
|
$jwk = $this->_getKeyByID($id); |
154
|
12 |
|
if (!$jwk) { |
155
|
1 |
|
throw new \LogicException("No key ID $id."); |
156
|
|
|
} |
157
|
11 |
|
return $jwk; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Get mapping from parameter values of given parameter name to JWK. |
162
|
|
|
* |
163
|
|
|
* Later duplicate value shall override earlier JWK. |
164
|
|
|
* |
165
|
|
|
* @param string $name Parameter name |
166
|
|
|
* @return array |
167
|
|
|
*/ |
168
|
18 |
|
protected function _getMapping($name) { |
169
|
18 |
|
if (!isset($this->_mappings[$name])) { |
170
|
14 |
|
$mapping = array(); |
171
|
14 |
|
foreach ($this->_jwks as $jwk) { |
172
|
14 |
|
if ($jwk->has($name)) { |
173
|
11 |
|
$key = (string) $jwk->get($name)->value(); |
174
|
11 |
|
$mapping[$key] = $jwk; |
175
|
11 |
|
} |
176
|
14 |
|
} |
177
|
14 |
|
$this->_mappings[$name] = $mapping; |
178
|
14 |
|
} |
179
|
18 |
|
return $this->_mappings[$name]; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Convert to array. |
184
|
|
|
* |
185
|
|
|
* @return array |
186
|
|
|
*/ |
187
|
1 |
|
public function toArray() { |
188
|
1 |
|
$data = $this->_additional; |
189
|
1 |
|
$data["keys"] = array_map( |
190
|
1 |
|
function (JWK $jwk) { |
191
|
1 |
|
return $jwk->toArray(); |
192
|
1 |
|
}, $this->_jwks); |
193
|
1 |
|
return $data; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Convert to JSON. |
198
|
|
|
* |
199
|
|
|
* @return string |
200
|
|
|
*/ |
201
|
1 |
|
public function toJSON() { |
202
|
1 |
|
return json_encode((object) $this->toArray(), JSON_UNESCAPED_SLASHES); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Get the number of keys. |
207
|
|
|
* |
208
|
|
|
* @see Countable::count() |
209
|
|
|
*/ |
210
|
14 |
|
public function count() { |
211
|
14 |
|
return count($this->_jwks); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* Get iterator for JWK objects. |
216
|
|
|
* |
217
|
|
|
* @see IteratorAggregate::getIterator() |
218
|
|
|
* @return \ArrayIterator |
219
|
|
|
*/ |
220
|
1 |
|
public function getIterator() { |
221
|
1 |
|
return new \ArrayIterator($this->_jwks); |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
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.