Passed
Branch refactor_config_class (788a46)
by Jens
08:38
created

Manager::setCacheAdapter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * @author @jenschude <[email protected]>
4
 * @created: 22.01.15, 12:34
5
 */
6
7
namespace Commercetools\Core\Client\OAuth;
8
9
use Commercetools\Core\Client\Adapter\CorrelationIdAware;
10
use Commercetools\Core\Client\Credentials;
11
use Commercetools\Core\Config;
12
use Commercetools\Core\Error\ApiException;
13
use Commercetools\Core\Helper\CorrelationIdProvider;
14
use Psr\Cache\CacheItemPoolInterface;
15
use Psr\Http\Message\ResponseInterface;
16
use Commercetools\Core\AbstractHttpClient;
17
use Commercetools\Core\Cache\CacheAdapterFactory;
18
use Commercetools\Core\Error\InvalidClientCredentialsException;
19
use Psr\SimpleCache\CacheInterface;
20
21
/**
22
 * @package Commercetools\Core\OAuth
23
 * @internal
24
 */
25
class Manager extends AbstractHttpClient implements TokenProvider
26
{
27
    const TOKEN_CACHE_KEY = 'commercetools_io_access_token';
28
29
    const ANONYMOUS_ID = 'anonymous_id';
30
    const REFRESH_TOKEN = 'refresh_token';
31
    const ACCESS_TOKEN = 'access_token';
32
    const EXPIRES_IN = 'expires_in';
33
    const ERROR = 'error';
34
    const SCOPE = 'scope';
35
    const ERROR_DESCRIPTION = 'error_description';
36
37
    /**
38
     * @var array
39
     */
40
    protected $cacheKeys;
41
42
    /**
43
     * @var CacheItemPoolInterface|CacheInterface
44
     */
45
    protected $cacheAdapter;
46
47
    /**
48
     * @var CacheAdapterFactory
49
     */
50
    protected $cacheAdapterFactory;
51
52
    /**
53
     * @var Credentials
54
     */
55
    protected $credentials;
56
57
    /**
58
     * @var string
59
     */
60
    protected $cacheDir;
61
62 82
    public function __construct($config, $cache = null)
63
    {
64 82
        parent::__construct($config);
65 82
        $this->cacheKeys = [];
66 82
        $this->setCacheAdapter($cache);
67 82
    }
68
69
    /**
70
     * @param Config|array $config
71
     * @return $this
72
     */
73 82
    public function setConfig($config)
74
    {
75 82
        parent::setConfig($config);
76 82
        $this->clientConfig = $config->getOauthClientConfig();
77 82
        $this->credentials = $config->getCredentials();
78 82
        $this->credentials->check();
79 82
        $this->cacheDir = $config->getCacheDir();
80
81 82
        return $this;
82
    }
83
84
    /**
85
     * @return CacheAdapterFactory
86
     */
87 82
    public function getCacheAdapterFactory()
88
    {
89 82
        if (is_null($this->cacheAdapterFactory)) {
90 82
            $this->cacheAdapterFactory = new CacheAdapterFactory($this->cacheDir);
91
        }
92 82
        return $this->cacheAdapterFactory;
93
    }
94
95
    /**
96
     * @param $cache
97
     * @return $this
98
     */
99 82
    public function setCacheAdapter($cache)
100
    {
101 82
        $this->cacheAdapter = $this->getCacheAdapterFactory()->get($cache);
102
103 82
        return $this;
104
    }
105
106
    /**
107
     * @return CacheItemPoolInterface|CacheInterface
108
     */
109 565
    protected function getCacheAdapter()
110
    {
111 565
        return $this->cacheAdapter;
112
    }
113
114
    /**
115
     * @return Token
116
     * @throws InvalidClientCredentialsException
117
     */
118 565
    public function getToken()
119
    {
120 565
        $scope = $this->credentials->getScope();
121 565
        if ($this->credentials->getGrantType() == Config::GRANT_TYPE_BEARER_TOKEN) {
122 1
            return new Token($this->credentials->getBearerToken(), null, $scope);
123
        }
124 564
        if ($token = $this->getCacheToken()) {
125 559
            return new Token($token, null, $scope);
126
        }
127
128 40
        return $this->refreshToken();
129
    }
130
131
    /**
132
     * @return Token
133
     * @throws InvalidClientCredentialsException
134
     */
135 46
    public function refreshToken()
136
    {
137 46
        $scope = $this->credentials->getScope();
138 46
        $grantType = $this->credentials->getGrantType();
139 46
        $data = [Config::GRANT_TYPE => $grantType];
140 46
        if (!empty($scope)) {
141 44
            $data[Config::SCOPE] = $scope;
142
        }
143
144
        switch ($grantType) {
145 46
            case Config::GRANT_TYPE_BEARER_TOKEN:
146 1
                return new Token($this->credentials->getBearerToken(), null, $scope);
147 45
            case Config::GRANT_TYPE_PASSWORD:
148 19
                $user = $this->credentials->getUsername();
149 19
                $password = $this->credentials->getPassword();
150 19
                $data[Config::USER_NAME] = $user;
151 19
                $data[Config::PASSWORD] = $password;
152 19
                break;
153 40
            case Config::GRANT_TYPE_REFRESH:
154 1
                $refreshToken = $this->credentials->getRefreshToken();
155 1
                $data[Config::REFRESH_TOKEN] = $refreshToken;
156 1
                break;
157 40
            case Config::GRANT_TYPE_ANONYMOUS:
158 11
                $data[Config::GRANT_TYPE] = Config::GRANT_TYPE_CLIENT;
159 11
                $anonymousId = $this->credentials->getAnonymousId();
160 11
                if (!empty($anonymousId)) {
161 4
                    $data[Config::ANONYMOUS_ID] = $anonymousId;
162
                }
163
        }
164
165 45
        $token = $this->getBearerToken($data);
166
167
        // ensure token to be invalidated in cache before TTL
168 44
        $ttl = max(1, floor($token->getTtl()/2));
169
170 44
        $this->cache($token, $ttl);
171
172 44
        if ($grantType === Config::GRANT_TYPE_PASSWORD || $grantType == Config::GRANT_TYPE_ANONYMOUS) {
173 28
            $this->credentials->setRefreshToken($token->getRefreshToken());
174 28
            $this->cache($token, $ttl, $this->getCacheKey(Config::GRANT_TYPE_REFRESH));
175
        }
176
177 44
        return $token;
178
    }
179
180 44
    protected function cache(Token $token, $ttl, $cacheKey = null)
181
    {
182 44
        if (is_null($cacheKey)) {
183 44
            $cacheKey = $this->getCacheKey();
184
        }
185 44
        $cache = $this->getCacheAdapter();
186 44
        if ($cache instanceof CacheItemPoolInterface) {
187 43
            $item = $cache->getItem($cacheKey)->set($token->getToken())->expiresAfter((int)$ttl);
188 43
            $cache->save($item);
189
        }
190 44
        if ($cache instanceof CacheInterface) {
191 44
            $cache->set($cacheKey, $token->getToken(), (int)$ttl);
192
        }
193 44
    }
194
195 564
    protected function getCacheToken()
196
    {
197 564
        $cache = $this->getCacheAdapter();
198 564
        if ($cache instanceof CacheItemPoolInterface) {
199 562
            $item = $cache->getItem($this->getCacheKey());
200 562
            if ($item->isHit()) {
201 558
                return $item->get();
202
            }
203
        }
204 41
        if ($cache instanceof CacheInterface) {
205 41
            return $cache->get($this->getCacheKey(), false);
206
        }
207
208
        return false;
209
    }
210
211
    /**
212
     * @return string
213
     */
214 565
    protected function getCacheKey($grantType = null)
215
    {
216 565
        $scope = $this->credentials->getScope();
217 565
        if (is_null($grantType)) {
218 565
            $grantType = $this->credentials->getGrantType();
219
        }
220 565
        $cacheScope = $scope . '-' . $grantType;
221
222
        switch ($grantType) {
223 565
            case Config::GRANT_TYPE_PASSWORD:
224 19
                $user = base64_encode($this->credentials->getUsername());
225 19
                $cacheScope .= '-' . $user;
226 19
                break;
227 565
            case Config::GRANT_TYPE_REFRESH:
228 28
                $token = $this->credentials->getRefreshToken();
229 28
                $cacheScope .= '-' . $token;
230 28
                break;
231 565
            case Config::GRANT_TYPE_ANONYMOUS:
232 10
                $anonymousId = $this->credentials->getAnonymousId();
233 10
                if (!empty($anonymousId)) {
234 3
                    $cacheScope .= '-' . $anonymousId;
235
                }
236 10
                break;
237
        }
238
239 565
        if (!isset($this->cacheKeys[$cacheScope])) {
240 41
            $this->cacheKeys[$cacheScope] = static::TOKEN_CACHE_KEY . '_' .
241 41
                sha1($cacheScope);
242
        }
243
244 565
        return $this->cacheKeys[$cacheScope];
245
    }
246
247
    /**
248
     * @param array $data
249
     * @return Token
250
     * @throws ApiException
251
     * @throws \Commercetools\Core\Error\BadGatewayException
252
     * @throws \Commercetools\Core\Error\GatewayTimeoutException
253
     * @throws \Commercetools\Core\Error\ServiceUnavailableException
254
     */
255 45
    protected function getBearerToken(array $data)
256
    {
257
        try {
258 45
            $response = $this->execute($data);
259 3
        } catch (ApiException $exception) {
260 3
            throw ApiException::create($exception->getRequest(), $exception->getResponse());
261
        }
262
263 44
        $result = json_decode($response->getBody(), true);
264
265 44
        $token = new Token($result[static::ACCESS_TOKEN], $result[static::EXPIRES_IN], $result[static::SCOPE]);
266 44
        $token->setValidTo(new \DateTime('now +' . $result[static::EXPIRES_IN] . ' seconds'));
267 44
        if (isset($result[static::REFRESH_TOKEN])) {
268 28
            $token->setRefreshToken($result[static::REFRESH_TOKEN]);
269
        }
270
271 44
        return $token;
272
    }
273
274
    /**
275
     * @inheritDoc
276
     */
277 50
    public function getHttpClient($options = [])
278
    {
279 50
        if (is_null($this->httpClient)) {
280 49
            $clientOptions = $this->clientConfig->getClientOptions();
281 49
            if (count($clientOptions) > 0) {
282 49
                $options = array_merge($clientOptions, $options);
283
            }
284 49
            $client = parent::getHttpClient($options);
285 49
            if ($this->clientConfig->getCorrelationIdProvider() instanceof CorrelationIdProvider
286 49
                && $client instanceof CorrelationIdAware
287
            ) {
288 1
                $client->setCorrelationIdProvider($this->clientConfig->getCorrelationIdProvider());
289
            }
290 49
            $this->httpClient = $client;
291
        }
292 50
        return $this->httpClient;
293
    }
294
295
    /**
296
     * @return string
297
     */
298 50
    protected function getOauthUrl()
299
    {
300 50
        switch ($this->credentials->getGrantType()) {
301 50
            case Credentials::GRANT_TYPE_ANONYMOUS:
302 11
                return $this->clientConfig->getBaseUri() . '/oauth/' .
303 11
                    $this->credentials->getProject() . '/anonymous/token';
304 45
            case Credentials::GRANT_TYPE_PASSWORD:
305 40
            case Credentials::GRANT_TYPE_REFRESH:
306 24
                return $this->clientConfig->getBaseUri() . '/oauth/' .
307 24
                    $this->credentials->getProject() . '/customers/token';
308
            default:
309 40
                return $this->clientConfig->getBaseUri() . '/oauth/token';
310
        }
311
    }
312
313
    /**
314
     * @param $data
315
     * @return ResponseInterface
316
     */
317 46
    public function execute($data)
318
    {
319 46
        return $this->getHttpClient()->authenticate(
320 46
            $this->getOauthUrl(),
321 46
            $this->credentials->getClientId(),
322 46
            $this->credentials->getClientSecret(),
323 46
            $data
324
        );
325
    }
326
327
    /**
328
     * @return string
329
     */
330 49
    protected function getBaseUrl()
331
    {
332 49
        return $this->getOauthUrl();
333
    }
334
}
335