Provider::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 2
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * MercadoLibre Provider for OAuth 2.0 Client
4
 *
5
 * Licensed under The MIT License
6
 * For full copyright and license information, please see the LICENSE file
7
 * Redistributions of files must retain the above copyright notice.
8
 *
9
 * @copyright 2018 Lucas Banegas <[email protected]>
10
 * @license https://opensource.org/licenses/MIT MIT License
11
 * @author Lucas Banegas <[email protected]>
12
 * @link https://github.com/docta/oauth2-mercadolibre Repository
13
 * @link https://docta.github.io/oauth2-mercadolibre Documentation
14
 */
15
namespace Docta\MercadoLibre\OAuth2\Client;
16
17
use GuzzleHttp\Psr7\Uri;
18
use GuzzleHttp\Psr7\UriResolver;
19
use InvalidArgumentException;
20
use League\OAuth2\Client\Provider\AbstractProvider;
21
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
22
use League\OAuth2\Client\Token\AccessToken;
23
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
24
use Psr\Http\Message\RequestInterface;
25
use Psr\Http\Message\ResponseInterface;
26
27
/**
28
 * Represents a service provider.
29
 */
30
class Provider extends AbstractProvider
31
{
32
    use BearerAuthorizationTrait;
33
34
    /**
35
     * @var array
36
     */
37
    private $authSites = [
38
        'MLA' => 'https://auth.mercadolibre.com.ar',
39
        'MLB' => 'https://auth.mercadolivre.com.br',
40
        'MCO' => 'https://auth.mercadolibre.com.co',
41
        'MCR' => 'https://auth.mercadolibre.com.cr',
42
        'MEC' => 'https://auth.mercadolibre.com.ec',
43
        'MLC' => 'https://auth.mercadolibre.cl',
44
        'MLM' => 'https://auth.mercadolibre.com.mx',
45
        'MLU' => 'https://auth.mercadolibre.com.uy',
46
        'MLV' => 'https://auth.mercadolibre.com.ve',
47
        'MPA' => 'https://auth.mercadolibre.com.pa',
48
        'MPE' => 'https://auth.mercadolibre.com.pe',
49
        'MPT' => 'https://auth.mercadolibre.com.pt',
50
        'MRD' => 'https://auth.mercadolibre.com.do'
51
    ];
52
53
    /**
54
     * @var string
55
     */
56
    private $authSite;
57
58
    /**
59
     * @var string
60
     */
61
    protected $authUrl;
62
63
    /**
64
     * @var string
65
     */
66
    protected $apiUrl = 'https://api.mercadolibre.com';
67
68
    /**
69
     * @var string
70
     */
71
    protected $clientId;
72
73
    /**
74
     * @var string
75
     */
76
    protected $clientSecret;
77
78
    /**
79
     * @var string
80
     */
81
    protected $redirectUri;
82
83
    /**
84
     * Constructor.
85
     *
86
     * @param array $options
87
     * @param array $collaborators
88
     */
89
    public function __construct(array $options = [], array $collaborators = [])
90
    {
91
        // Check options
92
        $this->assertRequiredOptions($options);
93
        $this->assertAuthSite($options['authSite']);
94
        $this->authSite = $options['authSite'];
95
        $this->authUrl = $this->authSites[$this->authSite];
96
97
        // Build and filter options
98
        $required = $this->getRequiredOptions();
99
        $configured = array_intersect_key($options, array_flip($required));
100
101
        // Set options
102
        foreach ($configured as $key => $value) {
103
            $this->$key = $value;
104
        }
105
106
        // Apply the parent constructor
107
        $options = array_diff_key($options, $configured);
108
        parent::__construct($options, $collaborators);
109
    }
110
111
    /**
112
     * Returns all options that are required.
113
     *
114
     * @return array
115
     */
116
    protected function getRequiredOptions()
117
    {
118
        return [
119
            'authSite',
120
            'clientId',
121
            'clientSecret',
122
            'redirectUri'
123
        ];
124
    }
125
126
    /**
127
     * Verifies that all required options have been passed.
128
     *
129
     * @param array $options
130
     * @return void
131
     * @throws InvalidArgumentException
132
     */
133
    private function assertRequiredOptions(array $options)
134
    {
135
        $required = $this->getRequiredOptions();
136
        $missing = array_diff_key(array_flip($required), $options);
137
138
        if (!empty($missing)) {
139
            $missing = array_keys($missing);
140
            $missing = implode(', ', $missing);
141
            $template = 'Required options not defined: %s';
142
            $message = sprintf($template, $missing);
143
            throw new InvalidArgumentException($message);
144
        }
145
    }
146
147
    /**
148
     * Verifies that the `authSite` passed is valid.
149
     *
150
     * @param string $authSite
151
     * @return void
152
     * @throws InvalidArgumentException
153
     */
154
    private function assertAuthSite($authSite)
155
    {
156
        if (!array_key_exists($authSite, $this->authSites)) {
157
            $validValues = array_keys($this->authSites);
158
            $validValues = implode(', ', $validValues);
159
            $template = 'Valid values for authSite are only: %s';
160
            $message = sprintf($template, $validValues);
161
            throw new InvalidArgumentException($message);
162
        }
163
    }
164
165
    /**
166
     * Build and returns the URL for API requests.
167
     *
168
     * @param string $path
169
     * @param array $query
170
     * @return string
171
     */
172
    public function getApiUrl($path = '/', array $query = [])
173
    {
174
        $base = new Uri($this->apiUrl);
175
        $path = new Uri($path);
176
177
        foreach ($query as $key => $value) {
178
            $path = Uri::withQueryValue($path, $key, $value);
179
        }
180
181
        return (string) UriResolver::resolve($base, $path);
182
    }
183
184
    /**
185
     * Returns the base URL for authorizing a client.
186
     *
187
     * @return string
188
     */
189
    public function getBaseAuthorizationUrl()
190
    {
191
        $base = new Uri($this->authUrl);
192
        $path = new Uri('/authorization');
193
        return (string) UriResolver::resolve($base, $path);
194
        ;
195
    }
196
197
    /**
198
     * Returns the base URL for requesting an access token.
199
     *
200
     * @param array|null $params
201
     * @return string
202
     */
203
    public function getBaseAccessTokenUrl(array $params = null)
204
    {
205
        return $this->getApiUrl('/oauth/token');
206
    }
207
208
    /**
209
     * Returns the URL for requesting the resource owner's details.
210
     *
211
     * @param AccessToken|null $token
212
     * @return string
213
     */
214
    public function getResourceOwnerDetailsUrl(AccessToken $token = null)
215
    {
216
        return $this->getApiUrl('/users/me');
217
    }
218
219
    /**
220
     * Returns the default scopes used by this provider.
221
     *
222
     * @return null
223
     */
224
    public function getDefaultScopes()
225
    {
226
        return null;
227
    }
228
229
    /**
230
     * Returns the key used in the access token
231
     * response to identify the resource owner.
232
     *
233
     * @return string Resource owner identifier key
234
     */
235
    protected function getAccessTokenResourceOwnerId()
236
    {
237
        return 'user_id';
238
    }
239
240
    /**
241
     * Returns an authenticated PSR-7 request instance.
242
     *
243
     * @todo Use only authorization by HTTP header and remove the query parameter.
244
     *
245
     * @param string $method
246
     * @param string $url
247
     * @param AccessToken|string $token
248
     * @param array $options Any of `headers`, `body`, and `protocolVersion`.
249
     * @return RequestInterface
250
     */
251
    public function getAuthenticatedRequest($method, $url, $token, array $options = [])
252
    {
253
        $url = Uri::withQueryValue(new Uri($url), 'access_key', (string) $token);
254
        return $this->createRequest($method, (string) $url, $token, $options);
255
    }
256
257
258
    /**
259
     * Checks a provider response for errors.
260
     *
261
     * @throws IdentityProviderException
262
     * @param ResponseInterface $response
263
     * @param array|string $data Parsed response data
264
     * @return void
265
     */
266
    protected function checkResponse(ResponseInterface $response, $data)
267
    {
268
        if (isset($data['error'])) {
269
            throw new IdentityProviderException((string) $data['message'], (int) $data['status'], $data);
270
        }
271
    }
272
273
    /**
274
     * Generates a resource owner object from a
275
     * successful resource owner details request.
276
     *
277
     * @param array $response
278
     * @param AccessToken $token
279
     * @return ResourceOwner
280
     */
281
    protected function createResourceOwner(array $response, AccessToken $token)
282
    {
283
        return new ResourceOwner($response);
284
    }
285
}
286