iranianpep/botonomous

View on GitHub
src/Botonomous/OAuth.php

Summary

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

namespace Botonomous;

use Botonomous\client\ApiClient;
use Botonomous\utility\RequestUtility;
use Botonomous\utility\SecurityUtility;
use Botonomous\utility\SessionUtility;

/**
 * Class OAuth.
 */
class OAuth
{
    const AUTHORIZATION_URL = 'https://slack.com/oauth/authorize';
    const SESSION_STATE_KEY = 'state';

    private $clientId;
    private $clientSecret;
    private $scopes;
    private $redirectUri;
    private $state;
    private $teamId;
    private $apiClient;
    private $sessionUtility;
    private $requestUtility;

    /**
     * @var string configuration_url will be the URL that you can point your user to if they'd like to edit
     *             or remove this integration in Slack
     */
    private $configurationUrl;

    /**
     * @var string The team_name field will be the name of the team that installed your app
     */
    private $teamName;
    private $accessToken;

    /**
     * @var string the channel will be the channel name that they have chosen to post to
     */
    private $channel;

    /**
     * @var string you will need to use bot_user_id and bot_access_token whenever you are acting on behalf of
     *             that bot user for that team context.
     *             Use the top-level access_token value for other integration points.
     */
    private $botUserId;
    private $botAccessToken;

    private $config;

    /**
     * OAuth constructor.
     *
     * @param       $clientId
     * @param       $clientSecret
     * @param array $scopes
     */
    public function __construct($clientId = '', $clientSecret = '', array $scopes = [])
    {
        $this->setClientId($clientId);
        $this->setClientSecret($clientSecret);
        $this->setScopes($scopes);
    }

    /**
     * @throws \Exception
     *
     * @return string
     */
    public function getClientId(): string
    {
        if (empty($this->clientId)) {
            $this->setClientId($this->getConfig()->get('clientId'));
        }

        return $this->clientId;
    }

    /**
     * @param string $clientId
     */
    public function setClientId(string $clientId)
    {
        $this->clientId = $clientId;
    }

    /**
     * @throws \Exception
     *
     * @return array
     */
    public function getScopes(): array
    {
        if (empty($this->scopes)) {
            $this->setScopes($this->getConfig()->get('scopes'));
        }

        return $this->scopes;
    }

    /**
     * @param array $scopes
     */
    public function setScopes(array $scopes)
    {
        $this->scopes = $scopes;
    }

    /**
     * @return string
     */
    public function getRedirectUri(): string
    {
        return $this->redirectUri;
    }

    /**
     * @param string $redirectUri
     */
    public function setRedirectUri(string $redirectUri)
    {
        $this->redirectUri = $redirectUri;
    }

    /**
     * @throws \Exception
     *
     * @return string
     */
    public function getState(): string
    {
        if (!isset($this->state)) {
            $this->setState((new SecurityUtility())->generateToken());
        }

        return $this->state;
    }

    /**
     * @param string $state
     */
    public function setState(string $state)
    {
        $this->getSessionUtility()->set(self::SESSION_STATE_KEY, $state);
        $this->state = $state;
    }

    /**
     * @param $state
     *
     * @return bool
     */
    public function verifyState($state): bool
    {
        if (empty($state)) {
            return false;
        }

        return $state === $this->getSessionUtility()->get(self::SESSION_STATE_KEY) ? true : false;
    }

    /**
     * @return string
     */
    public function getTeamId(): string
    {
        return $this->teamId;
    }

    /**
     * @param string $teamId
     */
    public function setTeamId(string $teamId)
    {
        $this->teamId = $teamId;
    }

    /**
     * @param string $height
     * @param string $weight
     * @param string $cssClass
     *
     * @throws \Exception
     *
     * @return string
     */
    public function generateAddButton($height = '40', $weight = '139', $cssClass = ''): string
    {
        $authorizationUrl = self::AUTHORIZATION_URL;
        $scope = implode(',', $this->getScopes());
        $clientId = $this->getClientId();

        $stateQueryString = '';
        if (!empty($this->getState())) {
            $state = $this->getState();
            $stateQueryString = "&state={$state}";
        }

        $href = "{$authorizationUrl}?scope={$scope}&client_id={$clientId}{$stateQueryString}";

        $html = "<a href='{$href}'>
<img alt='Add to Slack' class='{$cssClass}' height='{$height}' width='{$weight}'
src='https://platform.slack-edge.com/img/add_to_slack.png'
srcset='https://platform.slack-edge.com/img/add_to_slack.png 1x,
https://platform.slack-edge.com/img/add_to_slack@2x.png 2x' /></a>";

        return $html;
    }

    /**
     * @param      $code
     * @param bool $verifyState State is checked against the value in the session
     * @param null $state
     *
     * @throws \Exception
     *
     * @return mixed
     */
    public function getAccessToken($code, $verifyState = true, $state = null)
    {
        if (!isset($this->accessToken)) {
            if ($verifyState === true && $this->verifyState($state) !== true) {
                throw new BotonomousException("State: '{$state}' is not valid");
            }

            try {
                $this->handleRequestAccessTokenResponse($this->requestAccessToken($code));
            } catch (\Exception $e) {
                throw $e;
            }
        }

        return $this->accessToken;
    }

