Completed
Pull Request — master (#11616)
by
unknown
07:51
created

WC_Auth::_format_api_key_data()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 5
c 1
b 0
f 1
nc 2
nop 1
dl 0
loc 8
rs 9.4285
1
<?php
2
/**
3
 * WooCommerce Auth
4
 *
5
 * Handles wc-auth endpoint requests.
6
 *
7
 * @author   WooThemes
8
 * @category API
9
 * @package  WooCommerce/API
10
 * @since    2.4.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
if ( ! class_exists( 'WC_Auth' ) ) :
18
19
class WC_Auth {
20
21
	/**
22
	 * Version.
23
	 *
24
	 * @var int
25
	 */
26
	const VERSION = 1;
27
28
	/**
29
	 * Setup class.
30
	 *
31
	 * @since 2.4.0
32
	 */
33
	public function __construct() {
34
		// Add query vars
35
		add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 );
36
37
		// Register auth endpoint
38
		add_action( 'init', array( __CLASS__, 'add_endpoint' ), 0 );
39
40
		// Handle auth requests
41
		add_action( 'parse_request', array( $this, 'handle_auth_requests' ), 0 );
42
	}
43
44
	/**
45
	 * Add query vars.
46
	 *
47
	 * @since  2.4.0
48
	 *
49
	 * @param  array $vars
50
	 *
51
	 * @return string[]
52
	 */
53
	public function add_query_vars( $vars ) {
54
		$vars[] = 'wc-auth-version';
55
		$vars[] = 'wc-auth-route';
56
		return $vars;
57
	}
58
59
	/**
60
	 * Add auth endpoint.
61
	 *
62
	 * @since 2.4.0
63
	 */
64
	public static function add_endpoint() {
65
		add_rewrite_rule( '^wc-auth/v([1]{1})/(.*)?', 'index.php?wc-auth-version=$matches[1]&wc-auth-route=$matches[2]', 'top' );
66
	}
67
68
	/**
69
	 * Get scope name.
70
	 *
71
	 * @since 2.4.0
72
	 *
73
	 * @param  string $scope
74
	 *
75
	 * @return string
76
	 */
77
	protected function get_i18n_scope( $scope ) {
78
		$permissions = array(
79
			'read'       => __( 'Read', 'woocommerce' ),
80
			'write'      => __( 'Write', 'woocommerce' ),
81
			'read_write' => __( 'Read/Write', 'woocommerce' ),
82
		);
83
84
		return $permissions[ $scope ];
85
	}
86
87
	/**
88
	 * Return a list of permissions a scope allows.
89
	 *
90
	 * @since  2.4.0
91
	 *
92
	 * @param  string $scope
93
	 *
94
	 * @return array
95
	 */
96
	protected function get_permissions_in_scope( $scope ) {
97
		$permissions = array();
98
		switch ( $scope )  {
99 View Code Duplication
			case 'read' :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
100
				$permissions[] = __( 'View coupons', 'woocommerce' );
101
				$permissions[] = __( 'View customers', 'woocommerce' );
102
				$permissions[] = __( 'View orders and sales reports', 'woocommerce' );
103
				$permissions[] = __( 'View products', 'woocommerce' );
104
			break;
105 View Code Duplication
			case 'write' :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
106
				$permissions[] = __( 'Create webhooks', 'woocommerce' );
107
				$permissions[] = __( 'Create coupons', 'woocommerce' );
108
				$permissions[] = __( 'Create customers', 'woocommerce' );
109
				$permissions[] = __( 'Create orders', 'woocommerce' );
110
				$permissions[] = __( 'Create products', 'woocommerce' );
111
			break;
112 View Code Duplication
			case 'read_write' :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
113
				$permissions[] = __( 'Create webhooks', 'woocommerce' );
114
				$permissions[] = __( 'View and manage coupons', 'woocommerce' );
115
				$permissions[] = __( 'View and manage customers', 'woocommerce' );
116
				$permissions[] = __( 'View and manage orders and sales reports', 'woocommerce' );
117
				$permissions[] = __( 'View and manage products', 'woocommerce' );
118
			break;
119
		}
120
		return apply_filters( 'woocommerce_api_permissions_in_scope', $permissions, $scope );
121
	}
122
123
	/**
124
	 * Build auth urls.
125
	 *
126
	 * @since  2.4.0
127
	 *
128
	 * @param  array $data
129
	 * @param  string $endpoint
130
	 *
131
	 * @return string
132
	 */
