Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 64 additions & 14 deletions inc/RestCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -591,21 +591,9 @@ function ( $a, $b ) {
}
return array( $response->get_status(), $response->get_data(), $response->get_headers() );
} elseif ( 'http' === $this->scope ) {
$headers = array();
if ( ! empty( $this->auth ) && 'basic' === $this->auth['type'] ) {
$username = isset( $this->auth['username'] ) ? $this->auth['username'] : '';
$password = isset( $this->auth['password'] ) ? $this->auth['password'] : '';
if ( is_scalar( $username ) && is_scalar( $password ) ) {
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$headers['Authorization'] = 'Basic ' . base64_encode( (string) $username . ':' . (string) $password );
}
}
if ( 'OPTIONS' === $method ) {
$method = 'GET';
$assoc_args['_method'] = 'OPTIONS';
}
list( $method, $url, $assoc_args, $headers ) = $this->get_http_request_args( $method, $route, $assoc_args );
/** @var \WpOrg\Requests\Response $response */
$response = Utils\http_request( $method, rtrim( $this->api_url, '/' ) . $route, $assoc_args, $headers );
$response = Utils\http_request( $method, $url, $assoc_args, $headers );
$body = json_decode( $response->body, true );
if ( ! is_array( $body ) ) {
$body = array();
Expand Down Expand Up @@ -633,6 +621,68 @@ function ( $a, $b ) {
return array( 0, '', array() );
}

/**
* Build HTTP request arguments and allow custom authentication mechanisms.
*
* Runs the `restful_http_request_args` hook with an associative array containing
* `method`, `url`, `assoc_args`, and `headers`, plus the current RestCommand
* instance as the second callback argument. Hook callbacks may return a modified
* array to customize the HTTP request before dispatch.
*
* @param string $method
* @param string $route
* @param array<string, mixed> $assoc_args
*
* @return array{0: string, 1: string, 2: array<string, mixed>, 3: array<string, mixed>}
*/
private function get_http_request_args( $method, $route, $assoc_args ) {
$headers = array();
if ( ! empty( $this->auth ) && 'basic' === $this->auth['type'] ) {
$username = isset( $this->auth['username'] ) ? $this->auth['username'] : '';
$password = isset( $this->auth['password'] ) ? $this->auth['password'] : '';
if ( is_scalar( $username ) && is_scalar( $password ) ) {
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$headers['Authorization'] = 'Basic ' . base64_encode( (string) $username . ':' . (string) $password );
}
}

if ( 'OPTIONS' === $method ) {
$method = 'GET';
$assoc_args['_method'] = 'OPTIONS';
}

$url = rtrim( $this->api_url, '/' ) . $route;

/** @var array{method?: string, url?: string, assoc_args?: array<string, mixed>, headers?: array<string, mixed>} $request_args */
$request_args = WP_CLI::do_hook(
'restful_http_request_args',
Comment thread
swissspidy marked this conversation as resolved.
array(
'method' => $method,
'url' => $url,
'assoc_args' => $assoc_args,
'headers' => $headers,
),
$this
);

if ( is_array( $request_args ) ) {
if ( array_key_exists( 'method', $request_args ) && is_string( $request_args['method'] ) ) {
$method = $request_args['method'];
}
if ( array_key_exists( 'url', $request_args ) && is_string( $request_args['url'] ) ) {
$url = $request_args['url'];
}
if ( array_key_exists( 'assoc_args', $request_args ) && is_array( $request_args['assoc_args'] ) ) {
$assoc_args = $request_args['assoc_args'];
}
if ( array_key_exists( 'headers', $request_args ) && is_array( $request_args['headers'] ) ) {
$headers = $request_args['headers'];
}
}

return array( $method, $url, $assoc_args, $headers );
}

/**
* Get Formatter object based on supplied parameters.
*
Expand Down
99 changes: 99 additions & 0 deletions tests/RestCommand_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

use WP_CLI\Tests\TestCase;
use WP_REST_CLI\RestCommand;

class RestCommand_Test extends TestCase {

/** @var \ReflectionMethod */
protected $method;

/** @var RestCommand */
protected $command;

public function set_up() {
$this->command = new RestCommand( 'post', '/wp/v2/posts/(?P<id>[\\d]+)', array() );
$this->command->set_scope( 'http' );

$reflection = new \ReflectionClass( $this->command );
$this->method = $reflection->getMethod( 'get_http_request_args' );
$this->method->setAccessible( true );
}

public function test_http_request_args_include_basic_auth_header() {
$this->command->set_api_url( 'https://example.com/wp-json' );
$this->command->set_auth(
array(
'type' => 'basic',
'username' => 'wpuser',
'password' => 'wppass',
)
);

$result = $this->method->invokeArgs(
$this->command,
array(
'OPTIONS',
'/wp/v2/posts',
array( 'context' => 'edit' ),
)
);

$this->assertSame( 'GET', $result[0] );
$this->assertSame( 'https://example.com/wp-json/wp/v2/posts', $result[1] );
$this->assertSame(
array(
'context' => 'edit',
'_method' => 'OPTIONS',
),
$result[2]
);
$this->assertSame( 'Basic d3B1c2VyOndwcGFzcw==', $result[3]['Authorization'] );
}

public function test_http_request_args_can_be_filtered_for_custom_auth() {
$this->command->set_api_url( 'https://hooked.example/wp-json' );
$this->command->set_auth(
array(
'type' => 'basic',
'username' => 'old-user',
'password' => 'old-pass',
)
);

\WP_CLI::add_hook(
'restful_http_request_args',
function ( $request_args ) {
if ( ! isset( $request_args['url'] ) || 'https://hooked.example/wp-json/wp/v2/posts' !== $request_args['url'] ) {
return $request_args;
}
$request_args['headers'] = array(
'Authorization' => '******',
'X-Custom-Auth' => 'jwt',
);
$request_args['assoc_args'] = array(
'token' => 'abc123',
);
return $request_args;
}
);

$result = $this->method->invokeArgs(
$this->command,
array(
'GET',
'/wp/v2/posts',
array(),
)
);

$this->assertSame(
array(
'Authorization' => '******',
'X-Custom-Auth' => 'jwt',
),
$result[3]
);
$this->assertSame( array( 'token' => 'abc123' ), $result[2] );
}
}
Loading