rpicms

A CMS for the Raspberry Pi
git clone git://archive.git.mtrnord.blog/RpicmsTeam/rpicms.git
Log | Files | Refs | README | LICENSE

Api.php (8279B)


      1 <?php
      2 
      3 /**
      4  *  This program is free software: you can redistribute it and/or modify
      5  *  it under the terms of the GNU Lesser General Public License as published by
      6  *  the Free Software Foundation, either version 3 of the License, or
      7  *  (at your option) any later version.
      8  *
      9  *  This program is distributed in the hope that it will be useful,
     10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  *  GNU Lesser General Public License for more details.
     13  *
     14  *  You should have received a copy of the GNU Lesser General Public License
     15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  */
     17 
     18 namespace fkooman\OAuth\Client;
     19 
     20 use fkooman\OAuth\Client\Exception\ApiException;
     21 
     22 /**
     23  * API for talking to OAuth 2.0 protected resources.
     24  *
     25  * @author François Kooman <fkooman@tuxed.net>
     26  */
     27 class Api
     28 {
     29     const RANDOM_LENGTH = 8;
     30 
     31     private $clientConfigId;
     32     private $clientConfig;
     33     private $tokenStorage;
     34     private $httpClient;
     35 
     36     public function __construct($clientConfigId, ClientConfigInterface $clientConfig, StorageInterface $tokenStorage, \Guzzle\Http\Client $httpClient)
     37     {
     38         $this->setClientConfigId($clientConfigId);
     39         $this->setClientConfig($clientConfig);
     40         $this->setTokenStorage($tokenStorage);
     41         $this->setHttpClient($httpClient);
     42     }
     43 
     44     public function setClientConfigId($clientConfigId)
     45     {
     46         if (!is_string($clientConfigId) || 0 >= strlen($clientConfigId)) {
     47             throw new ApiException("clientConfigId must be a non-empty string");
     48         }
     49         $this->clientConfigId = $clientConfigId;
     50     }
     51 
     52     public function setClientConfig(ClientConfigInterface $clientConfig)
     53     {
     54         $this->clientConfig = $clientConfig;
     55     }
     56 
     57     public function setTokenStorage(StorageInterface $tokenStorage)
     58     {
     59         $this->tokenStorage = $tokenStorage;
     60     }
     61 
     62     public function setHttpClient(\Guzzle\Http\Client $httpClient)
     63     {
     64         $this->httpClient = $httpClient;
     65     }
     66 
     67     public function getRefreshToken(Context $context)
     68     {
     69         return $this->tokenStorage->getRefreshToken($this->clientConfigId, $context);
     70     }
     71 
     72     public function getAccessToken(Context $context)
     73     {
     74         // do we have a valid access token?
     75         $accessToken = $this->tokenStorage->getAccessToken($this->clientConfigId, $context);
     76         if (false !== $accessToken) {
     77             if (null === $accessToken->getExpiresIn()) {
     78                 // no expiry set, assume always valid
     79                 return $accessToken;
     80             }
     81             // check if expired
     82             if (time() < $accessToken->getIssueTime() + $accessToken->getExpiresIn()) {
     83                 // not expired
     84                 return $accessToken;
     85             }
     86             // expired, delete it and continue
     87             $this->tokenStorage->deleteAccessToken($accessToken);
     88         }
     89 
     90         // no valid access token, is there a refresh_token?
     91         $refreshToken = $this->getRefreshToken($context);
     92         if (false !== $refreshToken) {
     93             // obtain a new access token with refresh token
     94             $tokenRequest = new TokenRequest($this->httpClient, $this->clientConfig);
     95             $tokenResponse = $tokenRequest->withRefreshToken($refreshToken->getRefreshToken());
     96             if (false === $tokenResponse) {
     97                 // unable to fetch with RefreshToken, delete it
     98                 $this->tokenStorage->deleteRefreshToken($refreshToken);
     99 
    100                 return false;
    101             }
    102 
    103             if (null === $tokenResponse->getScope()) {
    104                 // no scope in response, we assume we got the requested scope
    105                 $scope = $context->getScope();
    106             } else {
    107                 // the scope we got should be a superset of what we requested
    108                 $scope = $tokenResponse->getScope();
    109                 if (!$scope->hasScope($context->getScope())) {
    110                     // we didn't get the scope we requested, stop for now
    111                     // FIXME: we need to implement a way to request certain
    112                     // scope as being optional, while others need to be
    113                     // required
    114                     throw new ApiException("requested scope not obtained");
    115                 }
    116             }
    117 
    118             $accessToken = new AccessToken(
    119                 array(
    120                     "client_config_id" => $this->clientConfigId,
    121                     "user_id" => $context->getUserId(),
    122                     "scope" => $scope,
    123                     "access_token" => $tokenResponse->getAccessToken(),
    124                     "token_type" => $tokenResponse->getTokenType(),
    125                     "issue_time" => time(),
    126                     "expires_in" => $tokenResponse->getExpiresIn(),
    127                 )
    128             );
    129             $this->tokenStorage->storeAccessToken($accessToken);
    130             if (null !== $tokenResponse->getRefreshToken()) {
    131                 // delete the existing refresh token as we'll store a new one
    132                 $this->tokenStorage->deleteRefreshToken($refreshToken);
    133                 $refreshToken = new RefreshToken(
    134                     array(
    135                         "client_config_id" => $this->clientConfigId,
    136                         "user_id" => $context->getUserId(),
    137                         "scope" => $scope,
    138                         "refresh_token" => $tokenResponse->getRefreshToken(),
    139                         "issue_time" => time(),
    140                     )
    141                 );
    142                 $this->tokenStorage->storeRefreshToken($refreshToken);
    143             }
    144 
    145             return $accessToken;
    146         }
    147         // no access token, and refresh token didn't work either or was not there, probably the tokens were revoked
    148         return false;
    149     }
    150 
    151     public function deleteAccessToken(Context $context)
    152     {
    153         $accessToken = $this->getAccessToken($context);
    154         if (false !== $accessToken) {
    155             $this->tokenStorage->deleteAccessToken($accessToken);
    156         }
    157     }
    158 
    159     public function deleteRefreshToken(Context $context)
    160     {
    161         $refreshToken = $this->getRefreshToken($context);
    162         if (false !== $refreshToken) {
    163             $this->tokenStorage->deleteRefreshToken($refreshToken);
    164         }
    165     }
    166 
    167     public function getAuthorizeUri(Context $context, $stateValue = null)
    168     {
    169         // allow caller to override a random generated state
    170         // FIXME: is this actually used anywhere?
    171         if (null === $stateValue) {
    172             $stateValue = bin2hex(openssl_random_pseudo_bytes(self::RANDOM_LENGTH));
    173         } else {
    174             if (!is_string($stateValue) || 0 >= strlen($stateValue)) {
    175                 throw new ApiException("state must be a non-empty string");
    176             }
    177         }
    178 
    179         // try to get a new access token
    180         $this->tokenStorage->deleteStateForContext($this->clientConfigId, $context);
    181         $state = new State(
    182             array(
    183                 "client_config_id" => $this->clientConfigId,
    184                 "user_id" => $context->getUserId(),
    185                 "scope" => $context->getScope(),
    186                 "issue_time" => time(),
    187                 "state" => $stateValue,
    188             )
    189         );
    190         if (false === $this->tokenStorage->storeState($state)) {
    191             throw new ApiException("unable to store state");
    192         }
    193 
    194         $q = array (
    195             "client_id" => $this->clientConfig->getClientId(),
    196             "response_type" => "code",
    197             "state" => $state->getState(),
    198         );
    199 
    200         // scope
    201         $contextScope = $context->getScope();
    202         if (!$contextScope->isEmpty()) {
    203             if ($this->clientConfig->getUseCommaSeparatedScope()) {
    204                 $q['scope'] = $contextScope->toString(",");
    205             } else {
    206                 $q['scope'] = $contextScope->toString();
    207             }
    208         }
    209 
    210         // redirect_uri
    211         if ($this->clientConfig->getRedirectUri()) {
    212             $q['redirect_uri'] = $this->clientConfig->getRedirectUri();
    213         }
    214 
    215         $separator = (false === strpos($this->clientConfig->getAuthorizeEndpoint(), "?")) ? "?" : "&";
    216         $authorizeUri = $this->clientConfig->getAuthorizeEndpoint().$separator.http_build_query($q, null, '&');
    217 
    218         return $authorizeUri;
    219     }
    220 }