Completed
Push — develop ( 5e8154...a89f38 )
by Jens
08:15
created

Manager::refreshToken()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 40
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 7

Importance

Changes 3
Bugs 0 Features 2
Metric Value
c 3
b 0
f 2
dl 0
loc 40
ccs 26
cts 26
cp 1
rs 6.7272
cc 7
eloc 28
nc 10
nop 0
crap 7
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 40
50
    public function __construct($config, $cache = null)
51 40
    {
52 40
        parent::__construct($config);
53 40
        $this->cacheKeys = [];
54 40
        $this->setCacheAdapter($cache);
55
    }
56
57
    /**
58
     * @return CacheAdapterFactory
59 40
     */
60
    public function getCacheAdapterFactory()
61 40
    {
62 40
        if (is_null($this->cacheAdapterFactory)) {
63
            $this->cacheAdapterFactory = new CacheAdapterFactory($this->getConfig()->getCacheDir());
64 40
        }
65
        return $this->cacheAdapterFactory;
66
    }
67
68
    /**
69
     * @param $cache
70
     * @return $this
71 40
     */
72
    public function setCacheAdapter($cache)
73 40
    {
74
        $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 40
76
        return $this;
77
    }
78
79
    /**
80
     * @internal will become protected in version 2.0
81
     * @return CacheAdapterInterface|CacheItemPoolInterface
82 320
     */
83
    public function getCacheAdapter()
84 320
    {
85
        return $this->cacheAdapter;
86
    }
87
88
    /**
89
     * @return Token
90
     * @throws InvalidClientCredentialsException
91 318
     */
92
    public function getToken()
93 318
    {
94 318
        $scope = $this->getConfig()->getScope();
95 317
        if ($token = $this->getCacheToken()) {
96
            return new Token($token, null, $scope);
97
        }
98 9
99
        return $this->refreshToken();
100
    }
101
102
    /**
103
     * @return Token
104
     * @throws InvalidClientCredentialsException
105 12
     */
106
    public function refreshToken()
107 12
    {
108 12
        $scope = $this->getConfig()->getScope();
109 12
        $grantType = $this->getConfig()->getGrantType();
110
        $data = [Config::SCOPE => $scope, Config::GRANT_TYPE => $grantType];
111 12
112 4
        switch ($grantType) {
113 4
            case Config::GRANT_TYPE_PASSWORD:
114 4
                $user = $this->getConfig()->getUsername();
115 4
                $password = $this->getConfig()->getPassword();
116 9
                $data[Config::USER_NAME] = $user;
117 1
                $data[Config::PASSWORD] = $password;
118 1
                break;
119
            case Config::GRANT_TYPE_REFRESH:
120
                $refreshToken = $this->getConfig()->getRefreshToken();
121 12
                $data[Config::REFRESH_TOKEN] = $refreshToken;
122
                break;
123 11
            case Config::GRANT_TYPE_ANONYMOUS:
124 4
                $data[Config::GRANT_TYPE] = Config::GRANT_TYPE_CLIENT;
125 4
                $anonymousId = $this->getConfig()->getAnonymousId();
126
                if (!empty($anonymousId)) {
127
                    $data[Config::ANONYMOUS_ID] = $anonymousId;
128
                }
129 11
        }
130 11
131
        $token = $this->getBearerToken($data);
132 11
133
        // ensure token to be invalidated in cache before TTL
134
        $ttl = max(1, floor($token->getTtl()/2));
135 11
136
        $this->cache($token, $ttl);
137 11
138 11
        if ($grantType === Config::GRANT_TYPE_PASSWORD || $grantType == Config::GRANT_TYPE_ANONYMOUS) {
139 8
            $this->getConfig()->setGrantType(Config::GRANT_TYPE_REFRESH);
140
            $this->getConfig()->setRefreshToken($token->getRefreshToken());
141 11
            $this->cache($token, $ttl);
142 3
        }
143 3
144 3
        return $token;
145
    }
146 11
147
    protected function cache(Token $token, $ttl)
