← All changes
|
includes/rest-api/controller/version2/class-mainwp-rest-api-keys-controller.php
+46
-20
6.1.8
→
6.2.1
View file →
| @@ -224,17 +224,20 @@ | ||
| 224 | 224 | * |
| 225 | 225 | * @return WP_Error|WP_REST_Response |
| 226 | 226 | */ |
| 227 | 227 | public function create_new_key( $request ) { // phpcs:ignore -- NOSONAR - complex. |
| 228 | - // Get request body. | |
| 229 | - $body = $request->get_body_params(); | |
| 230 | - if ( empty( $body ) ) { | |
| 231 | - return new WP_Error( | |
| 232 | - 'empty_body', | |
| 233 | - __( 'Request body is empty.', 'mainwp' ), | |
| 234 | - ); | |
| 228 | + $body = $this->get_request_body_data( $request ); | |
| 229 | + if ( is_wp_error( $body ) ) { | |
| 230 | + return $body; | |
| 235 | 231 | } |
| 236 | 232 | |
| 233 | + // Core validated and sanitized the registered args from every bag | |
| 234 | + // (query included); reading only the body bag would ignore a query | |
| 235 | + // value that satisfied the required-arg check. | |
| 236 | + $active_param = $request->get_param( 'active' ); | |
| 237 | + $permissions_param = $request->get_param( 'permissions' ); | |
| 238 | + $description_param = $request->get_param( 'description' ); | |
| 239 | + | |
| 237 | 240 | // Generate consumer key and secret. |
| 238 | 241 | $_consumer_key = MainWP_Rest_Api_Page::mainwp_generate_rand_hash(); |
| 239 | 242 | $_consumer_secret = MainWP_Rest_Api_Page::mainwp_generate_rand_hash(); |
| 240 | 243 | |
| @@ -240,11 +243,11 @@ | ||
| 240 | 243 | |
| 241 | 244 | // Map data. |
| 242 | 245 | $consumer_key = 'ck_' . $_consumer_key; |
| 243 | 246 | $consumer_secret = 'cs_' . $_consumer_secret; |
| 244 | - $active = ! empty( $body['active'] ) ? 1 : 0; | |
| 245 | - $permission = ! empty( $body['permissions'] ) ? sanitize_text_field( wp_unslash( $body['permissions'] ) ) : ''; | |
| 246 | - $desc = ! empty( $body['description'] ) ? sanitize_text_field( wp_unslash( $body['description'] ) ) : ''; | |
| 247 | + $active = ! empty( $active_param ) ? 1 : 0; | |
| 248 | + $permission = ! empty( $permissions_param ) ? sanitize_text_field( $permissions_param ) : ''; | |
| 249 | + $desc = ! empty( $description_param ) ? sanitize_text_field( $description_param ) : ''; | |
| 247 | 250 | $scope = $this->determine_scope( $permission ); |
| 248 | 251 | |
| 249 | 252 | try { |
| 250 | 253 | // Save api key v2. insert_rest_api_key() returns false when the |
| @@ -293,17 +296,27 @@ | ||
| 293 | 296 | __( 'API key not found.', 'mainwp' ), |
| 294 | 297 | ); |
| 295 | 298 | } |
| 296 | 299 | |
| 297 | - // Get request body. | |
| 298 | - $body = $request->get_json_params(); | |
| 299 | - if ( empty( $body ) ) { | |
| 300 | + $body = $this->get_request_body_data( $request ); | |
| 301 | + if ( is_wp_error( $body ) ) { | |
| 302 | + return $body; | |
| 303 | + } | |
| 304 | + | |
| 305 | + // Every field here is optional, so a JSON payload sent without its content type | |
| 306 | + // (which core parses as one URL-encoded key) would otherwise "succeed" and change nothing. | |
| 307 | + // has_param() also sees a validated query value the body bag alone would miss. | |
| 308 | + if ( ! $request->has_param( 'active' ) && ! $request->has_param( 'description' ) && ! $request->has_param( 'permissions' ) ) { | |
| 300 | 309 | return new WP_Error( |
| 301 | 310 | 'empty_body', |
| 302 | 311 | __( 'Request body is empty.', 'mainwp' ), |
| 312 | + array( 'status' => 400 ) | |
| 303 | 313 | ); |
| 304 | 314 | } |
| 305 | 315 | |
| 316 | + $description_param = $request->get_param( 'description' ); | |
| 317 | + $permissions_param = $request->get_param( 'permissions' ); | |
| 318 | + | |
| 306 | 319 | // Get key identifier. |
| 307 | 320 | $cons_key_id = $request->get_param( 'key_identifier' ); |
| 308 | 321 | |
| 309 | 322 | // Determine defaults based on key version (v1 = array, v2 = object). |
| @@ -318,17 +331,17 @@ | ||
| 318 | 331 | } |
| 319 | 332 | |
| 320 | 333 | // Determine active. |
| 321 | 334 | $active = $current_enabled; |
| 322 | - if ( isset( $body['active'] ) ) { | |
| 323 | - $active = $body['active'] ? 1 : 0; | |
| 335 | + if ( $request->has_param( 'active' ) ) { | |
| 336 | + $active = $request->get_param( 'active' ) ? 1 : 0; | |
| 324 | 337 | } |
| 325 | 338 | // Determine description. |
| 326 | - $desc = ! empty( $body['description'] ) ? sanitize_text_field( wp_unslash( $body['description'] ) ) : $current_description; | |
| 339 | + $desc = ! empty( $description_param ) ? sanitize_text_field( $description_param ) : $current_description; | |
| 327 | 340 | |
| 328 | 341 | // Edit api key v1. |
| 329 | 342 | if ( ! is_numeric( $cons_key_id ) ) { |
| 330 | - $scope = ! empty( $body['permissions'] ) ? $this->determine_scope( $body['permissions'], 'v1' ) : $current_permissions; | |
| 343 | + $scope = ! empty( $permissions_param ) ? $this->determine_scope( $permissions_param, 'v1' ) : $current_permissions; | |
| 331 | 344 | $save = false; |
| 332 | 345 | $all_keys = get_option( 'mainwp_rest_api_keys', false ); |
| 333 | 346 | if ( is_array( $all_keys ) && isset( $all_keys[ $cons_key_id ] ) ) { |
| 334 | 347 | $item = $all_keys[ $cons_key_id ]; |
| @@ -349,9 +362,9 @@ | ||
| 349 | 362 | } |
| 350 | 363 | |
| 351 | 364 | MainWP_Utility::update_option( 'mainwp_rest_api_keys', $all_keys ); |
| 352 | 365 | } else { |
| 353 | - $scope = ! empty( $body['permissions'] ) ? $this->determine_scope( $body['permissions'] ) : $current_permissions; | |
| 366 | + $scope = ! empty( $permissions_param ) ? $this->determine_scope( $permissions_param ) : $current_permissions; | |
| 354 | 367 | // Update api key. |
| 355 | 368 | $updated = MainWP_DB::instance()->update_rest_api_key( $api_key->key_id, $scope, $desc, $active ); |
| 356 | 369 | if ( false === $updated ) { |
| 357 | 370 | return new WP_Error( |
| @@ -464,9 +477,12 @@ | ||
| 464 | 477 | 'active' => array( |
| 465 | 478 | 'required' => true, |
| 466 | 479 | 'type' => 'boolean', |
| 467 | 480 | 'description' => __( 'Active or disable API key.', 'mainwp' ), |
| 468 | - 'sanitize_callback' => 'rest_sanitize_boolean', | |
| 481 | + // rest_parse_request_arg validates before it sanitizes, so an explicit | |
| 482 | + // JSON null is a 400 instead of being coerced to false past the validator. | |
| 483 | + 'sanitize_callback' => 'rest_parse_request_arg', | |
| 484 | + 'validate_callback' => 'rest_validate_request_arg', | |
| 469 | 485 | ), |
| 470 | 486 | 'description' => array( |
| 471 | 487 | 'required' => false, |
| 472 | 488 | 'type' => 'string', |
| @@ -471,8 +487,9 @@ | ||
| 471 | 487 | 'required' => false, |
| 472 | 488 | 'type' => 'string', |
| 473 | 489 | 'description' => __( 'API key description.', 'mainwp' ), |
| 474 | 490 | 'sanitize_callback' => 'sanitize_text_field', |
| 491 | + 'validate_callback' => 'rest_validate_request_arg', | |
| 475 | 492 | ), |
| 476 | 493 | 'permissions' => array( |
| 477 | 494 | 'required' => true, |
| 478 | 495 | 'type' => 'string', |
| @@ -495,9 +512,10 @@ | ||
| 495 | 512 | 'active' => array( |
| 496 | 513 | 'required' => false, |
| 497 | 514 | 'type' => 'boolean', |
| 498 | 515 | 'description' => __( 'Active or disable API key.', 'mainwp' ), |
| 499 | - 'sanitize_callback' => 'rest_sanitize_boolean', | |
| 516 | + 'sanitize_callback' => 'rest_parse_request_arg', | |
| 517 | + 'validate_callback' => 'rest_validate_request_arg', | |
| 500 | 518 | ), |
| 501 | 519 | 'description' => array( |
| 502 | 520 | 'required' => false, |
| 503 | 521 | 'type' => 'string', |
| @@ -502,8 +520,9 @@ | ||
| 502 | 520 | 'required' => false, |
| 503 | 521 | 'type' => 'string', |
| 504 | 522 | 'description' => __( 'API key description.', 'mainwp' ), |
| 505 | 523 | 'sanitize_callback' => 'sanitize_text_field', |
| 524 | + 'validate_callback' => 'rest_validate_request_arg', | |
| 506 | 525 | ), |
| 507 | 526 | 'permissions' => array( |
| 508 | 527 | 'required' => false, |
| 509 | 528 | 'type' => 'string', |
| @@ -523,8 +542,15 @@ | ||
| 523 | 542 | * @param string $param Parameter name. |
| 524 | 543 | * @return bool|WP_Error |
| 525 | 544 | */ |
| 526 | 545 | public function rest_api_validate_permissions_param( $value, $request, $param ) { |
| 546 | + // A custom validate_callback replaces the registered-type check, so the | |
| 547 | + // declared string type has to be enforced here before explode() sees an array. | |
| 548 | + $valid = rest_validate_request_arg( $value, $request, $param ); | |
| 549 | + if ( is_wp_error( $valid ) ) { | |
| 550 | + return $valid; | |
| 551 | + } | |
| 552 | + | |
| 527 | 553 | if ( empty( $value ) ) { |
| 528 | 554 | return false; |
| 529 | 555 | } |
| 530 | 556 | |