Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
40.30% covered (danger)
40.30%
27 / 67
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Authorize_Redirect
40.30% covered (danger)
40.30%
27 / 67
40.00% covered (danger)
40.00%
2 / 5
158.00
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 handle
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
156
 build_authorize_url
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 filter_connect_redirect_url
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
4.25
 filter_connect_request_body
73.68% covered (warning)
73.68%
14 / 19
0.00% covered (danger)
0.00%
0 / 1
6.66
 get_calypso_env
n/a
0 / 0
n/a
0 / 0
1
1<?php
2/**
3 * Authorize_Redirect Webhook handler class.
4 *
5 * @package automattic/jetpack-connection
6 */
7
8namespace Automattic\Jetpack\Connection\Webhooks;
9
10use Automattic\Jetpack\Admin_UI\Admin_Menu;
11use Automattic\Jetpack\Constants;
12use Automattic\Jetpack\Licensing;
13use Automattic\Jetpack\Status\Host;
14use Automattic\Jetpack\Tracking;
15use GP_Locales;
16use Jetpack_Network;
17
18/**
19 * Authorize_Redirect Webhook handler class.
20 */
21class Authorize_Redirect {
22    /**
23     * The Connection Manager object.
24     *
25     * @var \Automattic\Jetpack\Connection\Manager
26     */
27    private $connection;
28
29    /**
30     * Constructs the object
31     *
32     * @param \Automattic\Jetpack\Connection\Manager $connection The Connection Manager object.
33     */
34    public function __construct( $connection ) {
35        $this->connection = $connection;
36    }
37
38    /**
39     * Handle the webhook
40     *
41     * This method implements what's in Jetpack::admin_page_load when the Jetpack plugin is not present
42     *
43     * @return never
44     */
45    public function handle() {
46
47        add_filter(
48            'allowed_redirect_hosts',
49            function ( $domains ) {
50                $domains[] = 'jetpack.com';
51                $domains[] = 'jetpack.wordpress.com';
52                $domains[] = 'wordpress.com';
53                // Calypso envs.
54                $domains[] = 'calypso.localhost';
55                $domains[] = 'wpcalypso.wordpress.com';
56                $domains[] = 'horizon.wordpress.com';
57                return array_unique( $domains );
58            }
59        );
60
61        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
62        $dest_url = empty( $_GET['dest_url'] ) ? null : esc_url_raw( wp_unslash( $_GET['dest_url'] ) );
63
64        if ( ! $dest_url || ( 0 === stripos( $dest_url, 'https://jetpack.com/' ) && 0 === stripos( $dest_url, 'https://wordpress.com/' ) ) ) {
65            // The destination URL is missing or invalid, nothing to do here.
66            exit( 0 );
67        }
68
69        // The user is either already connected, or finished the connection process.
70        if ( $this->connection->is_connected() && $this->connection->is_user_connected() ) {
71            if ( class_exists( '\Automattic\Jetpack\Licensing' ) && method_exists( '\Automattic\Jetpack\Licensing', 'handle_user_connected_redirect' ) ) {
72                Licensing::instance()->handle_user_connected_redirect( $dest_url );
73            }
74
75            wp_safe_redirect( $dest_url );
76            exit( 0 );
77        } elseif ( ! empty( $_GET['done'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
78            // The user decided not to proceed with setting up the connection.
79
80            wp_safe_redirect( Admin_Menu::get_top_level_menu_item_url() );
81            exit( 0 );
82        }
83
84        $redirect_args = array(
85            'page'     => 'jetpack',
86            'action'   => 'authorize_redirect',
87            'dest_url' => rawurlencode( $dest_url ),
88            'done'     => '1',
89        );
90
91        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
92        if ( ! empty( $_GET['from'] ) && 'jetpack_site_only_checkout' === $_GET['from'] ) {
93            $redirect_args['from'] = 'jetpack_site_only_checkout';
94        }
95
96        wp_safe_redirect( $this->build_authorize_url( add_query_arg( $redirect_args, admin_url( 'admin.php' ) ) ) );
97        exit( 0 );
98    }
99
100    /**
101     * Create the Jetpack authorization URL.
102     *
103     * @since 2.7.6 Added optional $from and $raw parameters.
104     * @since 6.8.0 Added optional $provider and $provider_args parameters.
105     *
106     * @param bool|string $redirect URL to redirect to.
107     * @param bool|string $from     If not false, adds 'from=$from' param to the connect URL.
108     * @param bool        $raw If true, URL will not be escaped.
109     *
110     * @todo Update default value for redirect since the called function expects a string.
111     *
112     * @return mixed|void
113     */
114    public function build_authorize_url( $redirect = false, $from = false, $raw = false ) {
115
116        add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
117        add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
118
119        $url = $this->connection->get_authorization_url( wp_get_current_user(), $redirect, $from, $raw );
120
121        remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
122        remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
123
124        /**
125         * Filter the URL used when authorizing a user to a WordPress.com account.
126         *
127         * @since jetpack-8.9.0
128         * @since 2.7.6 Added $raw parameter.
129         *
130         * @param string      $url           Connection URL.
131         * @param bool        $raw           If true, URL will not be escaped.
132         */
133        return apply_filters( 'jetpack_build_authorize_url', $url, $raw );
134    }
135
136    /**
137     * Filters the redirection URL that is used for connect requests. The redirect
138     * URL should return the user back to the My Jetpack page.
139     *
140     * @param string $redirect the default redirect URL used by the package.
141     * @return string the modified URL.
142     */
143    public static function filter_connect_redirect_url( $redirect ) {
144        $jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=my-jetpack' ) );
145        $redirect           = $redirect
146            ? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
147            : $jetpack_admin_page;
148
149        if (
150            class_exists( 'Jetpack_Network' )
151            && isset( $_REQUEST['is_multisite'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
152        ) {
153            $redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
154        }
155
156        return $redirect;
157    }
158
159    /**
160     * Filters the connection URL parameter array.
161     *
162     * @param array $args default URL parameters used by the package.
163     * @return array the modified URL arguments array.
164     */
165    public static function filter_connect_request_body( $args ) {
166        if (
167            Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
168            && include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
169        ) {
170            $gp_locale      = GP_Locales::by_field( 'wp_locale', get_locale() );
171            $args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
172                ? $gp_locale->slug
173                : '';
174        }
175
176        $tracking        = new Tracking();
177        $tracks_identity = $tracking->tracks_get_identity( $args['state'] );
178
179        $args = array_merge(
180            $args,
181            array(
182                '_ui' => $tracks_identity['_ui'],
183                '_ut' => $tracks_identity['_ut'],
184            )
185        );
186
187        $calypso_env = ( new Host() )->get_calypso_env();
188
189        if ( ! empty( $calypso_env ) ) {
190            $args['calypso_env'] = $calypso_env;
191        }
192
193        return $args;
194    }
195
196    /**
197     * Return Calypso environment value; used for developing Jetpack and pairing
198     * it with different Calypso enrionments, such as localhost.
199     * Copied from Jetpack class.
200     *
201     * @deprecated 2.7.6
202     *
203     * @since 1.37.1
204     *
205     * @return string Calypso environment
206     */
207    public static function get_calypso_env() {
208        _deprecated_function( __METHOD__, '2.7.6', 'Automattic\\Jetpack\\Status\\Host::get_calypso_env' );
209
210        return ( new Host() )->get_calypso_env();
211    }
212}