Completed
Branch 3.x (e636ad)
by Дмитрий
05:24
created

AbstractProvider   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 328
Duplicated Lines 0 %

Test Coverage

Coverage 50.93%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 105
c 3
b 1
f 0
dl 0
loc 328
ccs 55
cts 108
cp 0.5093
rs 10
wmc 26

15 Methods

Rating   Name   Duplication   Size   Complexity  
A getAccessTokenByRequestParameters() 0 10 2
A makeAuthUrl() 0 6 1
A getSignatureBaseString() 0 13 1
A __construct() 0 6 1
A parseAccessToken() 0 17 4
A getAccessToken() 0 17 1
A setConsumerToken() 0 3 1
A authorizationHeader() 0 12 1
A setScope() 0 3 1
A parseToken() 0 12 5
A getConsumerToken() 0 3 1
A prepareRequest() 0 21 2
A getScope() 0 3 1
A oauthRequest() 0 31 2
A requestAuthToken() 0 24 2
1
<?php
2
/**
3
 * SocialConnect project
4
 * @author: Patsura Dmitry https://github.com/ovr <[email protected]>
5
 */
6
declare(strict_types=1);
7
8
namespace SocialConnect\OAuth1;
9
10
use Psr\Http\Message\ResponseInterface;
11
use SocialConnect\Common\HttpStack;
12
use SocialConnect\OAuth1\Exception\UnknownAuthorization;
13
use SocialConnect\OAuth1\Signature\AbstractSignatureMethod;
14
use SocialConnect\Provider\AbstractBaseProvider;
15
use SocialConnect\Provider\AccessTokenInterface;
16
use SocialConnect\Provider\Consumer;
17
use SocialConnect\Provider\Exception\InvalidAccessToken;
18
use SocialConnect\Provider\Exception\InvalidResponse;
19
use SocialConnect\OAuth1\Exception\InvalidRequestToken;
20
use SocialConnect\OAuth1\Signature\MethodHMACSHA1;
21
use SocialConnect\Provider\Session\SessionInterface;
22
23
abstract class AbstractProvider extends AbstractBaseProvider
24
{
25
    /**
26
     * @var string
27
     */
28
    protected $oauth1Version = '1.0a';
29
30
    /**
31
     * @var string
32
     */
33
    protected $requestTokenMethod = 'POST';
34
35
    /**
36
     * @var Consumer
37
     */
38
    protected $consumer;
39
40
    /**
41
     * @var Token
42
     */
43
    protected $consumerToken;
44
45
    /**
46
     * @var array
47
     */
48
    protected $scope = [];
49
50
    /**
51
     * @var AbstractSignatureMethod
52
     */
53
    protected $signature;
54
55
    /**
56
     * {@inheritDoc}
57
     */
58 32
    public function __construct(HttpStack $httpStack, SessionInterface $session, array $parameters)
59
    {
60 32
        parent::__construct($httpStack, $session, $parameters);
61
62 32
        $this->consumerToken = new Token('', '');
63 32
        $this->signature = new MethodHMACSHA1();
64 32
    }
65
66
    /**
67
     * @return string
68
     */
69
    abstract public function getAuthorizeUri();
70
71
    /**
72
     * @return string
73
     */
74
    abstract public function getRequestTokenUri();
75
76
    /**
77
     * @return string
78
     */
79
    abstract public function getRequestTokenAccessUri();
80
81
    /**
82
     * @return Token
83
     * @throws InvalidRequestToken
84
     * @throws InvalidResponse
85
     * @throws \Psr\Http\Client\ClientExceptionInterface
86
     */
87
    protected function requestAuthToken()
88
    {
89
        $parameters = [];
90
91
        /**
92
         * OAuth Core 1.0 Revision A: oauth_callback: An absolute URL to which the Service Provider will redirect
93
         * the User back when the Obtaining User Authorization step is completed.
94
         *
95
         * http://oauth.net/core/1.0a/#auth_step1
96
         */
97
        if ('1.0a' === $this->oauth1Version) {
98
            $parameters['oauth_callback'] = $this->getRedirectUrl();
99
        }
100
101
        $response = $this->oauthRequest(
102
            $this->getRequestTokenUri(),
103
            $this->requestTokenMethod,
104
            $parameters
105
        );
106
107
        $token = $this->parseToken($response->getBody()->getContents());
108
        $this->session->set('oauth1_request_token', $token);
109
110
        return $token;
111
    }
112
113
    /**
114
     * Parse Token from response's $body
115
     *
116
     * @param string|boolean $body
117
     * @return Token
118
     * @throws InvalidRequestToken
119
     * @throws InvalidResponse
120
     */
121
    public function parseToken($body)
122
    {
123
        if (empty($body)) {
124
            throw new InvalidResponse('Provider response with empty body');
125
        }
126
127
        parse_str($body, $token);
128
        if (!is_array($token) || !isset($token['oauth_token']) || !isset($token['oauth_token_secret'])) {
129
            throw new InvalidRequestToken;
130
        }
131
132
        return new Token($token['oauth_token'], $token['oauth_token_secret']);
133
    }
134
135
    /**
136
     * @param string $method
137
     * @param string $uri
138
     * @param array $query
139
     * @return string
140
     */
141 5
    public function getSignatureBaseString(string $method, string $uri, array $query): string
142
    {
143
        $parts = [
144 5
            $method,
145 5
            $uri,
146 5
            Util::buildHttpQuery(
147 5
                $query
148
            )
149
        ];
150
151 5
        $parts = Util::urlencodeRFC3986($parts);
152
153 5
        return implode('&', $parts);
154
    }
155
156 5
    public function authorizationHeader(array $query)
157
    {
158 5
        ksort($query);
159
160 5
        $parameters = http_build_query(
161 5
            $query,
162 5
            '',
163 5
            ', ',
164 5
            PHP_QUERY_RFC3986
165
        );
166
167 5
        return "OAuth $parameters";
168
    }
169
170 4
    public function prepareRequest(string $method, string $uri, array &$headers, array &$query, AccessTokenInterface $accessToken = null): void
171
    {
172 4
        $headers['Accept'] = 'application/json';
173
174 4
        $query['oauth_version'] = '1.0';
175 4
        $query['oauth_nonce'] = $this->generateState();
176 4
        $query['oauth_timestamp'] = time();
177 4
        $query['oauth_consumer_key'] = $this->consumer->getKey();
178
179 4
        if ($accessToken) {
180 3
            $query['oauth_token'] = $accessToken->getToken();
181
        }
182
183 4
        $query['oauth_signature_method'] = $this->signature->getName();
184 4
        $query['oauth_signature'] = $this->signature->buildSignature(
185 4
            $this->getSignatureBaseString($method, $uri, $query),
186 4
            $this->consumer,
187 4
            $this->consumerToken
188
        );
189
190 4
        $headers['Authorization'] = $this->authorizationHeader($query);
191 4
    }
192
193
    /**
194
     * @param string $uri
195
     * @param string $method
196
     * @param array $query
197
     * @param array|null $payload
198
     * @param array $headers
199
     * @return ResponseInterface
200
     * @throws InvalidResponse
201
     * @throws \Psr\Http\Client\ClientExceptionInterface
202
     * @throws \Exception
203
     */
204 1
    protected function oauthRequest($uri, $method = 'GET', array $query = [], array $payload = null, $headers = []): ResponseInterface
205
    {
206 1
        $headers = array_merge([
207 1
            'Accept' => 'application/json'
208
        ], $headers);
209
210 1
        if ($method === 'POST') {
211
            $headers['Content-Type'] = 'application/x-www-form-urlencoded';
212
        }
213
214 1
        $query['oauth_version'] = '1.0';
215 1
        $query['oauth_nonce'] = $this->generateState();
216 1
        $query['oauth_timestamp'] = time();
217 1
        $query['oauth_consumer_key'] = $this->consumer->getKey();
218
219 1
        $query['oauth_signature_method'] = $this->signature->getName();
220 1
        $query['oauth_signature'] = $this->signature->buildSignature(
221 1
            $this->getSignatureBaseString($method, $uri, $query),
222 1
            $this->consumer,
223 1
            $this->consumerToken
224
        );
225
226 1
        $headers['Authorization'] = $this->authorizationHeader($query);
227
228 1
        return $this->executeRequest(
229 1
            $this->createRequest(
230 1
                $method,
231
                $uri,
232
                $query,
233
                $headers,
234
                $payload
235
            )
236
        );
237
    }
238
239
    /**
240
     * {@inheritdoc}
241
     */
242
    public function makeAuthUrl(): string
243
    {
244
        $urlParameters = $this->getAuthUrlParameters();
245
        $urlParameters['oauth_token'] = $this->requestAuthToken()->getKey();
246
247
        return $this->getAuthorizeUri() . '?' . http_build_query($urlParameters, '', '&');
248
    }
249
250
    /**
251
     * @param array $parameters
252
     * @return AccessToken
253
     * @throws InvalidAccessToken
254
     * @throws InvalidResponse
255
     * @throws UnknownAuthorization
256
     */
257
    public function getAccessTokenByRequestParameters(array $parameters)
258
    {
259
        $token = $this->session->get('oauth1_request_token');
260
        if (!$token) {
261
            throw new UnknownAuthorization();
262
        }
263
264
        $this->session->delete('oauth1_request_token');
265
266
        return $this->getAccessToken($token, $parameters['oauth_verifier']);
267
    }
268
269
    /**
270
     * @param Token $token
271
     * @param string $oauthVerifier
272
     * @return AccessToken
273
     * @throws \Psr\Http\Client\ClientExceptionInterface
274
     */
275
    public function getAccessToken(Token $token, string $oauthVerifier)
276
    {
277
        $this->consumerToken = $token;
278
279
        $parameters = [
280
            'oauth_consumer_key' => $this->consumer->getKey(),
281
            'oauth_token' => $token->getKey(),
282
            'oauth_verifier' => $oauthVerifier
283
        ];
284
285
        $response = $this->oauthRequest(
286
            $this->getRequestTokenAccessUri(),
287
            $this->requestTokenMethod,
288
            $parameters
289
        );
290
291
        return $this->parseAccessToken($response->getBody()->getContents());
292
    }
293
294
    /**
295
     * Parse AccessToken from response's $body
296
     *
297
     * @param string $body
298
     * @return AccessToken
299
     * @throws InvalidAccessToken
300
     * @throws InvalidResponse
301
     */
302
    public function parseAccessToken(string $body)
303
    {
304
        if (empty($body)) {
305
            throw new InvalidResponse('Provider response with empty body');
306
        }
307
308
        parse_str($body, $token);
309
310
        if ($token) {
311
            if (!is_array($token)) {
312
                throw new InvalidAccessToken('Response must be array');
313
            }
314
315
            return new AccessToken($token);
316
        }
317
318
        throw new InvalidAccessToken('Server response with not valid/empty JSON');
319
    }
320
321
    /**
322
     * @return array
323
     */
324
    public function getScope()
325
    {
326
        return $this->scope;
327
    }
328
329
    /**
330
     * @param array $scope
331
     */
332 32
    public function setScope(array $scope)
333
    {
334 32
        $this->scope = $scope;
335 32
    }
336
337
    /**
338
     * @param Token $token
339
     */
340
    public function setConsumerToken(Token $token)
341
    {
342
        $this->consumerToken = $token;
343
    }
344
345
    /**
346
     * @return \SocialConnect\OAuth1\Token
347
     */
348
    public function getConsumerToken()
349
    {
350
        return $this->consumerToken;
351
    }
352
}
353