Completed
Push — master ( 5f432b...5d5b15 )
by Jens
15:50 queued 05:48
created

Manager   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 100%

Importance

Changes 6
Bugs 0 Features 2
Metric Value
wmc 34
c 6
b 0
f 2
lcom 1
cbo 10
dl 0
loc 240
ccs 104
cts 104
cp 1
rs 9.2

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getCacheAdapterFactory() 0 7 2
A setCacheAdapter() 0 6 1
A getCacheAdapter() 0 4 1
A getToken() 0 9 2
C refreshToken() 0 39 7
A cache() 0 15 4
A getCacheToken() 0 15 4
C getCacheKey() 0 32 7
A getBearerToken() 0 18 3
A execute() 0 9 1
A getBaseUrl() 0 4 1
1
<?php
2
/**
3
 * @author @jayS-de <[email protected]>
4
 * @created: 22.01.15, 12:34
5
 */
6
7
namespace Commercetools\Core\Client\OAuth;
8
9
use Cache\Adapter\Common\CacheItem;
10
use Commercetools\Core\Config;
11
use Commercetools\Core\Error\ApiException;
12
use Psr\Cache\CacheItemPoolInterface;
13
use Psr\Http\Message\ResponseInterface;
14
use Commercetools\Core\AbstractHttpClient;
15
use Commercetools\Core\Cache\CacheAdapterFactory;
16
use Commercetools\Core\Cache\CacheAdapterInterface;
17
use Commercetools\Core\Error\InvalidClientCredentialsException;
18
19
/**
20
 * @package Commercetools\Core\OAuth
21
 * @internal
22
 */
