Plugin Directory

source: jetpack/trunk/jetpack_vendor/automattic/jetpack-connection/src/class-webhooks.php @ 3056649

Last change on this file since 3056649 was 3056649, checked in by zinigor, 4 months ago

Updating trunk to version 13.2.2

File size: 6.3 KB
Line 
1<?php
2/**
3 * Connection Webhooks class.
4 *
5 * @package automattic/jetpack-connection
6 */
7
8namespace Automattic\Jetpack\Connection;
9
10use Automattic\Jetpack\CookieState;
11use Automattic\Jetpack\Roles;
12use Automattic\Jetpack\Status\Host;
13use Automattic\Jetpack\Tracking;
14use Jetpack_Options;
15
16/**
17 * Connection Webhooks class.
18 */
19class Webhooks {
20
21        /**
22         * The Connection Manager object.
23         *
24         * @var Manager
25        ��*/
26        private $connection;
27
28        /**
29         * Webhooks constructor.
30         *
31         * @param Manager $connection The Connection Manager object.
32         */
33        public function __construct( $connection ) {
34                $this->connection = $connection;
35        }
36
37        /**
38         * Initialize the webhooks.
39         *
40         * @param Manager $connection The Connection Manager object.
41         */
42        public static function init( $connection ) {
43                $webhooks = new static( $connection );
44
45                add_action( 'init', array( $webhooks, 'controller' ) );
46                add_action( 'load-toplevel_page_jetpack', array( $webhooks, 'fallback_jetpack_controller' ) );
47        }
48
49        /**
50         * Jetpack plugin used to trigger this webhooks in Jetpack::admin_page_load()
51         *
52         * The Jetpack toplevel menu is still accessible for stand-alone plugins, and while there's no content for that page, there are still
53         * actions from Calypso and WPCOM that reach that route regardless of the site having the Jetpack plugin or not. That's why we are still handling it here.
54         */
55        public function fallback_jetpack_controller() {
56                $this->controller( true );
57        }
58
59        /**
60         * The "controller" decides which handler we need to run.
61         *
62         * @param bool $force Do not check if it's a webhook request and just run the controller.
63         */
64        public function controller( $force = false ) {
65                if ( ! $force ) {
66                        // The nonce is verified in specific handlers.
67                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
68                        if ( empty( $_GET['handler'] ) || 'jetpack-connection-webhooks' !== $_GET['handler'] ) {
69                                return;
70                        }
71                }
72
73                // phpcs:ignore WordPress.Security.NonceVerification.Recommended
74                if ( isset( $_GET['connect_url_redirect'] ) ) {
75                        $this->handle_connect_url_redirect();
76                }
77
78                // phpcs:ignore WordPress.Security.NonceVerification.Recommended
79                if ( empty( $_GET['action'] ) ) {
80                        return;
81                }
82
83                // The nonce is verified in specific handlers.
84                // phpcs:ignore WordPress.Security.NonceVerification.Recommended
85                switch ( $_GET['action'] ) {
86                        case 'authorize':
87                                $this->handle_authorize();
88                                $this->do_exit();
89                                break;
90                        case 'authorize_redirect':
91                                $this->handle_authorize_redirect();
92                                $this->do_exit();
93                                break;
94                        // Class Jetpack::admin_page_load() still handles other cases.
95                }
96        }
97
98        /**
99         * Perform the authorization action.
100         */
101        public function handle_authorize() {
102                if ( $this->connection->is_connected() && $this->connection->is_user_connected() ) {
103                        $redirect_url = apply_filters( 'jetpack_client_authorize_already_authorized_url', admin_url() );
104                        wp_safe_redirect( $redirect_url );
105
106                        return;
107                }
108                do_action( 'jetpack_client_authorize_processing' );
109
110                $data              = stripslashes_deep( $_GET );
111                $data['auth_type'] = 'client';
112                $roles             = new Roles();
113                $role              = $roles->translate_current_user_to_role();
114                $redirect          = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
115
116                check_admin_referer( "jetpack-authorize_{$role}_{$redirect}" );
117
118                $tracking = new Tracking();
119
120                $result = $this->connection->authorize( $data );
121
122                if ( is_wp_error( $result ) ) {
123                        do_action( 'jetpack_client_authorize_error', $result );
124
125                        $tracking->record_user_event(
126                                'jpc_client_authorize_fail',
127                                array(
128                                        'error_code'    => $result->get_error_code(),
129                                        'error_message' => $result->get_error_message(),
130                                )
131                        );
132                } else {
133                        /**
134                         * Fires after the Jetpack client is authorized to communicate with WordPress.com.
135                         *
136                         * @param int Jetpack Blog ID.
137                         *
138                         * @since 1.7.0
139                         * @since-jetpack 4.2.0
140                         */
141                        do_action( 'jetpack_client_authorized', Jetpack_Options::get_option( 'id' ) );
142
143                        $tracking->record_user_event( 'jpc_client_authorize_success' );
144                }
145
146                $fallback_redirect = apply_filters( 'jetpack_client_authorize_fallback_url', admin_url() );
147                $redirect          = wp_validate_redirect( $redirect ) ? $redirect : $fallback_redirect;
148
149                wp_safe_redirect( $redirect );
150        }
151
152        /**
153         * The authorhize_redirect webhook handler
154         */
155        public function handle_authorize_redirect() {
156                $authorize_redirect_handler = new Webhooks\Authorize_Redirect( $this->connection );
157                $authorize_redirect_handler->handle();
158        }
159
160        /**
161         * The `exit` is wrapped into a method so we could mock it.
162         */
163        protected function do_exit() {
164                exit;
165        }
166
167        /**
168         * Handle the `connect_url_redirect` action,
169         * which is usually called to repeat an attempt for user to authorize the connection.
170         *
171         * @return void
172         */
173        public function handle_connect_url_redirect() {
174                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no site changes.
175                $from = ! empty( $_GET['from'] ) ? sanitize_text_field( wp_unslash( $_GET['from'] ) ) : 'iframe';
176
177                // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- no site changes, sanitization happens in get_authorization_url()
178                $redirect = ! empty( $_GET['redirect_after_auth'] ) ? wp_unslash( $_GET['redirect_after_auth'] ) : false;
179
180                add_filter( 'allowed_redirect_hosts', array( Host::class, 'allow_wpcom_environments' ) );
181
182                if ( ! $this->connection->is_user_connected() ) {
183                        if ( ! $this->connection->is_connected() ) {
184                                $this->connection->register();
185                        }
186
187                        $connect_url = add_query_arg( 'from', $from, $this->connection->get_authorization_url( null, $redirect ) );
188
189                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no site changes.
190                        if ( isset( $_GET['notes_iframe'] ) ) {
191                                $connect_url .= '&notes_iframe';
192                        }
193                        wp_safe_redirect( $connect_url );
194                        $this->do_exit();
195                } elseif ( ! isset( $_GET['calypso_env'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no site changes.
196                        ( new CookieState() )->state( 'message', 'already_authorized' );
197                        wp_safe_redirect( $redirect );
198                        $this->do_exit();
199                } else {
200                        $connect_url = add_query_arg(
201                                array(
202                                        'from'               => $from,
203                                        'already_authorized' => true,
204                                ),
205                                $this->connection->get_authorization_url()
206                        );
207                        wp_safe_redirect( $connect_url );
208                        $this->do_exit();
209                }
210        }
211}
Note: See TracBrowser for help on using the repository browser.