File "class-wc-api-authentication.php"

Full path: /home/kosmetik/public_html/wp-content/plugins/woocommerce/includes/legacy/api/v3/class-wc-api-authentication.php
File size: 8.59 B
MIME-type: text/x-php
Charset: utf-8

Download   Open   Edit   Advanced Editor   Back

<?php

if (!defined('ABSPATH')) {
    exit;
}
class WC_API_Authentication
{
    public function __construct()
    {
        add_filter('woocommerce_api_check_authentication', array($this, 'authenticate'), 0);
    }
    public function authenticate($user)
    {
        if ('/' === WC()->api->server->path) {
            return new WP_User(0);
        }
        try {
            if (is_ssl()) {
                $keys = $this->perform_ssl_authentication();
            } else {
                $keys = $this->perform_oauth_authentication();
            }
            $this->check_api_key_permissions($keys['permissions']);
            $user = $this->get_user_by_id($keys['user_id']);
            $this->update_api_key_last_access($keys['key_id']);
        } catch (Exception $e) {
            $user = new WP_Error('woocommerce_api_authentication_error', $e->getMessage(), array('status' => $e->getCode()));
        }
        return $user;
    }
    private function perform_ssl_authentication()
    {
        $params = WC()->api->server->params['GET'];
        if (!empty($params['consumer_key']) && !empty($params['consumer_secret'])) {
            $keys = $this->get_keys_by_consumer_key($params['consumer_key']);
            if (!$this->is_consumer_secret_valid($keys['consumer_secret'], $params['consumer_secret'])) {
                throw new Exception(__('Consumer secret is invalid.', 'woocommerce'), 401);
            }
            return $keys;
        }
        if (empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW'])) {
            $this->exit_with_unauthorized_headers();
        }
        $keys = $this->get_keys_by_consumer_key($_SERVER['PHP_AUTH_USER']);
        if (!$this->is_consumer_secret_valid($keys['consumer_secret'], $_SERVER['PHP_AUTH_PW'])) {
            $this->exit_with_unauthorized_headers();
        }
        return $keys;
    }
    private function exit_with_unauthorized_headers()
    {
        $auth_message = __('WooCommerce API. Use a consumer key in the username field and a consumer secret in the password field.', 'woocommerce');
        header('WWW-Authenticate: Basic realm="' . $auth_message . '"');
        header('HTTP/1.0 401 Unauthorized');
        throw new Exception(__('Consumer Secret is invalid.', 'woocommerce'), 401);
    }
    private function perform_oauth_authentication()
    {
        $params = WC()->api->server->params['GET'];
        $param_names = array('oauth_consumer_key', 'oauth_timestamp', 'oauth_nonce', 'oauth_signature', 'oauth_signature_method');
        foreach ($param_names as $param_name) {
            if (empty($params[$param_name])) {
                throw new Exception(sprintf(__('%s parameter is missing', 'woocommerce'), $param_name), 404);
            }
        }
        $keys = $this->get_keys_by_consumer_key($params['oauth_consumer_key']);
        $this->check_oauth_signature($keys, $params);
        $this->check_oauth_timestamp_and_nonce($keys, $params['oauth_timestamp'], $params['oauth_nonce']);
        return $keys;
    }
    private function get_keys_by_consumer_key($consumer_key)
    {
        global $wpdb;
        $consumer_key = wc_api_hash(sanitize_text_field($consumer_key));
        $keys = $wpdb->get_row($wpdb->prepare("\n\t\t\tSELECT key_id, user_id, permissions, consumer_key, consumer_secret, nonces\n\t\t\tFROM {$wpdb->prefix}woocommerce_api_keys\n\t\t\tWHERE consumer_key = '%s'\n\t\t", $consumer_key), ARRAY_A);
        if (empty($keys)) {
            throw new Exception(__('Consumer key is invalid.', 'woocommerce'), 401);
        }
        return $keys;
    }
    private function get_user_by_id($user_id)
    {
        $user = get_user_by('id', $user_id);
        if (!$user) {
            throw new Exception(__('API user is invalid', 'woocommerce'), 401);
        }
        return $user;
    }
    private function is_consumer_secret_valid($keys_consumer_secret, $consumer_secret)
    {
        return hash_equals($keys_consumer_secret, $consumer_secret);
    }
    private function check_oauth_signature($keys, $params)
    {
        $http_method = strtoupper(WC()->api->server->method);
        $server_path = WC()->api->server->path;
        if (isset($_SERVER['REDIRECT_URL']) && '/' === substr($_SERVER['REDIRECT_URL'], -1)) {
            $server_path .= '/';
        }
        $base_request_uri = rawurlencode(untrailingslashit(get_woocommerce_api_url('')) . $server_path);
        $consumer_signature = rawurldecode(str_replace(' ', '+', $params['oauth_signature']));
        unset($params['oauth_signature']);
        if (!uksort($params, 'strcmp')) {
            throw new Exception(__('Invalid signature - failed to sort parameters.', 'woocommerce'), 401);
        }
        $params = $this->normalize_parameters($params);
        $query_parameters = array();
        foreach ($params as $param_key => $param_value) {
            if (is_array($param_value)) {
                foreach ($param_value as $param_key_inner => $param_value_inner) {
                    $query_parameters[] = $param_key . '%255B' . $param_key_inner . '%255D%3D' . $param_value_inner;
                }
            } else {
                $query_parameters[] = $param_key . '%3D' . $param_value;
            }
        }
        $query_string = implode('%26', $query_parameters);
        $string_to_sign = $http_method . '&' . $base_request_uri . '&' . $query_string;
        if ('HMAC-SHA1' !== $params['oauth_signature_method'] && 'HMAC-SHA256' !== $params['oauth_signature_method']) {
            throw new Exception(__('Invalid signature - signature method is invalid.', 'woocommerce'), 401);
        }
        $hash_algorithm = strtolower(str_replace('HMAC-', '', $params['oauth_signature_method']));
        $secret = $keys['consumer_secret'] . '&';
        $signature = base64_encode(hash_hmac($hash_algorithm, $string_to_sign, $secret, true));
        if (!hash_equals($signature, $consumer_signature)) {
            throw new Exception(__('Invalid signature - provided signature does not match.', 'woocommerce'), 401);
        }
    }
    private function normalize_parameters($parameters)
    {
        $keys = WC_API_Authentication::urlencode_rfc3986(array_keys($parameters));
        $values = WC_API_Authentication::urlencode_rfc3986(array_values($parameters));
        $parameters = array_combine($keys, $values);
        return $parameters;
    }
    public static function urlencode_rfc3986($value)
    {
        if (is_array($value)) {
            return array_map(array('WC_API_Authentication', 'urlencode_rfc3986'), $value);
        } else {
            return str_replace('%', '%25', rawurlencode(rawurldecode($value)));
        }
    }
    private function check_oauth_timestamp_and_nonce($keys, $timestamp, $nonce)
    {
        global $wpdb;
        $valid_window = 15 * 60;
        if ($timestamp < time() - $valid_window || $timestamp > time() + $valid_window) {
            throw new Exception(__('Invalid timestamp.', 'woocommerce'), 401);
        }
        $used_nonces = maybe_unserialize($keys['nonces']);
        if (empty($used_nonces)) {
            $used_nonces = array();
        }
        if (in_array($nonce, $used_nonces)) {
            throw new Exception(__('Invalid nonce - nonce has already been used.', 'woocommerce'), 401);
        }
        $used_nonces[$timestamp] = $nonce;
        foreach ($used_nonces as $nonce_timestamp => $nonce) {
            if ($nonce_timestamp < time() - $valid_window) {
                unset($used_nonces[$nonce_timestamp]);
            }
        }
        $used_nonces = maybe_serialize($used_nonces);
        $wpdb->update($wpdb->prefix . 'woocommerce_api_keys', array('nonces' => $used_nonces), array('key_id' => $keys['key_id']), array('%s'), array('%d'));
    }
    public function check_api_key_permissions($key_permissions)
    {
        switch (WC()->api->server->method) {
            case 'HEAD':
            case 'GET':
                if ('read' !== $key_permissions && 'read_write' !== $key_permissions) {
                    throw new Exception(__('The API key provided does not have read permissions.', 'woocommerce'), 401);
                }
                break;
            case 'POST':
            case 'PUT':
            case 'PATCH':
            case 'DELETE':
                if ('write' !== $key_permissions && 'read_write' !== $key_permissions) {
                    throw new Exception(__('The API key provided does not have write permissions.', 'woocommerce'), 401);
                }
                break;
        }
    }
    private function update_api_key_last_access($key_id)
    {
        global $wpdb;
        $wpdb->update($wpdb->prefix . 'woocommerce_api_keys', array('last_access' => current_time('mysql')), array('key_id' => $key_id), array('%s'), array('%d'));
    }
}