133
	protected function build_url( $data, $endpoint ) {
134
		$url = wc_get_endpoint_url( 'wc-auth/v' . self::VERSION, $endpoint, home_url( '/' ) );
135
136
		return add_query_arg( array(
137
			'app_name'            => wc_clean( $data['app_name'] ),
138
			'user_id'             => wc_clean( $data['user_id'] ),
139
			'return_url'          => urlencode( $this->get_formatted_url( $data['return_url'] ) ),
140
			'callback_url'        => urlencode( $this->get_formatted_url( $data['callback_url'] ) ),
141
			'scope'               => wc_clean( $data['scope'] ),
142
		), $url );
143
	}
144
145
	/**
146
	 * Decode and format a URL.
147
	 * @param  string $url
148
	 * @return array
149
	 */
150
	protected function get_formatted_url( $url ) {
151
		$url = urldecode( $url );
152
153
		if ( ! strstr( $url, '://' ) ) {
154
			$url = 'https://' . $url;
155
		}
156
157
		return $url;
158
	}
159
160
	/**
161
	 * Make validation.
162
	 *
163
	 * @since  2.4.0
164
	 */
165
	protected function make_validation() {
166
		$params = array(
167
			'app_name',
168
			'user_id',
169
			'return_url',
170
			'callback_url',
171
			'scope'
172
		);
173
174
		foreach ( $params as $param ) {
175
			if ( empty( $_REQUEST[ $param ] ) ) {
176
				throw new Exception( sprintf( __( 'Missing parameter %s', 'woocommerce' ), $param ) );
177
			}
178
		}
179
180
		if ( ! in_array( $_REQUEST['scope'], array( 'read', 'write', 'read_write' ) ) ) {
181
			throw new Exception( sprintf( __( 'Invalid scope %s', 'woocommerce' ), wc_clean( $_REQUEST['scope'] ) ) );
182
		}
183
184
		foreach ( array( 'return_url', 'callback_url' ) as $param ) {
185
			$param = $this->get_formatted_url( $_REQUEST[ $param ] );
186
187
			if ( false === filter_var( $param, FILTER_VALIDATE_URL ) ) {
188
				throw new Exception( sprintf( __( 'The %s is not a valid URL', 'woocommerce' ), $param ) );
189
			}
190
		}
191
192
		$callback_url = $this->get_formatted_url( $_REQUEST['callback_url'] );
193
194
		if ( 0 !== stripos( $callback_url, 'https://' ) ) {
195
			throw new Exception( __( 'The callback_url need to be over SSL', 'woocommerce' ) );
196
		}
197
	}
198
199
	/**
200
	 * Create keys.
201
	 *
202
	 * @since  2.4.0
203
	 *
204
	 * @param  string $app_name
205
	 * @param  string $app_user_id
206
	 * @param  string $scope
207
	 *
208
	 * @return array
209
	 */
210
	protected function create_keys( $app_name, $app_user_id, $scope ) {
211
		$description = sprintf( __( '%s - API %s (created on %s at %s).', 'woocommerce' ), wc_clean( $app_name ), $this->get_i18n_scope( $scope ), date_i18n( wc_date_format() ), date_i18n( wc_time_format() ) );
212
		$user        = wp_get_current_user();
213
214
		// Created API keys.
215
		$result = WC_Auth::create_api_key( array(
216
			'description' => $description,
217
			'user_id' => $user->ID,
218
			'scope' => $scope
219
		) );
220
221
		// @TODO: Apparently $app_user_id was not used previously (before 2.6.2) but for return array
222
		// @TODO: It could be deprecated and user only current user or actually use $app_user_id instead
223
224
		$result['user_id'] = $app_user_id;
225
		return $result;
226
	}
227
228
	/**
229
	 * Post consumer data.
230
	 *
231
	 * @since  2.4.0
232
	 *
233
	 * @param  array  $consumer_data
234
	 * @param  string $url
235
	 *
236
	 * @return bool
237
	 * @throws Exception
238
	 */
239
	protected function post_consumer_data( $consumer_data, $url ) {
240
		$params = array(
241
			'body'      => json_encode( $consumer_data ),
242
			'timeout'   => 60,
243
			'headers'   => array(
244
				'Content-Type' => 'application/json;charset=' . get_bloginfo( 'charset' ),
245
			)
246
		);
247
248
		$response = wp_safe_remote_post( esc_url_raw( $url ), $params );
249
250
		if ( is_wp_error( $response ) ) {
251
			throw new Exception( $response->get_error_message() );
252
		} else if ( 200 != $response['response']['code'] ) {
253
			throw new Exception( __( 'An error occurred in the request and at the time were unable to send the consumer data', 'woocommerce' ) );
254
		}
255
256
		return true;
257
	}
