Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
WPCOM_REST_API_Proxy_Request
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 3
182
0.00% covered (danger)
0.00%
0 / 1
 proxy_request_to_wpcom
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 1
132
 proxy_request_to_wpcom_as_user
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 proxy_request_to_wpcom_as_blog
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Trait WPCOM_REST_API_Proxy_Request
4 *
5 * Used to proxy requests to wpcom servers.
6 *
7 * @package automattic/jetpack-connection
8 */
9
10namespace Automattic\Jetpack\Connection\Traits;
11
12use Automattic\Jetpack\Connection\Client;
13use Automattic\Jetpack\Connection\Manager;
14use Automattic\Jetpack\Status\Visitor;
15use WP_Error;
16use WP_REST_Request;
17
18trait WPCOM_REST_API_Proxy_Request {
19
20    /**
21     * Base path for the API.
22     *
23     * @var string
24     */
25    protected $base_api_path;
26
27    /**
28     * Version of the API.
29     *
30     * @var string
31     */
32    protected $version;
33
34    /**
35     * The base of the controller's route.
36     *
37     * @var string
38     */
39    protected $rest_base;
40
41    /**
42     * Proxy request to wpcom servers on behalf of a user or using the Site-level Connection (blog token).
43     *
44     * @param WP_REST_Request $request Request to proxy.
45     * @param string          $path Path to append to the rest base.
46     * @param string          $context Whether the request should be proxied on behalf of the current user or using the Site-level Connection, aka 'blog' token. Can be Either 'user' or 'blog'. Defaults to 'user'.
47     * @param bool            $allow_fallback_to_blog If the $context is 'user', whether we should fallback to using the Site-level Connection in case the current user is not connected.
48     * @param array           $request_options Request options to pass to wp_remote_request.
49     *
50     * @return mixed|WP_Error           Response from wpcom servers or an error.
51     */
52    public function proxy_request_to_wpcom( $request, $path = '', $context = 'user', $allow_fallback_to_blog = false, $request_options = array() ) {
53        $blog_id      = \Jetpack_Options::get_option( 'id' );
54        $path         = '/sites/' . rawurldecode( $blog_id ) . '/' . rawurldecode( ltrim( $this->rest_base, '/' ) ) . ( $path ? '/' . rawurldecode( ltrim( $path, '/' ) ) : '' );
55        $query_params = $request->get_query_params();
56        $manager      = new Manager();
57
58        /*
59         * A rest_route parameter can be added when using plain permalinks.
60         * It is not necessary to pass them to WordPress.com,
61         * and may even cause issues with some endpoints.
62         * Let's remove it.
63         */
64        if ( isset( $query_params['rest_route'] ) ) {
65            unset( $query_params['rest_route'] );
66        }
67        $api_url = add_query_arg( $query_params, $path );
68
69        $request_options = array_replace_recursive(
70            array(
71                'headers' => array(
72                    'Content-Type'    => 'application/json',
73                    'X-Forwarded-For' => ( new Visitor() )->get_ip( true ),
74                ),
75                'method'  => $request->get_method(),
76            ),
77            $request_options
78        );
79
80        // If no body is present, passing it as $request->get_body() will cause an error.
81        $body = $request->get_body() ? $request->get_body() : null;
82
83        $response = new WP_Error(
84            'rest_unauthorized',
85            __( 'Please connect your user account to WordPress.com', 'jetpack-connection' ),
86            array( 'status' => rest_authorization_required_code() )
87        );
88
89        if ( 'user' === $context ) {
90            if ( ! $manager->is_user_connected() ) {
91                if ( false === $allow_fallback_to_blog ) {
92                    return $response;
93                }
94
95                $context = 'blog';
96            } else {
97                $response = Client::wpcom_json_api_request_as_user( $api_url, $this->version, $request_options, $body, $this->base_api_path );
98            }
99        }
100
101        if ( 'blog' === $context ) {
102            if ( ! $manager->is_connected() ) {
103                return $response;
104            }
105
106            $response = Client::wpcom_json_api_request_as_blog( $api_url, $this->version, $request_options, $body, $this->base_api_path );
107        }
108
109        if ( is_wp_error( $response ) ) {
110            return $response;
111        }
112
113        $response_status = wp_remote_retrieve_response_code( $response );
114        $response_body   = json_decode( wp_remote_retrieve_body( $response ), true );
115
116        if ( $response_status >= 400 ) {
117            $code    = $response_body['code'] ?? 'unknown_error';
118            $message = $response_body['message'] ?? __( 'An unknown error occurred.', 'jetpack-connection' );
119
120            return new WP_Error( $code, $message, array( 'status' => $response_status ) );
121        }
122
123        return $response_body;
124    }
125
126    /**
127     * Proxy request to wpcom servers on behalf of a user.
128     *
129     * @param WP_REST_Request $request Request to proxy.
130     * @param string          $path Path to append to the rest base.
131     * @param array           $request_options Request options to pass to wp_remote_request.
132     *
133     * @return mixed|WP_Error           Response from wpcom servers or an error.
134     */
135    public function proxy_request_to_wpcom_as_user( $request, $path = '', $request_options = array() ) {
136        return $this->proxy_request_to_wpcom( $request, $path, 'user', false, $request_options );
137    }
138
139    /**
140     * Proxy request to wpcom servers using the Site-level Connection (blog token).
141     *
142     * @param WP_REST_Request $request Request to proxy.
143     * @param string          $path Path to append to the rest base.
144     * @param array           $request_options Request options to pass to wp_remote_request.
145     *
146     * @return mixed|WP_Error           Response from wpcom servers or an error.
147     */
148    public function proxy_request_to_wpcom_as_blog( $request, $path = '', $request_options = array() ) {
149        return $this->proxy_request_to_wpcom( $request, $path, 'blog', false, $request_options );
150    }
151}