bkdotcom/PHPDebugConsole

View on GitHub
src/Debug/Collector/OAuth.php

Summary

Maintainability
A
35 mins
Test Coverage
A
98%
<?php

/**
 * This file is part of PHPDebugConsole
 *
 * @package   PHPDebugConsole
 * @author    Brad Kent <bkfake-github@yahoo.com>
 * @license   http://opensource.org/licenses/MIT MIT
 * @copyright 2014-2024 Brad Kent
 * @since     3.0b3
 */

namespace bdk\Debug\Collector;

use bdk\Debug;
use bdk\Debug\Plugin\Redaction;
use bdk\HttpMessage\Utility\Uri as UriUtils;
use Oauth as OAuthBase;
use OAuthException;

/**
 * OAuth client with debugging
 */
class OAuth extends OAuthBase
{
    /** @var Debug */
    protected $debugger;

    /** @var string */
    protected $icon = ':authorize:';

    /** @var float */
    private $elapsed;

    /** @var OAuthException|null */
    private $exception;

    /**
     * Constructor
     *
     * @param string $consumerKey     The consumer key provided by the service provider
     * @param string $consumerSecret  The consumer secret provided by the service provide
     * @param string $signatureMethod (OAUTH_SIG_METHOD_HMACSHA1) defines which signature method to use
     * @param int    $authType        (OAUTH_AUTH_TYPE_AUTHORIZATION) defines how to pass the OAuth parameters to a consumer
     * @param Debug  $debug           (optional) $debug instance
     *
     * @SuppressWarnings(PHPMD.StaticAccess)
     */
    public function __construct($consumerKey, $consumerSecret, $signatureMethod = OAUTH_SIG_METHOD_HMACSHA1, $authType = OAUTH_AUTH_TYPE_AUTHORIZATION, $debug = null)
    {
        parent::__construct($consumerKey, $consumerSecret, $signatureMethod, $authType);
        if ($debug === null) {
            $debug = Debug::getChannel('OAuth', array('channelIcon' => $this->icon));
        } elseif ($debug === $debug->rootInstance) {
            $debug = $debug->getChannel('OAuth', array('channelIcon' => $this->icon));
        }
        $this->enableDebug();
        $this->debugger = $debug;
    }

    /**
     * {@inheritDoc}
     */
    public function fetch($protectedResourceUrl, $extraParameters = array(), $httpMethod = OAUTH_HTTP_METHOD_GET, $httpHeaders = array())
    {
        $return = $this->profileCall(__FUNCTION__, \func_get_args());
        if ($this->debug) {
            $this->debugger->groupCollapsed('OAuth::' . __FUNCTION__, $this->getHttpMethod(), $protectedResourceUrl);
            $this->logRequest($protectedResourceUrl);
            $this->debugger->groupEnd();
        }
        if ($this->exception) {
            throw $this->exception;
        }
        return $return;
    }

    /**
     * {@inheritDoc}
     */
    public function getAccessToken($accessTokenUrl, $authSessionHandle = null, $verifierToken = null, $httpMethod = OAUTH_HTTP_METHOD_GET)
    {
        $return = $this->profileCall(__FUNCTION__, \func_get_args());
        if ($this->debug) {
            $this->debugger->groupCollapsed('OAuth::' . __FUNCTION__, $this->getHttpMethod(), $accessTokenUrl);
            $this->logRequest($accessTokenUrl);
            $this->debugger->groupEnd();
        }
        if ($this->exception) {
            throw $this->exception;
        }
        return $return;
    }

    /**
     * {@inheritDoc}
     */
    public function getRequestToken($requestTokenUrl, $callbackUrl = null, $httpMethod = OAUTH_HTTP_METHOD_GET)
    {
        $return = $this->profileCall(__FUNCTION__, \func_get_args());
        if ($this->debug) {
            $this->debugger->groupCollapsed('OAuth::' . __FUNCTION__, $this->getHttpMethod(), $requestTokenUrl);
            $this->debugger->log('callback url', $callbackUrl);
            $this->logRequest($requestTokenUrl);
            $this->debugger->groupEnd();
        }
        if ($this->exception) {
            throw $this->exception;
        }
        return $return;
    }

    /**
     * Get the http method used for last request
     *
     * @return string
     */
    private function getHttpMethod()
    {
        \preg_match('/^(\w+)/', $this->getDebugInfo()['sbs'], $matches);
        return $matches
            ? $matches[1]
            : '';
    }

    /**
     * Get debugInfo with default values
     *
     * @return array
     */
    private function getDebugInfo()
    {
        return \array_merge(array(
            'body_recv' => null,
            'body_sent' => null,
            'headers_recv' => '',
            'headers_sent' => '',
            'sbs' => '',
        ), $this->debugInfo ?: array());
    }