258
259
	/**
260
	 * Handle auth requests.
261
	 *
262
	 * @since 2.4.0
263
	 */
264
	public function handle_auth_requests() {
265
		global $wp;
266
267
		if ( ! empty( $_GET['wc-auth-version'] ) ) {
268
			$wp->query_vars['wc-auth-version'] = $_GET['wc-auth-version'];
269
		}
270
271
		if ( ! empty( $_GET['wc-auth-route'] ) ) {
272
			$wp->query_vars['wc-auth-route'] = $_GET['wc-auth-route'];
273
		}
274
275
		// wc-auth endpoint requests
276
		if ( ! empty( $wp->query_vars['wc-auth-version'] ) && ! empty( $wp->query_vars['wc-auth-route'] ) ) {
277
			$this->auth_endpoint( $wp->query_vars['wc-auth-route'] );
278
		}
279
	}
280
281
	/**
282
	 * Auth endpoint.
283
	 *
284
	 * @since 2.4.0
285
	 *
286
	 * @param string $route
287
	 */
288
	protected function auth_endpoint( $route ) {
289
		ob_start();
290
291
		$consumer_data = array();
292
293
		try {
294
			if ( 'yes' !== get_option( 'woocommerce_api_enabled' ) ) {
295
				throw new Exception( __( 'API disabled!', 'woocommerce' ) );
296
			}
297
298
			$route = strtolower( wc_clean( $route ) );
299
			$this->make_validation();
300
301
			// Login endpoint
302
			if ( 'login' == $route && ! is_user_logged_in() ) {
303
				wc_get_template( 'auth/form-login.php', array(
304
					'app_name'     => $_REQUEST['app_name'],
305
					'return_url'   => add_query_arg( array( 'success' => 0, 'user_id' => wc_clean( $_REQUEST['user_id'] ) ), $this->get_formatted_url( $_REQUEST['return_url'] ) ),
306
					'redirect_url' => $this->build_url( $_REQUEST, 'authorize' ),
307
				) );
308
309
				exit;
310
311
			// Redirect with user is logged in
312
			} else if ( 'login' == $route && is_user_logged_in() ) {
313
				wp_redirect( esc_url_raw( $this->build_url( $_REQUEST, 'authorize' ) ) );
314
				exit;
315
316
			// Redirect with user is not logged in and trying to access the authorize endpoint
317
			} else if ( 'authorize' == $route && ! is_user_logged_in() ) {
318
				wp_redirect( esc_url_raw( $this->build_url( $_REQUEST, 'login' ) ) );
319
				exit;
320
321
			// Authorize endpoint
322
			} else if ( 'authorize' == $route && current_user_can( 'manage_woocommerce' ) ) {
323
				wc_get_template( 'auth/form-grant-access.php', array(
324
					'app_name'    => $_REQUEST['app_name'],
325
					'return_url'  => add_query_arg( array( 'success' => 0, 'user_id' => wc_clean( $_REQUEST['user_id'] ) ), $this->get_formatted_url( $_REQUEST['return_url'] ) ),
326
					'scope'       => $this->get_i18n_scope( wc_clean( $_REQUEST['scope'] ) ),
327
					'permissions' => $this->get_permissions_in_scope( wc_clean( $_REQUEST['scope'] ) ),
328
					'granted_url' => wp_nonce_url( $this->build_url( $_REQUEST, 'access_granted' ), 'wc_auth_grant_access', 'wc_auth_nonce' ),
329
					'logout_url'  => wp_logout_url( $this->build_url( $_REQUEST, 'login' ) ),
330
					'user'        => wp_get_current_user()
331
				) );
332
				exit;
333
334
			// Granted access endpoint
335
			} else if ( 'access_granted' == $route && current_user_can( 'manage_woocommerce' ) ) {
336
				if ( ! isset( $_GET['wc_auth_nonce'] ) || ! wp_verify_nonce( $_GET['wc_auth_nonce'], 'wc_auth_grant_access' ) ) {
337
					throw new Exception( __( 'Invalid nonce verification', 'woocommerce' ) );
338
				}
339
340
				$consumer_data = $this->create_keys( $_REQUEST['app_name'], $_REQUEST['user_id'], $_REQUEST['scope'] );
341
				$response      = $this->post_consumer_data( $consumer_data, $this->get_formatted_url( $_REQUEST['callback_url'] ) );
0 ignored issues
show
Security Bug introduced by
It seems like $consumer_data defined by $this->create_keys($_REQ...'], $_REQUEST['scope']) on line 340 can also be of type false; however, WC_Auth::post_consumer_data() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
342
343
				if ( $response ) {
344
					wp_redirect( esc_url_raw( add_query_arg( array( 'success' => 1, 'user_id' => wc_clean( $_REQUEST['user_id'] ) ), $this->get_formatted_url( $_REQUEST['return_url'] ) ) ) );
345
					exit;
346
				}
347
			} else {
348
				throw new Exception( __( 'You do not have permissions to access this page!', 'woocommerce' ) );
349
			}
350
		} catch ( Exception $e ) {
351
			$this->maybe_delete_key( $consumer_data );
0 ignored issues
show
Security Bug introduced by
It seems like $consumer_data defined by $this->create_keys($_REQ...'], $_REQUEST['scope']) on line 340 can also be of type false; however, WC_Auth::maybe_delete_key() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
352
353
			wp_die( sprintf( __( 'Error: %s', 'woocommerce' ), $e->getMessage() ), __( 'Access Denied', 'woocommerce' ), array( 'response' => 401 ) );
354
		}
355
	}
356
357
	/**
358
	 * Maybe delete key.
359
	 *
360
	 * @since 2.4.0
361
	 *
362
	 * @param array $key
363
	 */
364
	private function maybe_delete_key( $key ) {
365
		if ( isset( $key['key_id'] ) ) {
366
			WC_Auth::delete_api_key( $key['key_id'] );
367
		}
368
	}
369
370
	/**
371
	 * Generate a new pair of API Key/Secret
372
	 *
373
	 * @param array $args {
374
	 *     An array of arguments.
375
	 *
376
	 *     @type string    $description        Application description
377
	 *     @type int       $user_id            The user ID that the pair will be assigned to
378
	 *     @type string    $scope           Scope of the API Keys ('read', 'write' or 'read_write')
379
	 * }
380
	 *
381
	 * @return array|bool Return an array with the API Keys data. False in case of error.
382
	 */
383
	public static function create_api_key( $args = array() ) {
384
		global $wpdb;
385
386
		$defaults = array(
387
			'description' => '',
388
			'user_id' => 0,
389
			'scope' => 'read'
390
		);
391
392
		$args = wp_parse_args( $args, $defaults );
393
394
		// Sanitize description
395
		$description = wc_clean( $args['description'] );
396
397
		// If the user is not passed, let's use the current user
398
		$user_id = absint( $args['user_id'] );
399
		if ( ! $user_id ) {
400
			$user_id = get_current_user_id();
401
		}
402
403
		$user = get_userdata( $user_id );
404
405
		if ( ! $user ) {
406
			return false;
407
		}
408
409
		// Sanitize scope
410
		$scope = sanitize_text_field( $args['scope'] );
411
		$permissions     = ( in_array( $scope, array( 'read', 'write', 'read_write' ) ) ) ? $scope : 'read';
412
413
		// Created API keys.
414
		$consumer_key    = 'ck_' . wc_rand_hash();
415
		$consumer_secret = 'cs_' . wc_rand_hash();
416
417
		$wpdb->insert(
418
			$wpdb->prefix . 'woocommerce_api_keys',
419
			array(
420
				'user_id'         => $user->ID,
421
				'description'     => $description,
422
				'permissions'     => $permissions,
423
				'consumer_key'    => wc_api_hash( $consumer_key ),
424
				'consumer_secret' => $consumer_secret,
425
				'truncated_key'   => substr( $consumer_key, -7 )
426
			),
427
			array(
428
				'%d',
429
				'%s',
430
				'%s',
431
				'%s',
432
				'%s',
433
				'%s'
434
			)
435
		);
436
437
		return array(
438
			'key_id'          => $wpdb->insert_id,
439
			'user_id'         => $user_id,
440
			'consumer_key'    => $consumer_key,
441
			'consumer_secret' => $consumer_secret,
442
			'key_permissions' => $permissions
443
		);
444
	}
445
446
	/**
447
	 * Update a single API Key pair data
448
	 *
449
	 * @param int $key_id Key ID
450
	 * @param array $args {
451
	 *      List of arguments to be updated. Must include at least one of these:
452
	 *      @type int       $user_id            New User ID
453
	 *      @type string    $description        New Application description
454
	 *      @type string    $scope              New Scope of the API Keys ('read', 'write' or 'read_write')
455
	 *      @type string    $last_access        New last access date in MySQL format
456
	 *      @type array     $last_access        New nonces list
457
	 * }
458
	 *
459
	 * @return bool
460
	 */
461
	public static function update_api_key( $key_id, $args = array() ) {
462
		global $wpdb;
463
464
		$api_key_data = self::get_api_key_data( $key_id );
465
		if ( ! $api_key_data ) {
466
			return false;
467
		}
468
469
		$update = array();
470
		$update_format = array();
471
472
		if ( isset( $args['user_id'] ) ) {
473
			$user = get_userdata( $args['user_id'] );
474
			if ( ! $user ) {
475
				return false;
476
			}
477
478
			$update['user_id'] = $args['user_id'];
479
			$update_format[] = '%d';
480
		}
481
482
		if ( isset( $args['scope'] ) ) {
483
			$scope = sanitize_text_field( $args['scope'] );
484
			$permissions     = ( in_array( $scope, array( 'read', 'write', 'read_write' ) ) ) ? $scope : 'read';
485
486
			$update['permissions'] = $permissions;
487
			$update_format[] = '%s';
488
		}
489
490
		if ( isset( $args['description'] ) ) {
491
			$update['description'] = sanitize_text_field( $args['description'] );
492
			$update_format[] = '%s';
493
		}
494
495
		if ( isset( $args['last_access'] ) ) {
496
			$update['last_access'] = $args['last_access'];
497
			$update_format[] = '%s';
498
		}
499
500
		if ( isset( $args['nonces'] ) ) {
501
			$update['nonces'] = maybe_serialize( $args['nonces'] );
502
			$update_format[] = '%s';
503
		}
504
505
		if ( empty( $update ) ) {
506
			return false;
507
		}
508
509
		 $wpdb->update(
510
			$wpdb->prefix . 'woocommerce_api_keys',
511
			$update,
512
			array( 'key_id' => $key_id ),
513
			$update_format,
514
			array( '%d' )
515
		);
516
517
		self::clear_api_key_cache( $key_id );
518
519
		return true;
520
	}
521
522
	public static function update_last_access( $key_id ) {
523
		return self::update_api_key( $key_id, array( 'last_access' => current_time( 'mysql' ) ) );
524
	}
525
526
	/**
527
	 * Get a list of API Key data rows
528
	 *
529
	 * @param array $args
530
	 *
531
	 * @return array
532
	 */
533
	public static function get_api_keys( $args = array() ) {
534
		global $wpdb;
535
536
		$defaults = array(
537
			'per_page' => 10,
538
			'page' => 1,
539
			's' => '',
540
			'count' => false
541
		);
542
543
		$args = wp_parse_args( $args, $defaults );
544
545
		$where = array( "1 = 1" );
546
547
		$args['s'] = wc_clean( $args['s'] );
548
		if ( $args['s'] ) {
549
			$where[] = $wpdb->prepare( "description LIKE %s", '%' . $wpdb->esc_like( $args['s'] ) . '%' );
550
		}
551
552
		$where = "WHERE " . implode( " AND ", $where );
553
554
		if ( $args['per_page'] < 0 ) {
555
			$limit = '';
556
		}
557
		else {
558
			if ( 1 < $args['page'] ) {
559
				$offset = $args['per_page'] * ( $args['page'] - 1 );
560
			} else {
561
				$offset = 0;
562
			}
563
564
			$limit = $wpdb->prepare( "LIMIT %d OFFSET %d", $args['per_page'], $offset );
565
		}
566
567
568
		if ( $args['count'] ) {
569
			$sql = "SELECT COUNT(key_id) FROM {$wpdb->prefix}woocommerce_api_keys $where";
570
571
			$cache_key = md5( $sql );
572
			$cached = wp_cache_get( 'wc_api_key_counts' );
573
			if ( false === $cached || ! is_array( $cached ) ) {
574
				$cached = array();
575
			}
576
577
			$count = isset( $cached[ $cache_key ] ) ? $cached[ $cache_key ] : false;
578
			if ( false === $count ) {
579
				$count = $wpdb->get_var( $sql );
580
				$cached[ $cache_key ] = $count;
581
				wp_cache_set( 'wc_api_key_counts', $cached );
582
			}
583
584
			return $count;
585
586
		}
587
		else {
588
			$sql = "SELECT * FROM {$wpdb->prefix}woocommerce_api_keys $where ORDER BY key_id DESC $limit";
589
590
			$cache_key = md5( $sql );
591
			$cached = wp_cache_get( 'wc_get_api_keys' );
592
			if ( false === $cached || ! is_array( $cached ) ) {
593
				$cached = array();
594
			}
595
596
			$results = isset( $cached[ $cache_key ] ) ? $cached[ $cache_key ] : false;
597
			if ( false === $results ) {
598
				$results = $wpdb->get_results( $sql );
599
600
				if ( ! $results ) {
601
					$results = array();
602
				}
603
604
				$cached[ $cache_key ] = $results;
605
				wp_cache_set( 'wc_get_api_keys', $cached );
606
			}
607
608
			$list = array();
609
			foreach ( $results as $row ) {
610
				wp_cache_add( $row->key_id, $row, 'wc_api_keys' );
611
				wp_cache_add( $row->consumer_key, $row, 'wc_api_keys_consumer' );
612
				$list[] = self::_format_api_key_data( $row );
613
			}
614
615
			return $list;
616
		}
617
618
	}
619
620
	public static function get_api_keys_count( $args = array() ) {
621
		$args['count'] = true;
622
		return self::get_api_keys( $args );
623
	}
624
625
	/**
626
	 * Get a single API Key pair data
627
	 *
628
	 * @param int $key_id Key ID
629
	 *
630
	 * @return bool|object
631
	 */
632
	public static function get_api_key_data( $key_id ) {
633
		global $wpdb;
634
635
		$data = wp_cache_get( $key_id, 'wc_api_keys' );
636
		if ( false === $data ) {
637
			$table = $wpdb->prefix . 'woocommerce_api_keys';
638
639
			$data = $wpdb->get_row(
640
				$wpdb->prepare(
641
					"SELECT * FROM $table 
642
				WHERE key_id = %d
643
				LIMIT 1",
644
					$key_id
645
				)
646
			);
647
648
			wp_cache_add( $key_id, $data, 'wc_api_keys' );
649
		}
650
651
		return self::_format_api_key_data( $data );
652
	}
653
654
	/**
655
	 * Return the Api Key data for the given consumer_key.
656
	 *
657
	 * @param string $consumer_key
658
	 * @return array
659
	 */
660
	public static function get_api_key_data_by_consumer_key( $consumer_key ) {
661
		global $wpdb;
662
663
		$consumer_key = wc_api_hash( sanitize_text_field( $consumer_key ) );
664
665
		$data = wp_cache_get( $consumer_key, 'wc_api_keys_consumer' );
666
		if ( false === $data ) {
667
			$data         = $wpdb->get_row(
668
				$wpdb->prepare( "
669
				SELECT *
670
				FROM {$wpdb->prefix}woocommerce_api_keys
671
				WHERE consumer_key = %s ",
672
					$consumer_key
673
				)
674
			);
675
676
			wp_cache_add( $consumer_key, $data, 'wc_api_keys_consumer' );
677
		}
678
679
680
		return self::_format_api_key_data( $data );
681
	}
682
683
	/**
684
	 * Format an API Key Data row object
685
	 *
686
	 * @param object $data
687
	 * @return bool|object
688
	 */
689
	private static function _format_api_key_data( $data ) {
690
		if ( ! $data ) {
691
			return false;
692
		}
693
694
		$data->nonces = maybe_unserialize( $data->nonces );
695
		return $data;
696
	}
697
698
	/**
699
	 * Delete an API Key pair data
700
	 *
701
	 * @param int $key_id Key ID
702
	 *
703
	 * @return bool
704
	 */
705
	public static function delete_api_key( $key_id ) {
706
		global $wpdb;
707
	    $result = $wpdb->delete(
708
			$wpdb->prefix . 'woocommerce_api_keys',
709
			array( 'key_id' => $key_id ),
710
			array( '%d' )
711
		);
712
713
		self::clear_api_key_cache( $key_id );
714
715
		return $result;
716
	}
717
718
	public static function clear_api_key_cache( $key_id ) {
719
		$data = self::get_api_key_data( $key_id );
720
		if ( ! $data ) {
721
			return;
722
		}
723
724
		wp_cache_delete( $key_id, 'wc_api_keys' );
725
		wp_cache_delete( $data->consumer_key, 'wc_api_keys_consumer' );
726
		wp_cache_delete( 'wc_get_api_keys' );
727
		wp_cache_delete( 'wc_api_key_counts' );
728
	}
729
}
730
731
endif;
732
733
return new WC_Auth();
734