Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
34 / 34
Social\Providers\AbstractProvider\AbstractProvider
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
13 / 13
17
100.00% covered (success)
100.00%
34 / 34
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 setProviderName
100.00% covered (success)
100.00%
1 / 1
1
n/a
0 / 0
 updateAuthorisationParams
100.00% covered (success)
100.00%
1 / 1
1
n/a
0 / 0
 updateAccessParams
100.00% covered (success)
100.00%
1 / 1
1
n/a
0 / 0
 handleAccessTokenResponse
100.00% covered (success)
100.00%
1 / 1
1
n/a
0 / 0
 processUserProfile
100.00% covered (success)
100.00%
1 / 1
1
n/a
0 / 0
 getRedirectRoute
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 sendClientRequest
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
16 / 16
 checkReturnedQuery
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
3 / 3
 getQuery
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
5 / 5
 setCsrf
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 getCsrf
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 checkCsrf
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
<?php
/**
 * Class AbstractProvider 
 *
 * @package     Social\Providers\AbstractProvider
 * @author      Nigel Hurnell
 * @version     v 1.0.0
 * @license     BSD
 * @copyright   Copyright (c) 2017, Nigel Hurnell
 */
namespace Social\Providers\AbstractProvider;
use Social\Providers\ProviderInterface\ProviderInterface;
use Social\Service\SocialManager;
use Zend\Validator\Csrf;
use Zend\Http\Client;
/**
 * Class AbstractProvider handle generic functionality of all 
 * (extending) social media providers
 *
 * @package     Social\Providers\AbstractProvider
 * @author      Nigel Hurnell
 * @version     v 1.0.0
 * @license     BSD
 * @copyright   Copyright (c) 2017, Nigel Hurnell
 * 
 */
abstract class AbstractProvider implements ProviderInterface
{
    /**
     * The manager that handles basic logic for the Social module
     * 
     * @var SocialManager 
     */
    protected $socialManager;
    /**
     * The hashed string to add extra level of security 
     * Stands for cross site request forgery 
     * @var string hash 
     */
    protected $csrf;
    /**
     * The name of the social provider
     * Set in actual named provider
     * 
     * @var string
     */
    protected $providerName;
    /**
     * The basic parameters to append to the initial call to the provider
     * 
     * @var array 
     */
    protected $authorisationParams = [
        'client_id' => '',
        'redirect_uri' => '',
        'response_type' => 'code',
        'scope' => '',
        'state' => '',
    ];
    /**
     * Basic former for access params array
     * @var array 
     */
    protected $accessParams = [
        'client_id' => '',
        'client_secret' => '',
        'code' => '',
        'redirect_uri' => '',
    ];
    /**
     * The URI that the provider returns to
     * 
     * @var string
     */
    protected $callback;
    /**
     * social login or registration
     * 
     * @var string 
     */
    protected $action;
    /**
     * Constructor Instantiate (extending) class and pass Social manager and
     * set the extending social provider name.
     * 
     * @param SocialManager $socialManager
     */
    public function __construct(SocialManager $socialManager)
    {
        $this->socialManager = $socialManager;
        $this->setProviderName();
    }
    /**
     * Set provider name
     * Overridden by 'real' providers that give actual name of the provider
     */
    abstract protected function setProviderName();
    /**
     * Update authorisation parameters
     * Different (but simular) in each provider
     */
    abstract protected function updateAuthorisationParams();
    /**
     * Update Access Parameters
     * Different (but simular) in each provider
     * 
     * @param array $queryParams 
     */
    abstract protected function updateAccessParams($queryParams);
    /**
     * Handle Access Token Response
     * Different (but simular) in each provider
     * 
     * @param Client $client
     * @param Response $response
     */
    abstract protected function handleAccessTokenResponse(Client $client, $response);
    /**
     * Process user profile
     * Different in each provider
     * 
     * @param Response $response
     */
    abstract protected function processUserProfile($response);
    /**
     * Get the full redirect URL (including query string)
     * 
     * @param string $callback
     * @return string full redirect URL
     */
    public function getRedirectRoute($callback)
    {
        $this->callback = $callback;
        $query = $this->getQuery();
        return $this->baseAuthorisationUrl . '?' . $query;
    }
    /**
     * Send Client Request
     * Form client request URL with query params and send via Zend Client
     * 
     * @param string $callback the callback URL
     * @param array $queryParams parameters to append to end of callback URL
     * @return array that contains the user profile
     */
    public function sendClientRequest($callback, $queryParams)
    {
        $this->checkReturnedQuery($queryParams);
        $this->accessParams['code'] = $queryParams['code'];
        $this->accessParams['client_id'] = $this->socialManager->getModuleOptions()->getClientId($this->providerName);
        $this->accessParams['client_secret'] = $this->socialManager->getModuleOptions()->getSecret($this->providerName);
        $this->accessParams['redirect_uri'] = $callback;
        $additionalHeaders = $this->updateAccessParams($queryParams);
        $client = $this->socialManager->getClient();
        $client->setUri($this->requestAccessTokenUrl);
        $headers = ['Content-Type' => 'application/x-www-form-urlencoded',];
        $client->setHeaders(array_merge($headers, $additionalHeaders));
        $client->setMethod('POST');
        $client->setParameterPost($this->accessParams);
        $response = $client->send();
        if (200 == $response->getStatusCode()) {
            return $this->handleAccessTokenResponse($client, $response);
        }
        throw new \Exception('AbstractProvider::sendClientRequest failed to return valid response.');
    }
    /**
     * Check the query string provided by the social provider
     * to ensure that it has the keys code and state and that the value of state 
     * corresponds to the CSRF value that was sent to the provider
     * 
     * @param array $params the params that need checking
     * @throws \Exception
     */
    protected function checkReturnedQuery($params)
    {
        if (!array_key_exists('code', $params) || !array_key_exists('state', $params) || !$this->checkCsrf($params['state'])) {
            throw new \Exception('The social provider returned invalid parameters.');
        }
    }
    /**
     * Get HTTP query string 
     * after setting up basic values for authorisationParams array
     * 
     * @return string the HTT{ query string
     */
    protected function getQuery()
    {
        $this->authorisationParams['state'] = $this->getCsrf();
        $this->authorisationParams['redirect_uri'] = $this->callback;
        $this->authorisationParams['client_id'] = $this->socialManager->getModuleOptions()->getClientId($this->providerName);
        /* update the parameters (each provider has own method) */
        $this->updateAuthorisationParams();
        return http_build_query($this->authorisationParams);
    }
    /**
     * Set (new) csrf hashed string
     * 
     * @return string csrf hash
     */
    protected function setCsrf()
    {
        $this->csrf = new Csrf();
        return $this->csrf;
    }
    /**
     * Get hashed security string 
     * 
     * @param boolean $regenerate whether to create a new one if one already exists
     * @return string hashed csrf
     */
    protected function getCsrf($regenerate = false)
    {
        return $this->setCsrf()->getHash($regenerate);
    }
    /**
     * Check whether the hash value matches the original created in getCsrf()
     * 
     * @param string $value the hashed value to check
     * @return boolean whether passed value is valid
     */
    protected function checkCsrf($value)
    {
        return $this->setCsrf()->isValid($value);
    }
}