    /**
     * Get query params from request url
     *
     * @return array
     */
    private function getQueryParams()
    {
        $parts = \array_merge(array(
            'query' => '',
        ), UriUtils::parseUrl($this->getLastResponseInfo()['url']));
        $queryParams = array();
        \parse_str($parts['query'], $queryParams);
        return $queryParams;
    }

    /**
     * debugInfo + lastResponseInfo.
     * any values avail in headers or logged separately are omitted
     *
     * @param string $url requested url
     *
     * @return array
     */
    private function additionalInfo($url)
    {
        $debugInfo = \array_diff_key($this->getDebugInfo(), \array_flip([
            'headers_sent', 'body_sent', 'headers_recv', 'body_recv',
            // "sbs" may be only key remaining
        ]));
        $lastResponseInfo = \array_merge(array(
            'download_content_length' => 0,
            'size_download' => 0,
            'url' => $url,
        ), $this->getLastResponseInfo() ?: array());
        $lastResponseInfo = \array_diff_key($lastResponseInfo, \array_filter(array(
            'content_type' => true,
            'download_content_length' => true,
            'http_code' => true,
            'size_download' => $lastResponseInfo['size_download'] === $lastResponseInfo['download_content_length'], // content length plus any overhead
            'size_upload' => isset($this->debugInfo['body_sent']) === false,
            'url' => $lastResponseInfo['url'] === $url,
        )));
        return $lastResponseInfo + $debugInfo;
    }

    /**
     * Log oauth request details
     *
     * @param string $url requested url
     *
     * @return void
     */
    private function logRequest($url)
    {
        $this->debugger->time($this->elapsed);
        $debugInfo = $this->getDebugInfo();
        // values available in the headers or elsewhere
        $this->debugger->log('OAuth Parameters', $this->oauthParams(), $this->debugger->meta('cfg', 'abstracter.stringMinLen.encoded', -1));
        $this->debugger->log('additional info', $this->additionalInfo($url));
        $this->debugger->log('request headers', $this->debugger->redactHeaders($debugInfo['headers_sent']), $this->debugger->meta('icon', ':send:'));
        if (isset($debugInfo['body_sent'])) {
            $this->debugger->log('request body', $debugInfo['body_sent'], $this->debugger->meta(array(
                'icon' => ':send:',
                'redact' => true,
            )));
        }
        $this->debugger->log('response headers', $debugInfo['headers_recv'], $this->debugger->meta('icon', ':receive:'));
        $this->debugger->log('response body', $debugInfo['body_recv'], $this->debugger->meta('icon', ':receive:'));
        if ($this->exception) {
            $this->debugger->warn(\get_class($this->exception), $this->exception->getMessage());
        }
    }

    /**
     * Get the request's OAuth parameters
     *
     * @return array
     */
    private function oauthParams()
    {
        $oauthParamKeys = [
            'oauth_consumer_key',
            'oauth_nonce',
            'oauth_signature',
            'oauth_signature_method',
            'oauth_timestamp',
            'oauth_token',
            'oauth_version',
        ];
        $oauthParams = array();
        $debugInfo = $this->getDebugInfo();
        if (\preg_match('/^Authorization:\s+([^\r]+)/m', $debugInfo['headers_sent'], $matches)) {
            // if OAUTH_AUTH_TYPE_AUTHORIZATION, we can get params from header
            $authHeader = $matches[1];
            \preg_match_all('/(\w+)="([^"]+)"/', $authHeader, $matches, PREG_PATTERN_ORDER);
            $oauthParams = \array_map('urldecode', \array_combine($matches[1], $matches[2]));
        } elseif ($debugInfo['sbs']) {
            // get params from Signature Base String
            $sbsParsed = array();
            \parse_str(\urldecode($debugInfo['sbs']), $sbsParsed);
            $oauthParams = \array_intersect_key($sbsParsed + $this->getQueryParams(), \array_flip($oauthParamKeys));
        }
        if (isset($oauthParams['oauth_signature'])) {
            $oauthParams['oauth_signature'] = Redaction::REPLACEMENT;
        }
        \ksort($oauthParams);
        return $oauthParams;
    }

    /**
     * Call oauth method
     * capture elapsed time and any thrown exception
     *
     * @param string $method Method being called
     * @param array  $args   Arguments
     *
     * @return array|false
     */
    private function profileCall($method, $args)
    {
        $this->exception = null;
        $return = false;
        $this->debugger->time();
        try {
            $return = \call_user_func_array(['OAuth', $method], $args);
        } catch (OAuthException $e) {
            $this->exception = $e;
        }
        $this->elapsed = $this->debugger->timeEnd($this->debugger->meta('silent'));
        return $return;
    }
}