PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.2
Elementor Website Builder – more than just a page builder v4.3.2
4.3.2 4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 All 455 releases
elementor / core / common / modules / events-manager / rest-api / events-proxy-rest-api.php

events-proxy-rest-api.php in Elementor Website Builder – more than just a page builder 4.3.2, at core/common/modules/events-manager/rest-api/events-proxy-rest-api.php

212 lines 5.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Core\Common\Modules\EventsManager\RestApi;
4
5 use Elementor\Core\Common\Modules\EventsManager\Module;
6 use Elementor\Utils;
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit; // Exit if accessed directly.
10 }
11
12 class Events_Proxy_REST_API {
13 const API_NAMESPACE = 'elementor/v1';
14 const API_BASE = 'events';
15
16 const REQUEST_TIMEOUT = 3;
17 const MAX_BODY_BYTES = 10 * MB_IN_BYTES;
18
19 const FORWARDED_REQUEST_HEADERS = [ 'content-type', 'content-encoding' ];
20
21 const RAW_RESPONSE_HEADER = 'X-Elementor-Raw-Proxy-Response';
22
23 const ASYNC_DISPATCH_PATHS = [ 'track', 'engage', 'groups', 'record' ];
24 const ASYNC_DISPATCH_SUCCESS_BODY = '1';
25
26 public function register_hooks() {
27 add_action( 'rest_api_init', fn() => $this->register_routes() );
28 add_filter( 'rest_authentication_errors', [ $this, 'bypass_nonce_check_for_own_routes' ], 0 );
29 add_filter( 'rest_pre_serve_request', [ $this, 'maybe_serve_raw_response' ], 10, 2 );
30 }
31
32 public function bypass_nonce_check_for_own_routes( $result ) {
33 if ( $this->is_own_route_request() ) {
34 return true;
35 }
36
37 return $result;
38 }
39
40 public function maybe_serve_raw_response( $served, $result ) {
41 if ( ! ( $result instanceof \WP_REST_Response ) ) {
42 return $served;
43 }
44
45 $headers = $result->get_headers();
46
47 if ( empty( $headers[ self::RAW_RESPONSE_HEADER ] ) ) {
48 return $served;
49 }
50
51 if ( ! headers_sent() ) {
52 status_header( $result->get_status() );
53
54 foreach ( $headers as $name => $value ) {
55 if ( self::RAW_RESPONSE_HEADER === $name ) {
56 continue;
57 }
58
59 header( "{$name}: {$value}" );
60 }
61 }
62
63 Utils::print_unescaped_internal_string( $result->get_data() );
64
65 return true;
66 }
67
68 private function is_own_route_request(): bool {
69 global $wp;
70
71 $route = $wp->query_vars['rest_route'] ?? null;
72
73 if ( ! is_string( $route ) ) {
74 return false;
75 }
76
77 return 0 === strpos( $route, '/' . self::API_NAMESPACE . '/' . self::API_BASE . '/' );
78 }
79
80 private function register_routes() {
81 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/api/(?P<path>.+)', [
82 'methods' => [ 'GET', 'POST' ],
83 'callback' => fn( \WP_REST_Request $request ) => $this->proxy_api_request( $request ),
84 'permission_callback' => fn() => current_user_can( 'edit_posts' ),
85 ] );
86
87 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/libs/(?P<file>[\w\-.]+)', [
88 'methods' => 'GET',
89 'callback' => fn( \WP_REST_Request $request ) => $this->proxy_libs_request( $request ),
90 'permission_callback' => fn() => current_user_can( 'edit_posts' ),
91 ] );
92 }
93
94 private function proxy_api_request( \WP_REST_Request $request ) {
95 $path = ltrim( (string) $request->get_param( 'path' ), '/' );
96 $query = $request->get_query_params();
97
98 unset( $query['rest_route'] );
99
100 $url = Module::get_mixpanel_api_host() . '/' . $path;
101
102 if ( ! empty( $query ) ) {
103 $url = add_query_arg( $query, $url );
104 }
105
106 return $this->forward_request( $url, $request, $this->is_fire_and_forget_path( $path ) );
107 }
108
109 private function is_fire_and_forget_path( string $path ): bool {
110 return in_array( strtok( $path, '/' ), self::ASYNC_DISPATCH_PATHS, true );
111 }
112
113 private function proxy_libs_request( \WP_REST_Request $request ) {
114 $file = (string) $request->get_param( 'file' );
115 $url = Module::get_mixpanel_lib_host() . '/' . $file;
116
117 return $this->forward_request( $url, $request, false );
118 }
119
120 private function forward_request( string $url, \WP_REST_Request $request, bool $async = true ): \WP_REST_Response {
121 $body = $request->get_body();
122
123 if ( strlen( $body ) > self::MAX_BODY_BYTES ) {
124 return $this->build_raw_response( '', 413 );
125 }
126
127 $args = [
128 'method' => $request->get_method(),
129 'timeout' => self::REQUEST_TIMEOUT,
130 'redirection' => 2,
131 'headers' => $this->build_forwarded_headers( $request ),
132 ];
133
134 if ( ! empty( $body ) ) {
135 $args['body'] = $body;
136 }
137
138 if ( $async ) {
139 $args['blocking'] = false;
140
141 wp_safe_remote_request( $url, $args );
142
143 return $this->build_raw_response( self::ASYNC_DISPATCH_SUCCESS_BODY, 200, 'text/plain' );
144 }
145
146 $response = wp_safe_remote_request( $url, $args );
147
148 if ( is_wp_error( $response ) ) {
149 return $this->build_raw_response( '', 502 );
150 }
151
152 return $this->build_raw_response(
153 wp_remote_retrieve_body( $response ),
154 (int) wp_remote_retrieve_response_code( $response ),
155 wp_remote_retrieve_header( $response, 'content-type' )
156 );
157 }
158
159 private function build_forwarded_headers( \WP_REST_Request $request ): array {
160 $headers = [];
161
162 foreach ( self::FORWARDED_REQUEST_HEADERS as $header_name ) {
163 $value = $request->get_header( $header_name );
164
165 if ( null !== $value && '' !== $value ) {
166 $headers[ $header_name ] = $value;
167 }
168 }
169
170 $mixpanel_authorization = $this->build_mixpanel_authorization_header();
171
172 if ( '' !== $mixpanel_authorization ) {
173 $headers['authorization'] = $mixpanel_authorization;
174 }
175
176 $remote_address = Utils::get_super_global_value( $_SERVER, 'REMOTE_ADDR' );
177 $host = Utils::get_super_global_value( $_SERVER, 'HTTP_HOST' );
178
179 if ( $remote_address ) {
180 $headers['x-forwarded-for'] = $remote_address;
181 }
182
183 if ( $host ) {
184 $headers['x-forwarded-host'] = $host;
185 }
186
187 return $headers;
188 }
189
190 private function build_mixpanel_authorization_header(): string {
191 $token = ELEMENTOR_EDITOR_EVENTS_MIXPANEL_TOKEN;
192
193 if ( empty( $token ) ) {
194 return '';
195 }
196
197 return 'Basic ' . base64_encode( $token . ':' );
198 }
199
200 private function build_raw_response( string $body, int $status, string $content_type = '' ) {
201 $response = new \WP_REST_Response( $body, $status );
202
203 if ( $content_type ) {
204 $response->header( 'Content-Type', $content_type );
205 }
206
207 $response->header( self::RAW_RESPONSE_HEADER, '1' );
208
209 return $response;
210 }
211 }
212