    /**
     * @param $response
     *
     * @throws \Exception
     */
    private function handleRequestAccessTokenResponse($response)
    {
        if ($response['ok'] !== true) {
            throw new BotonomousException($response['error']);
        }

        $this->setAccessToken($response['access_token']);
        $this->setTeamId($response['team_id']);
        $this->setBotUserId($response['bot']['bot_user_id']);
        $this->setBotAccessToken($response['bot']['bot_access_token']);

        $channel = '';
        if (isset($response['incoming_webhook']['channel'])) {
            $channel = $response['incoming_webhook']['channel'];
        }

        $this->setChannel($channel);
    }

    /**
     * @param $code
     *
     * @throws \Exception
     *
     * @return mixed
     */
    private function requestAccessToken($code)
    {
        if (empty($code)) {
            throw new BotonomousException('Code must be provided to get the access token');
        }

        try {
            return $this->getApiClient()->oauthAccess([
                'client_id'     => $this->getClientId(),
                'client_secret' => $this->getClientSecret(),
                'code'          => $code,
            ]);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * @param $accessToken
     */
    public function setAccessToken(string $accessToken)
    {
        $this->accessToken = $accessToken;
    }

    /**
     * @throws \Exception
     *
     * @return string
     */
    public function getClientSecret(): string
    {
        if (empty($this->clientSecret)) {
            $this->setClientSecret($this->getConfig()->get('clientSecret'));
        }

        return $this->clientSecret;
    }

    /**
     * @param string $clientSecret
     */
    public function setClientSecret(string $clientSecret)
    {
        $this->clientSecret = $clientSecret;
    }

    /**
     * @return string
     */
    public function getBotUserId(): string
    {
        return $this->botUserId;
    }

    /**
     * @param string $botUserId
     */
    public function setBotUserId(string $botUserId)
    {
        $this->botUserId = $botUserId;
    }

    /**
     * @return string
     */
    public function getBotAccessToken(): string
    {
        return $this->botAccessToken;
    }

    /**
     * @param string $botAccessToken
     */
    public function setBotAccessToken(string $botAccessToken)
    {
        $this->botAccessToken = $botAccessToken;
    }

    /**
     * @return string
     */
    public function getChannel(): string
    {
        return $this->channel;
    }

    /**
     * @param string $channel
     */
    public function setChannel(string $channel)
    {
        $this->channel = $channel;
    }

    /**
     * @return string
     */
    public function getTeamName(): string
    {
        return $this->teamName;
    }

    /**
     * @param string $teamName
     */
    public function setTeamName(string $teamName)
    {
        $this->teamName = $teamName;
    }

    /**
     * @return string
     */
    public function getConfigurationUrl(): string
    {
        return $this->configurationUrl;
    }

    /**
     * @param string $configurationUrl
     */
    public function setConfigurationUrl(string $configurationUrl)
    {
        $this->configurationUrl = $configurationUrl;
    }

    /**
     * @return ApiClient
     */
    public function getApiClient(): ApiClient
    {
        if (!isset($this->apiClient)) {
            $this->setApiClient(new ApiClient());
        }

        return $this->apiClient;
    }

    /**
     * @param ApiClient $apiClient
     */
    public function setApiClient(ApiClient $apiClient)
    {
        $this->apiClient = $apiClient;
    }

    /**
     * @param SessionUtility $sessionUtility
     */
    public function setSessionUtility(SessionUtility $sessionUtility)
    {
        $this->sessionUtility = $sessionUtility;
    }

    /**
     * @return SessionUtility|null
     */
    public function getSessionUtility(): SessionUtility
    {
        if (!isset($this->sessionUtility)) {
            $this->setSessionUtility(new SessionUtility());
        }

        return $this->sessionUtility;
    }

    /**
     * @return Config
     */
    public function getConfig(): Config
    {
        if (!isset($this->config)) {
            $this->setConfig(new Config());
        }

        return $this->config;
    }

    /**
     * @param Config $config
     */
    public function setConfig(Config $config)
    {
        $this->config = $config;
    }

    /**
     * @param null $code
     * @param null $state
     *
     * @throws \Exception
     *
     * @return bool
     */
    public function doOauth($code = null, $state = null)
    {
        $getRequest = $this->getRequestUtility()->getGet();

        // get code from GET request if $code is null
        $code = $code === null && isset($getRequest['code']) ? $getRequest['code'] : $code;

        // get state from GET request if $state is null
        $stateKey = 'state';
        $state = $state === null && isset($getRequest[$stateKey]) ? $getRequest[$stateKey] : $state;

        try {
            $this->processAccessToken($this->getAccessToken($code, true, $state));
        } catch (\Exception $e) {
            throw $e;
        }

        return true;
    }

    /**
     * @param $accessToken
     *
     * @throws \Exception
     */
    private function processAccessToken($accessToken)
    {
        if (empty($accessToken)) {
            throw new BotonomousException('Access token is not provided');
        }

        // do whatever you want with the access token
    }

    /**
     * @return RequestUtility
     */
    public function getRequestUtility(): RequestUtility
    {
        if (!isset($this->requestUtility)) {
            $this->setRequestUtility((new RequestUtility()));
        }

        return $this->requestUtility;
    }

    /**
     * @param RequestUtility $requestUtility
     */
    public function setRequestUtility(RequestUtility $requestUtility)
    {
        $this->requestUtility = $requestUtility;
    }
}