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); |
|
|
|
|
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
|
|
|
|
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 theid
property of an instance of theAccount
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.