148 318
    {
149
        $cache = $this->getCacheAdapter();
150 318
        if ($cache instanceof CacheAdapterInterface) {
151 318
            $cache->store($this->getCacheKey(), $token->getToken(), (int)$ttl);
152 9
        }
153
        if ($cache instanceof CacheItemPoolInterface) {
154 313
            $item = new CacheItem($this->getCacheKey(), true, $token->getToken());
155 313
            $item->expiresAfter((int)$ttl);
156 313
            $cache->save($item);
157 313
        }
158
    }
159
160
    protected function getCacheToken()
161 1
    {
162
        $cache = $this->getCacheAdapter();
163
        if ($cache instanceof CacheAdapterInterface) {
164
            return $cache->fetch($this->getCacheKey());
165
        }
166
        if ($cache instanceof CacheItemPoolInterface) {
167 318
            $item = $cache->getItem($this->getCacheKey());
168
            if ($item->isHit()) {
169 318
                return $item->get();
170 318
            }
171 318
        }
172
173 318
        return false;
174 3
    }
175 3
176 318
    /**
177 4
     * @return string
178 4
     */
179
    protected function getCacheKey()
180
    {
181 318
        $scope = $this->getConfig()->getScope();
182 11
        $grantType = $this->getConfig()->getGrantType();
183 11
        $cacheScope = $scope . '-' . $grantType;
184
185
        switch ($grantType) {
186 318
            case Config::GRANT_TYPE_PASSWORD:
187
                $user = base64_encode($this->getConfig()->getUsername());
188
                $cacheScope .= '-' . $user;
189
                break;
190
            case Config::GRANT_TYPE_REFRESH:
191
                $token = $this->getConfig()->getRefreshToken();
192
                $cacheScope .= '-' . $token;
193
                break;
194
            case Config::GRANT_TYPE_ANONYMOUS:
195
                $anonymousId = $this->getConfig()->getAnonymousId();
196
                if (!empty($anonymousId)) {
197 12
                    $cacheScope .= '-' . $anonymousId;
198
                }
199
                break;
200 12
        }
201 1
202 1
        if (!isset($this->cacheKeys[$cacheScope])) {
203
            $this->cacheKeys[$cacheScope] = static::TOKEN_CACHE_KEY . '_' .
204
                sha1($cacheScope);
205 11
        }
206
207 11
        return $this->cacheKeys[$cacheScope];
208 11
    }
209 11
210 4
    /**
211
     * @param array $data
212
     * @return Token
213 11
     * @throws ApiException
214
     * @throws \Commercetools\Core\Error\BadGatewayException
215
     * @throws \Commercetools\Core\Error\GatewayTimeoutException
216
     * @throws \Commercetools\Core\Error\ServiceUnavailableException
217
     */
218
    protected function getBearerToken(array $data)
219
    {
220 12
        try {
221
            $response = $this->execute($data);
222 12
        } catch (ApiException $exception) {
223 12
            throw ApiException::create($exception->getRequest(), $exception->getResponse());
224 12
        }
225 12
226
        $result = json_decode($response->getBody(), true);
227
228
        $token = new Token($result[static::ACCESS_TOKEN], $result[static::EXPIRES_IN], $result[static::SCOPE]);
229
        $token->setValidTo(new \DateTime('now +' . $result[static::EXPIRES_IN] . ' seconds'));
230
        if (isset($result[static::REFRESH_TOKEN])) {
231
            $token->setRefreshToken($result[static::REFRESH_TOKEN]);
232
        }
233 12
234
        return $token;
235 12
    }
236
237
    /**
238
     * @param $data
239
     * @return ResponseInterface
240
     */
241
    public function execute($data)
242
    {
243
        return $this->getHttpClient()->authenticate(
244
            $this->getConfig()->getOauthUrl(),
245
            $this->getConfig()->getClientId(),
246
            $this->getConfig()->getClientSecret(),
247
            $data
248
        );
249
    }
250
251
    /**
252
     * @return string
253
     */
254
    protected function getBaseUrl()
255
    {
256
        return $this->getConfig()->getOauthUrl();
257
    }
258
}
259