23
class Manager extends AbstractHttpClient
24
{
25
    const TOKEN_CACHE_KEY = 'commercetools_io_access_token';
26
27
    const ANONYMOUS_ID = 'anonymous_id';
28
    const REFRESH_TOKEN = 'refresh_token';
29
    const ACCESS_TOKEN = 'access_token';
30
    const EXPIRES_IN = 'expires_in';
31
    const ERROR = 'error';
32
    const SCOPE = 'scope';
33
    const ERROR_DESCRIPTION = 'error_description';
34
35
    /**
36
     * @var array
37
     */
38
    protected $cacheKeys;
39
40
    /**
41
     * @var CacheAdapterInterface|CacheItemPoolInterface
42
     */
43
    protected $cacheAdapter;
44
45
    /**
46
     * @var CacheAdapterFactory
47
     */
48
    protected $cacheAdapterFactory;
49
50 69
    public function __construct($config, $cache = null)
51
    {
52 69
        parent::__construct($config);
53 69
        $this->cacheKeys = [];
54 69
        $this->setCacheAdapter($cache);
55 69
    }
56
57
    /**
58
     * @return CacheAdapterFactory
59
     */
60 69
    public function getCacheAdapterFactory()
61
    {
62 69
        if (is_null($this->cacheAdapterFactory)) {
63 69
            $this->cacheAdapterFactory = new CacheAdapterFactory($this->getConfig()->getCacheDir());
64
        }
65 69
        return $this->cacheAdapterFactory;
66
    }
67
68
    /**
69
     * @param $cache
70
     * @return $this
71
     */
72 69
    public function setCacheAdapter($cache)
73
    {
74 69
        $this->cacheAdapter = $this->getCacheAdapterFactory()->get($cache);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getCacheAdapterFactory()->get($cache) can also be of type object<Commercetools\Cor...\CacheAdapterInterface>. However, the property $cacheAdapter is declared as type object<Psr\Cache\CacheItemPoolInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
75
76 69
        return $this;
77
    }
78
79
    /**
80
     * @internal will become protected in version 2.0
81
     * @return CacheAdapterInterface|CacheItemPoolInterface
82
     */
83 354
    public function getCacheAdapter()
84
    {
85 354
        return $this->cacheAdapter;
86
    }
87
88
    /**
89
     * @return Token
90
     * @throws InvalidClientCredentialsException
91
     */
92 352
    public function getToken()
93
    {
94 352
        $scope = $this->getConfig()->getScope();
95 352
        if ($token = $this->getCacheToken()) {
96 350
            return new Token($token, null, $scope);
97
        }
98
99 35
        return $this->refreshToken();
100
    }
101
102
    /**
103
     * @return Token
104
     * @throws InvalidClientCredentialsException
105
     */
106 42
    public function refreshToken()
107
    {
108 42
        $scope = $this->getConfig()->getScope();
109 42
        $grantType = $this->getConfig()->getGrantType();
110 42
        $data = [Config::SCOPE => $scope, Config::GRANT_TYPE => $grantType];
111
112
        switch ($grantType) {
113 42
            case Config::GRANT_TYPE_PASSWORD:
114 19
                $user = $this->getConfig()->getUsername();
115 19
                $password = $this->getConfig()->getPassword();
116 19
                $data[Config::USER_NAME] = $user;
117 19
                $data[Config::PASSWORD] = $password;
118 19
                break;
119 37
            case Config::GRANT_TYPE_REFRESH:
120 1
                $refreshToken = $this->getConfig()->getRefreshToken();
121 1
                $data[Config::REFRESH_TOKEN] = $refreshToken;
122 1
                break;
123 37
            case Config::GRANT_TYPE_ANONYMOUS:
124 11
                $data[Config::GRANT_TYPE] = Config::GRANT_TYPE_CLIENT;
125 11
                $anonymousId = $this->getConfig()->getAnonymousId();
126 11
                if (!empty($anonymousId)) {
127 4
                    $data[Config::ANONYMOUS_ID] = $anonymousId;
128
                }
129
        }
130
131 42
        $token = $this->getBearerToken($data);
132
133
        // ensure token to be invalidated in cache before TTL
134 41
        $ttl = max(1, floor($token->getTtl()/2));
135
136 41
        $this->cache($token, $ttl);
137
138 41
        if ($grantType === Config::GRANT_TYPE_PASSWORD || $grantType == Config::GRANT_TYPE_ANONYMOUS) {
139 28
            $this->getConfig()->setRefreshToken($token->getRefreshToken());
140 28
            $this->cache($token, $ttl, $this->getCacheKey(Config::GRANT_TYPE_REFRESH));
141
        }
142
143 41
        return $token;
144
    }
145
146 41
    protected function cache(Token $token, $ttl, $cacheKey = null)
147
    {
148 41
        if (is_null($cacheKey)) {
149 41
            $cacheKey = $this->getCacheKey();
150
        }
151 41
        $cache = $this->getCacheAdapter();
152 41
        if ($cache instanceof CacheAdapterInterface) {
153 5
            $cache->store($cacheKey, $token->getToken(), (int)$ttl);
154
        }
155 41
        if ($cache instanceof CacheItemPoolInterface) {
156 36
            $item = new CacheItem($cacheKey, true, $token->getToken());
157 36
            $item->expiresAfter((int)$ttl);
158 36
            $cache->save($item);
159
        }
160 41
    }
161
162 352
    protected function getCacheToken()
163
    {
164 352
        $cache = $this->getCacheAdapter();
165 352
        if ($cache instanceof CacheAdapterInterface) {
166 6
            return $cache->fetch($this->getCacheKey());
167
        }
168 347
        if ($cache instanceof CacheItemPoolInterface) {
169 347
            $item = $cache->getItem($this->getCacheKey());
170 347
            if ($item->isHit()) {
171 346
                return $item->get();
172
            }
173
        }
174
175 30
        return false;
176
    }
177
178
    /**
179
     * @return string
180
     */
181 352
    protected function getCacheKey($grantType = null)
182
    {
183 352
        $scope = $this->getConfig()->getScope();
184 352
        if (is_null($grantType)) {
185 352
            $grantType = $this->getConfig()->getGrantType();
186
        }
187 352
        $cacheScope = $scope . '-' . $grantType;
188
189
        switch ($grantType) {
190 352
            case Config::GRANT_TYPE_PASSWORD:
191 19
                $user = base64_encode($this->getConfig()->getUsername());
192 19
                $cacheScope .= '-' . $user;
193 19
                break;
194 352
            case Config::GRANT_TYPE_REFRESH:
195 28
                $token = $this->getConfig()->getRefreshToken();
196 28
                $cacheScope .= '-' . $token;
197 28
                break;
198 352
            case Config::GRANT_TYPE_ANONYMOUS:
199 10
                $anonymousId = $this->getConfig()->getAnonymousId();
200 10
                if (!empty($anonymousId)) {
201 3
                    $cacheScope .= '-' . $anonymousId;
202
                }
203 10
                break;
204
        }
205
206 352
        if (!isset($this->cacheKeys[$cacheScope])) {
207 35
            $this->cacheKeys[$cacheScope] = static::TOKEN_CACHE_KEY . '_' .
208 35
                sha1($cacheScope);
209
        }
210
211 352
        return $this->cacheKeys[$cacheScope];
212
    }
213
214
    /**
215
     * @param array $data
216
     * @return Token
217
     * @throws ApiException
218
     * @throws \Commercetools\Core\Error\BadGatewayException
219
     * @throws \Commercetools\Core\Error\GatewayTimeoutException
220
     * @throws \Commercetools\Core\Error\ServiceUnavailableException
221
     */
222 42
    protected function getBearerToken(array $data)
223
    {
224
        try {
225 42
            $response = $this->execute($data);
226 3
        } catch (ApiException $exception) {
227 3
            throw ApiException::create($exception->getRequest(), $exception->getResponse());
228
        }
229
230 41
        $result = json_decode($response->getBody(), true);
231
232 41
        $token = new Token($result[static::ACCESS_TOKEN], $result[static::EXPIRES_IN], $result[static::SCOPE]);
233 41
        $token->setValidTo(new \DateTime('now +' . $result[static::EXPIRES_IN] . ' seconds'));
234 41
        if (isset($result[static::REFRESH_TOKEN])) {
235 28
            $token->setRefreshToken($result[static::REFRESH_TOKEN]);
236
        }
237
238 41
        return $token;
239
    }
240
241
    /**
242
     * @param $data
243
     * @return ResponseInterface
244
     */
245 42
    public function execute($data)
246
    {
247 42
        return $this->getHttpClient()->authenticate(
248 42
            $this->getConfig()->getOauthUrl(),
249 42
            $this->getConfig()->getClientId(),
250 42
            $this->getConfig()->getClientSecret(),
251
            $data
252
        );
253
    }
254
255
    /**
256
     * @return string
257
     */
258 41
    protected function getBaseUrl()
259
    {
260 41
        return $this->getConfig()->getOauthUrl();
261
    }
262
}
263