PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 All 502 releases
jetpack / _inc / lib / core-api / wpcom-endpoints / service-api-keys.php

service-api-keys.php in Jetpack – WP Security, Backup, Speed, & Growth 16.2, at _inc/lib/core-api/wpcom-endpoints/service-api-keys.php

340 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Get and save API keys for a site.
4 *
5 * @package automattic/jetpack
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit( 0 );
10 }
11
12 /**
13 * Service API Keys: Exposes 3rd party api keys that are used on a site.
14 *
15 * [
16 * { # Availability Object. See schema for more detail.
17 * code: (string) Displays success if the operation was successfully executed and an error code if it was not
18 * service: (string) The name of the service in question
19 * service_api_key: (string) The API key used by the service empty if one is not set yet
20 * service_api_key_source: (string) The source of the API key, defaults to "site"
21 * message: (string) User friendly message
22 * },
23 * ...
24 * ]
25 *
26 * @since 6.9
27 */
28 class WPCOM_REST_API_V2_Endpoint_Service_API_Keys extends WP_REST_Controller {
29
30 /**
31 * Constructor.
32 */
33 public function __construct() {
34 $this->namespace = 'wpcom/v2';
35 $this->rest_base = 'service-api-keys';
36
37 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
38 }
39
40 /**
41 * Register endpoint routes.
42 */
43 public function register_routes() {
44 register_rest_route(
45 'wpcom/v2',
46 '/service-api-keys/(?P<service>[a-z\-_]+)',
47 array(
48 array(
49 'methods' => WP_REST_Server::READABLE,
50 'callback' => array( __CLASS__, 'get_service_api_key' ),
51 'permission_callback' => '__return_true',
52 ),
53 array(
54 'methods' => WP_REST_Server::EDITABLE,
55 'callback' => array( __CLASS__, 'update_service_api_key' ),
56 'permission_callback' => array( __CLASS__, 'edit_others_posts_check' ),
57 'args' => array(
58 'service_api_key' => array(
59 'required' => true,
60 'type' => 'string',
61 ),
62 ),
63 ),
64 array(
65 'methods' => WP_REST_Server::DELETABLE,
66 'callback' => array( __CLASS__, 'delete_service_api_key' ),
67 'permission_callback' => array( __CLASS__, 'edit_others_posts_check' ),
68 ),
69 )
70 );
71 }
72
73 /**
74 * Permission check.
75 */
76 public static function edit_others_posts_check() {
77 if ( current_user_can( 'edit_others_posts' ) ) {
78 return true;
79 }
80
81 $user_permissions_error_msg = esc_html__(
82 'You do not have the correct user permissions to perform this action.
83 Please contact your site admin if you think this is a mistake.',
84 'jetpack'
85 );
86
87 return new WP_Error( 'invalid_user_permission_edit_others_posts', $user_permissions_error_msg, rest_authorization_required_code() );
88 }
89
90 /**
91 * Return the available Gutenberg extensions schema
92 *
93 * @return array Service API Key schema
94 */
95 public function get_public_item_schema() {
96 $schema = array(
97 '$schema' => 'http://json-schema.org/draft-04/schema#',
98 'title' => 'service-api-keys',
99 'type' => 'object',
100 'properties' => array(
101 'code' => array(
102 'description' => __( 'Displays success if the operation was successfully executed and an error code if it was not', 'jetpack' ),
103 'type' => 'string',
104 ),
105 'service' => array(
106 'description' => __( 'The name of the service in question', 'jetpack' ),
107 'type' => 'string',
108 ),
109 'service_api_key' => array(
110 'description' => __( 'The API key used by the service. Empty if none has been set yet', 'jetpack' ),
111 'type' => 'string',
112 ),
113 'service_api_key_source' => array(
114 'description' => __( 'The source of the API key. Defaults to "site"', 'jetpack' ),
115 'type' => 'string',
116 ),
117 'message' => array(
118 'description' => __( 'User friendly message', 'jetpack' ),
119 'type' => 'string',
120 ),
121 ),
122 );
123
124 return $this->add_additional_fields_schema( $schema );
125 }
126
127 /**
128 * Get third party plugin API keys.
129 *
130 * @param WP_REST_Request $request {
131 * Array of parameters received by request.
132 *
133 * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
134 * }
135 */
136 public static function get_service_api_key( $request ) {
137 $service = self::validate_service_api_service( $request['service'] );
138 if ( ! $service ) {
139 return self::service_api_invalid_service_response();
140 }
141
142 switch ( $service ) {
143 case 'mapbox':
144 if ( ! class_exists( 'Jetpack_Mapbox_Helper' ) ) {
145 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-mapbox-helper.php';
146 }
147 $mapbox = Jetpack_Mapbox_Helper::get_access_token();
148 $service_api_key = $mapbox['key'];
149 $service_api_key_source = $mapbox['source'];
150 break;
151 default:
152 $option = self::key_for_api_service( $service );
153 $service_api_key = Jetpack_Options::get_option( $option, '' );
154 $service_api_key_source = 'site';
155 }
156
157 $message = esc_html__( 'API key retrieved successfully.', 'jetpack' );
158
159 return array(
160 'code' => 'success',
161 'service' => $service,
162 'service_api_key' => $service_api_key,
163 'service_api_key_source' => $service_api_key_source,
164 'message' => $message,
165 );
166 }
167
168 /**
169 * Update third party plugin API keys.
170 *
171 * @param WP_REST_Request $request {
172 * Array of parameters received by request.
173 *
174 * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
175 * }
176 */
177 public static function update_service_api_key( $request ) {
178 $service = self::validate_service_api_service( $request['service'] );
179 if ( ! $service ) {
180 return self::service_api_invalid_service_response();
181 }
182 $json_params = $request->get_json_params();
183 $params = ! empty( $json_params ) ? $json_params : $request->get_body_params();
184 $service_api_key = trim( $params['service_api_key'] );
185 $option = self::key_for_api_service( $service );
186
187 $validation = self::validate_service_api_key( $service_api_key, $service );
188 if ( ! $validation['status'] ) {
189 return new WP_Error( 'invalid_key', esc_html__( 'Invalid API Key', 'jetpack' ), array( 'status' => 404 ) );
190 }
191 $message = esc_html__( 'API key updated successfully.', 'jetpack' );
192 Jetpack_Options::update_option( $option, $service_api_key );
193 return array(
194 'code' => 'success',
195 'service' => $service,
196 'service_api_key' => Jetpack_Options::get_option( $option, '' ),
197 'service_api_key_source' => 'site',
198 'message' => $message,
199 );
200 }
201
202 /**
203 * Delete a third party plugin API key.
204 *
205 * @param WP_REST_Request $request {
206 * Array of parameters received by request.
207 *
208 * @type string $slug Plugin slug with the syntax 'plugin-directory/plugin-main-file.php'.
209 * }
210 */
211 public static function delete_service_api_key( $request ) {
212 $service = self::validate_service_api_service( $request['service'] );
213 if ( ! $service ) {
214 return self::service_api_invalid_service_response();
215 }
216 $option = self::key_for_api_service( $service );
217 Jetpack_Options::delete_option( $option );
218 $message = esc_html__( 'API key deleted successfully.', 'jetpack' );
219
220 switch ( $service ) {
221 case 'mapbox':
222 // After deleting a custom Mapbox key, try to revert to the WordPress.com one if available.
223 if ( ! class_exists( 'Jetpack_Mapbox_Helper' ) ) {
224 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-mapbox-helper.php';
225 }
226 $mapbox = Jetpack_Mapbox_Helper::get_access_token();
227 $service_api_key = $mapbox['key'];
228 $service_api_key_source = $mapbox['source'];
229 break;
230 default:
231 $service_api_key = Jetpack_Options::get_option( $option, '' );
232 $service_api_key_source = 'site';
233 }
234
235 return array(
236 'code' => 'success',
237 'service' => $service,
238 'service_api_key' => $service_api_key,
239 'service_api_key_source' => $service_api_key_source,
240 'message' => $message,
241 );
242 }
243
244 /**
245 * Validate the service provided in /service-api-keys/ endpoints.
246 * To add a service to these endpoints, add the service name to $valid_services
247 * and add '{service name}_api_key' to the non-compact return array in get_option_names(),
248 * in class-jetpack-options.php
249 *
250 * @param string $service The service the API key is for.
251 * @return string Returns the service name if valid, null if invalid.
252 */
253 public static function validate_service_api_service( $service = null ) {
254 $valid_services = array(
255 'mapbox',
256 );
257 return in_array( $service, $valid_services, true ) ? $service : null;
258 }
259
260 /**
261 * Error response for invalid service API key requests with an invalid service.
262 */
263 public static function service_api_invalid_service_response() {
264 return new WP_Error(
265 'invalid_service',
266 esc_html__( 'Invalid Service', 'jetpack' ),
267 array( 'status' => 404 )
268 );
269 }
270
271 /**
272 * Validate API Key
273 *
274 * @param string $key The API key to be validated.
275 * @param string $service The service the API key is for.
276 */
277 public static function validate_service_api_key( $key = null, $service = null ) {
278 $validation = false;
279 switch ( $service ) {
280 case 'mapbox':
281 $validation = self::validate_service_api_key_mapbox( $key );
282 break;
283 }
284 return $validation;
285 }
286
287 /**
288 * Validate Mapbox API key
289 * Based loosely on https://github.com/mapbox/geocoding-example/blob/master/php/MapboxTest.php
290 *
291 * @param string $key The API key to be validated.
292 */
293 public static function validate_service_api_key_mapbox( $key ) {
294 $status = true;
295 $msg = null;
296 $mapbox_url = sprintf(
297 'https://api.mapbox.com?%s',
298 $key
299 );
300 $mapbox_response = wp_safe_remote_get( esc_url_raw( $mapbox_url ) );
301 $mapbox_body = wp_remote_retrieve_body( $mapbox_response );
302 if ( '{"api":"mapbox"}' !== $mapbox_body ) {
303 $status = false;
304 $msg = esc_html__( 'Can\'t connect to Mapbox', 'jetpack' );
305 return array(
306 'status' => $status,
307 'error_message' => $msg,
308 );
309 }
310 $mapbox_geocode_url = esc_url_raw(
311 sprintf(
312 'https://api.mapbox.com/geocoding/v5/mapbox.places/%s.json?access_token=%s',
313 '1+broadway+new+york+ny+usa',
314 $key
315 )
316 );
317 $mapbox_geocode_response = wp_safe_remote_get( esc_url_raw( $mapbox_geocode_url ) );
318 $mapbox_geocode_body = wp_remote_retrieve_body( $mapbox_geocode_response );
319 $mapbox_geocode_json = json_decode( $mapbox_geocode_body );
320 if ( isset( $mapbox_geocode_json->message ) || ! isset( $mapbox_geocode_json->query ) ) {
321 $status = false;
322 $msg = $mapbox_geocode_json->message ?? 'Unknown error';
323 }
324 return array(
325 'status' => $status,
326 'error_message' => $msg,
327 );
328 }
329
330 /**
331 * Create site option key for service
332 *
333 * @param string $service The service to create key for.
334 */
335 private static function key_for_api_service( $service ) {
336 return $service . '_api_key';
337 }
338 }
339 wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Service_API_Keys' );
340