Completed
Pull Request — develop (#211)
by Jens
11:00
created

Manager::cache()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

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