PluginProbe ʕ •ᴥ•ʔ
Advanced Access Manager – Access Governance for WordPress / trunk
Advanced Access Manager – Access Governance for WordPress vtrunk
6.8.4 6.8.5 6.9.0 6.9.1 6.9.10 6.9.11 6.9.12 6.9.13 6.9.14 6.9.15 6.9.16 6.9.17 6.9.18 6.9.19 6.9.2 6.9.20 6.9.21 6.9.22 6.9.23 6.9.24 6.9.25 6.9.26 6.9.27 6.9.28 6.9.29 6.9.3 6.9.30 6.9.31 6.9.32 6.9.33 6.9.34 6.9.35 6.9.36 6.9.37 6.9.38 6.9.39 6.9.4 6.9.41 6.9.42 6.9.43 6.9.44 6.9.45 6.9.46 6.9.47 6.9.48 6.9.49 6.9.5 6.9.51 6.9.6 6.9.7 6.9.8 6.9.9 7.0.0 7.0.0-alpha.6 7.0.0-alpha.7 7.0.0-beta.1 7.0.0-rc1 7.0.0-rc2 7.0.0-rc3 7.0.1 7.0.10 7.0.11 7.0.2 7.0.3 7.0.4 7.0.5 7.0.6 7.0.7 7.0.8 7.0.9 7.1.0 7.1.1 trunk 3.0 4.0 4.0.1 4.1 4.2 4.3 4.4 4.4.1 4.5 4.6 4.6.1 4.6.2 4.7 4.7.1 4.7.2 4.7.5 4.7.6 4.8 4.8.1 4.9 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 4.9.5.1 4.9.5.2 5.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1 5.1.1 5.10 5.11 5.2 5.2.1 5.2.5 5.2.6 5.2.7 5.3 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.4 5.4.1 5.4.2 5.4.3 5.4.3.1 5.4.3.2 5.5 5.5.1 5.5.2 5.6 5.6.1 5.6.1.1 5.7 5.7.1 5.7.2 5.7.3 5.8 5.8.1 5.8.2 5.8.3 5.9 5.9.1 5.9.1.1 5.9.2 5.9.2.1 5.9.3 5.9.4 5.9.5 5.9.6 5.9.6.1 5.9.6.2 5.9.6.3 5.9.7 5.9.7.1 5.9.7.2 5.9.7.3 5.9.8 5.9.8.1 5.9.9 5.9.9.1 6.0.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.1.0 6.1.1 6.2.0 6.2.1 6.2.2 6.3.0 6.3.1 6.3.2 6.3.3 6.4.0 6.4.1 6.4.2 6.4.3 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.6.0 6.6.1 6.6.2 6.6.3 6.6.4 6.7.0 6.7.1 6.7.2 6.7.3 6.7.4 6.7.5 6.7.6 6.7.7 6.7.8 6.7.9 6.8.0 6.8.1 6.8.2 6.8.3
advanced-access-manager / application / Restful / Jwt.php
advanced-access-manager / application / Restful Last commit date
AccessDeniedRedirect.php 1 year ago AdminToolbar.php 1 year ago ApiRoute.php 1 year ago BackendMenu.php 1 year ago BackwardCompatibility.php 1 year ago Capability.php 1 year ago Configs.php 5 months ago Content.php 1 year ago Identity.php 1 year ago Jwt.php 5 months ago LoginRedirect.php 1 year ago LogoutRedirect.php 1 year ago Metabox.php 1 year ago Mu.php 1 year ago NotFoundRedirect.php 1 year ago Policies.php 1 year ago Roles.php 1 year ago SecureLogin.php 1 year ago SecurityAudit.php 1 year ago ServiceTrait.php 1 year ago Settings.php 1 year ago Urls.php 1 year ago Users.php 1 year ago Widgets.php 1 year ago
Jwt.php
474 lines
1 <?php
2
3 /**
4 * ======================================================================
5 * LICENSE: This file is subject to the terms and conditions defined in *
6 * file 'license.txt', which is part of this source code package. *
7 * ======================================================================
8 */
9
10 /**
11 * RESTful API for the JWT Token service
12 *
13 * @package AAM
14 * @version 7.0.0
15 */
16 class AAM_Restful_Jwt
17 {
18
19 use AAM_Restful_ServiceTrait;
20
21 /**
22 * Necessary permissions to access endpoint
23 *
24 * @version 7.1.0
25 */
26 const PERMISSIONS = [
27 'aam_manager',
28 AAM_Backend_Feature_Main_Jwt::ACCESS_CAPABILITY
29 ];
30
31 /**
32 * Constructor
33 *
34 * @return void
35 * @access protected
36 *
37 * @version 7.0.0
38 */
39 protected function __construct()
40 {
41 // Register API endpoint
42 add_action('rest_api_init', function() {
43 // Get the list of tokens
44 $this->_register_route('/jwts', [
45 'methods' => WP_REST_Server::READABLE,
46 'callback' => [ $this, 'get_tokens' ],
47 'args' => [
48 'fields' => array(
49 'description' => 'List of additional fields to return',
50 'type' => 'string',
51 'validate_callback' => function ($value) {
52 return $this->_validate_fields_input($value);
53 }
54 )
55 ]
56 ], self::PERMISSIONS, [ AAM_Framework_Type_AccessLevel::USER ]);
57
58 // Create a new jwt token
59 $this->_register_route('/jwts', [
60 'methods' => WP_REST_Server::CREATABLE,
61 'callback' => array($this, 'create_token'),
62 'args' => array(
63 'expires_at' => array(
64 'description' => 'Well formatted date-time when the token expires',
65 'type' => 'date-time'
66 ),
67 'expires_in' => array(
68 'description' => 'Relative datetime format',
69 'type' => 'string',
70 'validate_callback' => function ($value) {
71 return $this->_validate_expires_in_input($value);
72 }
73 ),
74 'is_refreshable' => array(
75 'description' => 'Wether issued JWT is refreshable',
76 'type' => 'boolean',
77 'default' => false
78 ),
79 'is_revocable' => array(
80 'description' => 'Wether issued JWT is revocable',
81 'type' => 'boolean',
82 'default' => true
83 ),
84 'additional_claims' => array(
85 'description' => 'Any additional claims to include in the token',
86 'type' => [ 'string', 'object' ],
87 'default' => []
88 ),
89 'description' => array(
90 'description' => 'JWT token description',
91 'type' => 'string',
92 'required' => false
93 ),
94 'fields' => array(
95 'description' => 'List of additional fields to return',
96 'type' => 'string',
97 'validate_callback' => function ($value) {
98 return $this->_validate_fields_input($value);
99 }
100 )
101 )
102 ], self::PERMISSIONS, [ AAM_Framework_Type_AccessLevel::USER ]);
103
104 // Get a token by ID
105 $this->_register_route('/jwt/(?P<id>[\w\-]+)', [
106 'methods' => WP_REST_Server::READABLE,
107 'callback' => array($this, 'get_token'),
108 'args' => array(
109 'id' => array(
110 'description' => 'Token unique ID',
111 'type' => 'string',
112 'format' => 'uuid',
113 'required' => true
114 ),
115 'fields' => array(
116 'description' => 'List of additional fields to return',
117 'type' => 'string',
118 'validate_callback' => function ($value) {
119 return $this->_validate_fields_input($value);
120 }
121 )
122 )
123 ], self::PERMISSIONS, [ AAM_Framework_Type_AccessLevel::USER ]);
124
125 // Delete a token
126 $this->_register_route('/jwt/(?P<id>[\w\-]+)', [
127 'methods' => WP_REST_Server::DELETABLE,
128 'callback' => array($this, 'delete_token'),
129 'args' => array(
130 'id' => array(
131 'description' => 'Token unique ID',
132 'type' => 'string',
133 'format' => 'uuid',
134 'required' => true
135 )
136 )
137 ], self::PERMISSIONS, [ AAM_Framework_Type_AccessLevel::USER ]);
138
139 // Refresh a token
140 $this->_register_route('/jwt/(?P<id>[\w\-]+)', [
141 'methods' => WP_REST_Server::EDITABLE,
142 'callback' => [ $this, 'refresh_token'],
143 'args' => [
144 'id' => [
145 'description' => 'Token unique ID',
146 'type' => 'string',
147 'format' => 'uuid',
148 'required' => true
149 ]
150 ]
151 ], self::PERMISSIONS, [ AAM_Framework_Type_AccessLevel::USER ]);
152
153 // Reset all tokens
154 $this->_register_route('/jwts', [
155 'methods' => WP_REST_Server::DELETABLE,
156 'callback' => array($this, 'reset_tokens')
157 ], self::PERMISSIONS, [ AAM_Framework_Type_AccessLevel::USER ]);
158 });
159 }
160
161 /**
162 * Get list of all registered tokens
163 *
164 * @param WP_REST_Request $request
165 *
166 * @return WP_REST_Response
167 * @access public
168 *
169 * @version 7.0.0
170 */
171 public function get_tokens(WP_REST_Request $request)
172 {
173 try {
174 $service = $this->_get_service($request);
175 $result = array();
176
177 foreach($service->get_tokens() as $token_data) {
178 array_push($result, $this->_prepare_token_output(
179 $token_data, $request->get_param('fields')
180 ));
181 }
182 } catch (Exception $e) {
183 $result = $this->_prepare_error_response($e);
184 }
185
186 return rest_ensure_response($result);
187 }
188
189 /**
190 * Create new JWT token
191 *
192 * @param WP_REST_Request $request
193 *
194 * @return WP_REST_Response
195 * @access public
196 *
197 * @version 7.0.0
198 */
199 public function create_token(WP_REST_Request $request)
200 {
201 try {
202 $service = $this->_get_service($request);
203 $claims = [];
204
205 // Do we have any additional claims to include
206 if ($request->has_param('additional_claims')) {
207 $raw_claims = $request->get_param('additional_claims');
208
209 if (is_string($raw_claims)) {
210 $claims = json_decode($raw_claims, true);
211 } elseif (is_array($raw_claims)) {
212 $claims = $raw_claims;
213 } else {
214 throw new InvalidArgumentException('Invalid additional claims');
215 }
216 }
217
218 // Determining the token expiration time
219 $expires_at = $request->get_param('expires_at');
220 $expires_in = $request->get_param('expires_in');
221
222 if (!empty($expires_at)) {
223 $ttl = $expires_at;
224 } elseif (!empty($expires_in)) {
225 $ttl = $expires_in;
226 } else {
227 $ttl = null;
228 }
229
230 $token_data = $service->issue($claims, [
231 'ttl' => $ttl,
232 'revocable' => $request->get_param('is_revocable'),
233 'refreshable' => $request->get_param('is_refreshable'),
234 'description' => $request->get_param('description')
235 ]);
236
237 $result = $this->_prepare_token_output(
238 $token_data, $request->get_param('fields')
239 );
240 } catch (Exception $e) {
241 $result = $this->_prepare_error_response($e);
242 }
243
244 return rest_ensure_response($result);
245 }
246
247 /**
248 * Get a token by ID
249 *
250 * @param WP_REST_Request $request
251 *
252 * @return WP_REST_Response
253 * @access public
254 *
255 * @version 7.0.0
256 */
257 public function get_token(WP_REST_Request $request)
258 {
259 try {
260 $service = $this->_get_service($request);
261 $result = $this->_prepare_token_output(
262 $service->get_token_by($request->get_param('id'), 'jti'),
263 $request->get_param('fields')
264 );
265 } catch (Exception $e) {
266 $result = $this->_prepare_error_response($e);
267 }
268
269 return rest_ensure_response($result);
270 }
271
272 /**
273 * Refresh a token
274 *
275 * @param WP_REST_Request $request
276 *
277 * @return WP_REST_Response
278 * @access public
279 *
280 * @version 7.0.0
281 */
282 public function refresh_token(WP_REST_Request $request)
283 {
284 try {
285 $service = $this->_get_service($request);
286 $token = $service->get_token_by($request->get_param('id'), 'jti');
287
288 if (!empty($token['is_valid'])) {
289 $result = $service->refresh($token['token']);
290 } else {
291 throw new OutOfRangeException('Token is invalid or does not exist');
292 }
293 } catch (Exception $e) {
294 $result = $this->_prepare_error_response($e);
295 }
296
297 return rest_ensure_response($result);
298 }
299
300 /**
301 * Delete a token
302 *
303 * @param WP_REST_Request $request
304 *
305 * @return WP_REST_Response
306 * @access public
307 *
308 * @version 7.0.0
309 */
310 public function delete_token(WP_REST_Request $request)
311 {
312 try {
313 $service = $this->_get_service($request);
314 $token = $service->get_token_by($request->get_param('id'), 'jti');
315
316 $result = [ 'success' => $service->revoke($token['token']) ];
317 } catch (Exception $e) {
318 $result = $this->_prepare_error_response($e);
319 }
320
321 return rest_ensure_response($result);
322 }
323
324 /**
325 * Reset all tokens
326 *
327 * @param WP_REST_Request $request
328 *
329 * @return WP_REST_Response
330 * @access public
331 *
332 * @version 7.0.0
333 */
334 public function reset_tokens(WP_REST_Request $request)
335 {
336 try {
337 $result = [ 'success' => $this->_get_service($request)->reset() ];
338 } catch (Exception $e) {
339 $result = $this->_prepare_error_response($e);
340 }
341
342 return rest_ensure_response($result);
343 }
344
345 /**
346 * Validate the input field "fields"
347 *
348 * @param string|null $value Input value
349 *
350 * @return bool|WP_Error
351 * @access private
352 *
353 * @version 7.0.0
354 */
355 private function _validate_fields_input($value)
356 {
357 $response = true;
358
359 if (is_string($value) && strlen($value) > 0) {
360 $invalid_fields = [];
361
362 foreach(explode(',', $value) as $field) {
363 if (strlen(sanitize_key($field)) !== strlen($field)) {
364 $invalid_fields[] = $field;
365 }
366 }
367
368 if (count($invalid_fields) > 0) {
369 $response = new WP_Error(
370 'rest_invalid_param',
371 sprintf('Invalid fields: %s', implode(', ', $invalid_fields)),
372 array('status' => 400)
373 );
374 }
375 }
376
377 return $response;
378 }
379
380 /**
381 * Validate the input field "expires_in"
382 *
383 * @param string|null $value Input value
384 *
385 * @return bool|WP_Error
386 * @access private
387 *
388 * @version 7.0.0
389 */
390 private function _validate_expires_in_input($value)
391 {
392 $response = true;
393
394 if (is_string($value) && strlen($value) > 0) {
395 $time = strtotime($value);
396
397 if ($time === false) {
398 $response = new WP_Error(
399 'rest_invalid_param',
400 'Invalid expires_in value',
401 array('status' => 400)
402 );
403 }
404 }
405
406 return $response;
407 }
408
409 /**
410 * Prepare token for the output
411 *
412 * @param array $token_data
413 * @param array $fields
414 *
415 * @return array
416 * @access private
417 *
418 * @version 7.1.0
419 */
420 private function _prepare_token_output($token_data, $fields)
421 {
422 $output = [
423 'id' => $token_data['claims']['jti'],
424 'token' => $token_data['token'],
425 'is_valid' => $token_data['is_valid']
426 ];
427
428 // Include also description is available
429 // This is done because token description feature was added in AAM 7.1.0
430 if (array_key_exists('description', $token_data)) {
431 $output['description'] = $token_data['description'];
432 }
433
434 if ($token_data['is_valid']) {
435 foreach((!empty($fields) ? wp_parse_list($fields) : []) as $field) {
436 if ($field === 'signed_url') {
437 $output[$field] = add_query_arg(
438 'aam-jwt', $token_data['token'], site_url()
439 );
440 } elseif (array_key_exists($field, $token_data)) {
441 $output[$field] = $token_data[$field];
442 }
443 }
444 } else {
445 $output['error'] = $token_data['error'];
446 }
447
448 return $output;
449 }
450
451 /**
452 * Get JWT framework service
453 *
454 * @param WP_REST_Request $request
455 *
456 * @return AAM_Framework_Service_Jwts
457 * @access private
458 *
459 * @version 7.0.0
460 */
461 private function _get_service($request)
462 {
463 $access_level = AAM::api()->access_levels->get(
464 AAM_Framework_Type_AccessLevel::USER,
465 $request->get_param('user_id')
466 );
467
468 return AAM::api()->jwts(
469 $access_level,
470 [ 'error_handling' => 'exception' ]
471 );
472 }
473
474 }