Completed
Pull Request — master (#49)
by
unknown
26:03
created

TokenManager::sendPostRequest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace MovingImage\Client\VMPro\Manager;
4
5
use Cache\Adapter\Void\VoidCachePool;
6
use GuzzleHttp\ClientInterface;
7
use MovingImage\Client\VMPro\Entity\ApiCredentials;
8
use MovingImage\Client\VMPro\Entity\Token;
9
use MovingImage\Client\VMPro\Extractor\TokenExtractor;
10
use MovingImage\Client\VMPro\Util\Logging\Traits\LoggerAwareTrait;
11
use Psr\Cache\CacheItemPoolInterface;
12
use Psr\Log\LoggerAwareInterface;
13
14
/**
15
 * Class TokenManager.
16
 *
17
 * @author Ruben Knol <[email protected]>
18
 */
19
class TokenManager implements LoggerAwareInterface
20
{
21
    use LoggerAwareTrait;
22
23
    /**
24
     * @var ClientInterface
25
     */
26
    private $httpClient;
27
28
    /**
29
     * @var ApiCredentials
30
     */
31
    protected $credentials;
32
33
    /**
34
     * @var TokenExtractor
35
     */
36
    private $tokenExtractor;
37
38
    /**
39
     * @var Token
40
     */
41
    private $accessToken;
42
43
    /**
44
     * @var Token
45
     */
46
    private $refreshToken;
47
48
    /**
49
     * If provided in the constructor, will be used to cache the access token.
50
     *
51
     * @var CacheItemPoolInterface
52
     */
53
    private $cacheItemPool;
54
55
    /**
56
     * TokenManager constructor.
57
     *
58
     * @param ClientInterface        $httpClient
59
     * @param ApiCredentials         $credentials
60
     * @param TokenExtractor         $tokenExtractor
61
     * @param CacheItemPoolInterface $cacheItemPool
62
     */
63
    public function __construct(
64
        ClientInterface $httpClient,
65
        ApiCredentials $credentials,
66
        TokenExtractor $tokenExtractor,
67
        CacheItemPoolInterface $cacheItemPool = null
68
    ) {
69
        $this->httpClient = $httpClient;
70
        $this->credentials = $credentials;
71
        $this->tokenExtractor = $tokenExtractor;
72
        $this->cacheItemPool = $cacheItemPool ?: new VoidCachePool();
73
    }
74
75
    /**
76
     * Create completely fresh Access + Refresh tokens.
77
     *
78
     * @TODO Implement proper error handling
79
     *
80
     * @return array
81
     */
82
    protected function createNewTokens()
83
    {
84
        $logger = $this->getLogger();
85
        $logger->debug('Starting request to create fresh access & refresh tokens');
86
87
        $body = [
88
            'client_id' => 'anonymous',
89
            'grant_type' => 'password',
90
            'response_type' => 'token',
91
            'scope' => 'openid',
92
            'username' => $this->credentials->getUsername(),
93
            'password' => $this->credentials->getPassword(),
94
        ];
95
96
        $response = $this->sendPostRequest($body);
97
98
        $logger->debug('Successfully retrieved new access & refresh tokens', $response);
99
100
        return [
101
            'accessToken' => new Token(
102
                $response['access_token'],
103
                $this->tokenExtractor->extract($response['access_token'])
104
            ),
105
            'refreshToken' => new Token(
106
                $response['refresh_token'],
107
                $this->tokenExtractor->extract($response['refresh_token'])
108
            ),
109
        ];
110
    }
111
112
    /**
113
     * Create a new access token for a video manager using a refresh token.
114
     *
115
     * @param Token $refreshToken
116
     *
117
     * @return Token
118
     */
119
    protected function createAccessTokenFromRefreshToken(Token $refreshToken)
120
    {
121
        $logger = $this->getLogger();
122
        $logger->debug('Starting request to create fresh access token from refresh token');
123
124
        $body = [
125
            'client_id' => 'anonymous',
126
            'grant_type' => 'refresh_token',
127
            'refresh_token' => $refreshToken->getTokenString(),
128
        ];
129
130
        $response = $this->sendPostRequest($body);
131
132
        $logger->debug('Successfully retrieved new access token', $response);
133
134
        return new Token(
135
            $response['access_token'],
136
            $this->tokenExtractor->extract($response['access_token'])
137
        );
138
    }
139
140
    /**
141
     * Log information about which tokens we have.
142
     */
143
    protected function logTokenData()
144
    {
145
        $this->getLogger()->debug('Token information', [
146
            'accessTokenExists' => isset($this->accessToken),
147
            'accessTokenExpiration' => isset($this->accessToken) ? $this->accessToken->getTokenData()['exp'] : null,
148
            'accessTokenHasExpired' => isset($this->accessToken) ? $this->accessToken->expired() : null,
149
            'refreshTokenExists' => isset($this->refreshToken),
150
            'refreshTokenExpiration' => isset($this->refreshToken) ? $this->refreshToken->getTokenData()['exp'] : null,
151
            'refreshTokenHasExpired' => isset($this->refreshToken) ? $this->refreshToken->expired() : null,
152
            'localTime' => time(),
153
        ]);
154
    }
155
156
    /**
157
     * Retrieve a valid token.
158
     *
159
     * @param int $videoManagerId - deprecated and unused, preserved for BC
160
     *
161
     * @return string
162
     */
163
    public function getToken($videoManagerId = null)
0 ignored issues
show
Unused Code introduced by
The parameter $videoManagerId is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
164
    {
165
        $logger = $this->getLogger();
166
        $this->logTokenData();
167
168
        $cacheKey = sha1(sprintf('%s.%s', __METHOD__, json_encode(func_get_args())));
169
        $cacheItem = $this->cacheItemPool->getItem($cacheKey);
170
        if (!$this->accessToken && $cacheItem->isHit()) {
171
            $this->accessToken = $cacheItem->get();
172
        }
173
174
        // Access token has expired, but expiration token has not expired.
175
        // Issue ourselves a new access token for the same video manager.
176
        if (!is_null($this->accessToken)
177
            && $this->accessToken->expired()
178
            && !$this->refreshToken->expired()) {
179
            $logger->info('Access token has expired - getting new one for same video manager with refresh token');
180
            $tokenData = $this->createAccessTokenFromRefreshToken($this->refreshToken);
181
            $this->accessToken = $tokenData['accessToken'];
182
        } elseif (is_null($this->accessToken)
183
            || (!is_null($this->refreshToken) && $this->refreshToken->expired())) {
184
            // Either we have no token, or the refresh token has expired
185
            // so we will need to generate completely new tokens
186
            $logger->info('No access token, or refresh token has expired - generate completely new ones');
187
            $tokenData = $this->createNewTokens();
188
189
            $this->accessToken = $tokenData['accessToken'];
190
            $this->refreshToken = $tokenData['refreshToken'];
191
        }
192
193
        $cacheItem->set($this->accessToken);
194
        $cacheItem->expiresAt((new \DateTime())
195
            ->setTimestamp($this->accessToken->getTokenData()['exp'])
196
            ->sub(new \DateInterval('PT30S'))
197
        );
198
        $this->cacheItemPool->save($cacheItem);
199
200
        return $this->accessToken->getTokenString();
201
    }
202
203
    /**
204
     * Sends a post request to the OAuth endpoint
205
     * Supports both guzzle 5 and 6 versions.
206
     *
207
     * @param array $body
208
     *
209
     * @return mixed
210
     */
211
    private function sendPostRequest(array $body)
212
    {
213
        $requestBodyKey = version_compare(ClientInterface::VERSION, '6.0', '>=') ? 'form_params' : 'body';
214
        $response = $this->httpClient->post('', [
215
            $requestBodyKey => $body,
216
        ]);
217
218
        return \json_decode($response->getBody(), true);
219
    }
220
}
221