1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* WooCommerce.com Product Installation. |
4
|
|
|
* |
5
|
|
|
* @package WooCommerce\WooCommerce_Site |
6
|
|
|
* @since 3.7.0 |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
defined( 'ABSPATH' ) || exit; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* WC_WCCOM_Site Class |
13
|
|
|
* |
14
|
|
|
* Main class for WooCommerce.com connected site. |
15
|
|
|
*/ |
16
|
|
|
class WC_WCCOM_Site { |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Load the WCCOM site class. |
20
|
|
|
* |
21
|
|
|
* @since 3.7.0 |
22
|
|
|
*/ |
23
|
|
|
public static function load() { |
24
|
|
|
self::includes(); |
25
|
|
|
|
26
|
|
|
add_action( 'woocommerce_wccom_install_products', array( 'WC_WCCOM_Site_Installer', 'install' ) ); |
27
|
|
|
add_filter( 'determine_current_user', array( __CLASS__, 'authenticate_wccom' ), 14 ); |
28
|
|
|
add_action( 'woocommerce_rest_api_get_rest_namespaces', array( __CLASS__, 'register_rest_namespace' ) ); |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Include support files. |
33
|
|
|
* |
34
|
|
|
* @since 3.7.0 |
35
|
|
|
*/ |
36
|
|
|
protected static function includes() { |
37
|
|
|
require_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper.php'; |
38
|
|
|
require_once WC_ABSPATH . 'includes/wccom-site/class-wc-wccom-site-installer.php'; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Authenticate WooCommerce.com request. |
43
|
|
|
* |
44
|
|
|
* @since 3.7.0 |
45
|
|
|
* @param int|false $user_id User ID. |
46
|
|
|
* @return int|false |
47
|
|
|
*/ |
48
|
|
|
public static function authenticate_wccom( $user_id ) { |
49
|
|
|
if ( ! empty( $user_id ) || ! self::is_request_to_wccom_site_rest_api() ) { |
50
|
|
|
return $user_id; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
$auth_header = self::get_authorization_header(); |
54
|
|
|
if ( empty( $auth_header ) ) { |
55
|
|
|
return false; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
59
|
|
|
$request_auth = trim( $auth_header ); |
60
|
|
|
if ( stripos( $request_auth, 'Bearer ' ) !== 0 ) { |
61
|
|
|
return false; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
if ( empty( $_SERVER['HTTP_X_WOO_SIGNATURE'] ) ) { |
65
|
|
|
return false; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
require_once WC_ABSPATH . 'includes/admin/helper/class-wc-helper-options.php'; |
69
|
|
|
$access_token = trim( substr( $request_auth, 7 ) ); |
70
|
|
|
$site_auth = WC_Helper_Options::get( 'auth' ); |
71
|
|
|
if ( empty( $site_auth['access_token'] ) || ! hash_equals( $access_token, $site_auth['access_token'] ) ) { |
72
|
|
|
return false; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
$body = WP_REST_Server::get_raw_data(); |
76
|
|
|
$signature = trim( $_SERVER['HTTP_X_WOO_SIGNATURE'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
77
|
|
|
|
78
|
|
|
if ( ! self::verify_wccom_request( $body, $signature, $site_auth['access_token_secret'] ) ) { |
79
|
|
|
return false; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
$user = get_user_by( 'id', $site_auth['user_id'] ); |
83
|
|
|
if ( ! $user ) { |
84
|
|
|
return false; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
return $user; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Get the authorization header. |
92
|
|
|
* |
93
|
|
|
* On certain systems and configurations, the Authorization header will be |
94
|
|
|
* stripped out by the server or PHP. Typically this is then used to |
95
|
|
|
* generate `PHP_AUTH_USER`/`PHP_AUTH_PASS` but not passed on. We use |
96
|
|
|
* `getallheaders` here to try and grab it out instead. |
97
|
|
|
* |
98
|
|
|
* @since 3.7.0 |
99
|
|
|
* @return string Authorization header if set. |
100
|
|
|
*/ |
101
|
|
View Code Duplication |
protected static function get_authorization_header() { |
|
|
|
|
102
|
|
|
if ( ! empty( $_SERVER['HTTP_AUTHORIZATION'] ) ) { |
103
|
|
|
return wp_unslash( $_SERVER['HTTP_AUTHORIZATION'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
if ( function_exists( 'getallheaders' ) ) { |
107
|
|
|
$headers = getallheaders(); |
108
|
|
|
// Check for the authoization header case-insensitively. |
109
|
|
|
foreach ( $headers as $key => $value ) { |
110
|
|
|
if ( 'authorization' === strtolower( $key ) ) { |
111
|
|
|
return $value; |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
return ''; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Check if this is a request to WCCOM Site REST API. |
121
|
|
|
* |
122
|
|
|
* @since 3.7.0 |
123
|
|
|
* @return bool |
124
|
|
|
*/ |
125
|
|
|
protected static function is_request_to_wccom_site_rest_api() { |
126
|
|
|
$request_uri = add_query_arg( array() ); |
127
|
|
|
$rest_prefix = trailingslashit( rest_get_url_prefix() ); |
128
|
|
|
$request_uri = esc_url_raw( wp_unslash( $request_uri ) ); |
129
|
|
|
|
130
|
|
|
return false !== strpos( $request_uri, $rest_prefix . 'wccom-site/' ); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* Verify WooCommerce.com request from a given body and signature request. |
135
|
|
|
* |
136
|
|
|
* @since 3.7.0 |
137
|
|
|
* @param string $body Request body. |
138
|
|
|
* @param string $signature Request signature found in X-Woo-Signature header. |
139
|
|
|
* @param string $access_token_secret Access token secret for this site. |
140
|
|
|
* @return bool |
141
|
|
|
*/ |
142
|
|
|
protected static function verify_wccom_request( $body, $signature, $access_token_secret ) { |
143
|
|
|
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
144
|
|
|
$data = array( |
145
|
|
|
'host' => $_SERVER['HTTP_HOST'], |
146
|
|
|
'request_uri' => $_SERVER['REQUEST_URI'], |
147
|
|
|
'method' => strtoupper( $_SERVER['REQUEST_METHOD'] ), |
148
|
|
|
); |
149
|
|
|
// phpcs:enable |
150
|
|
|
|
151
|
|
|
if ( ! empty( $body ) ) { |
152
|
|
|
$data['body'] = $body; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
$expected_signature = hash_hmac( 'sha256', wp_json_encode( $data ), $access_token_secret ); |
156
|
|
|
|
157
|
|
|
return hash_equals( $expected_signature, $signature ); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Register wccom-site REST namespace. |
162
|
|
|
* |
163
|
|
|
* @since 3.7.0 |
164
|
|
|
* @param array $namespaces List of registered namespaces. |
165
|
|
|
* @return array Registered namespaces. |
166
|
|
|
*/ |
167
|
1 |
|
public static function register_rest_namespace( $namespaces ) { |
168
|
1 |
|
require_once WC_ABSPATH . 'includes/wccom-site/rest-api/endpoints/class-wc-rest-wccom-site-installer-controller.php'; |
169
|
|
|
|
170
|
1 |
|
$namespaces['wccom-site/v1'] = array( |
171
|
|
|
'installer' => 'WC_REST_WCCOM_Site_Installer_Controller', |
172
|
|
|
); |
173
|
|
|
|
174
|
1 |
|
return $namespaces; |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
WC_WCCOM_Site::load(); |
179
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.