Xero::createClientCredentials()   B
last analyzed

Complexity

Conditions 6
Paths 9

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 9
nop 1
dl 0
loc 27
rs 8.8657
c 0
b 0
f 0
1
<?php
2
3
namespace Invoiced\OAuth1\Client\Server;
4
5
use Exception;
6
use GuzzleHttp\Client as GuzzleHttpClient;
7
use GuzzleHttp\Exception\BadResponseException;
8
use InvalidArgumentException;
9
use League\OAuth1\Client\Credentials\ClientCredentials;
10
use League\OAuth1\Client\Credentials\TokenCredentials;
11
use League\OAuth1\Client\Server\Server;
12
use League\OAuth1\Client\Signature\SignatureInterface;
13
14
class Xero extends Server
15
{
16
    /**
17
     * @var string
18
     */
19
    protected $responseType = 'xml';
20
21
    /**
22
     * @var array
23
     */
24
    protected $httpClientOptions = [];
25
26
    /**
27
     * @var array
28
     */
29
    protected $lastTokenCredentialsResponse;
30
31
    /**
32
     * @var array
33
     */
34
    protected $scope = [];
35
36
    /**
37
     * {@inheritdoc}
38
     */
39
    public function __construct($clientCredentials, SignatureInterface $signature = null)
40
    {
41
        if (is_array($clientCredentials)) {
42
            $this->parseConfiguration($clientCredentials);
43
44
            $clientCredentials = $this->createClientCredentials($clientCredentials);
45
46
            if (!$signature && $clientCredentials instanceof RsaClientCredentials) {
47
                $signature = new RsaSha1Signature($clientCredentials);
48
            }
49
        }
50
51
        parent::__construct($clientCredentials, $signature);
52
    }
53
54
    /**
55
     * Sets the value of the scope parameter used during authorization.
56
     *
57
     * @param array $scope Enumerated array where each element is a string
58
     *                     containing a single privilege value (e.g. 'payroll.employees')
59
     */
60
    public function setScope(array $scope)
61
    {
62
        $this->scope = $scope;
63
    }
64
65
    /**
66
     * Creates a Guzzle HTTP client for the given URL.
67
     *
68
     * @return GuzzleHttpClient
69
     */
70
    public function createHttpClient()
71
    {
72
        return new GuzzleHttpClient($this->httpClientOptions);
73
    }
74
75
    public function urlTemporaryCredentials()
76
    {
77
        return 'https://api.xero.com/oauth/RequestToken';
78
    }
79
80
    public function urlAuthorization()
81
    {
82
        return 'https://api.xero.com/oauth/Authorize'
83
            .$this->buildUrlAuthorizationQueryString();
84
    }
85
86
    /**
87
     * @return string
88
     */
89
    protected function buildUrlAuthorizationQueryString()
90
    {
91
        if (!$this->scope) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->scope of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
92
            return '';
93
        }
94
95
        return '?scope='.implode(',', $this->scope);
96
    }
97
98
    public function urlTokenCredentials()
99
    {
100
        return 'https://api.xero.com/oauth/AccessToken';
101
    }
102
103
    public function urlUserDetails()
104
    {
105
        return $this->notSupportedByXero();
106
    }
107
108
    public function userDetails($data, TokenCredentials $tokenCredentials)
109
    {
110
        return $this->notSupportedByXero();
111
    }
112
113
    public function userUid($data, TokenCredentials $tokenCredentials)
114
    {
115
        return $this->notSupportedByXero();
116
    }
117
118
    public function userEmail($data, TokenCredentials $tokenCredentials)
119
    {
120
        return $this->notSupportedByXero();
121
    }
122
123
    public function userScreenName($data, TokenCredentials $tokenCredentials)
124
    {
125
        return $this->notSupportedByXero();
126
    }
127
128
    /**
129
     * Gets the response of the last access token call. This might
130
     * be useful for partner applications to retrieve additional
131
     * OAuth parameters passed in by Xero.
132
     *
133
     * @return array|null
134
     */
135
    public function getLastTokenCredentialsResponse()
136
    {
137
        return $this->lastTokenCredentialsResponse;
138
    }
139
140
    /**
141
     * Refreshes an access token. Can be used by partner applications.
142
     *
143
     * @param TokenCredentials $tokenCredentials
144
     * @param string           $sessionHandle    Xero session handle
145
     *
146
     * @throws \League\OAuth1\Client\Credentials\CredentialsException when the access token cannot be refreshed
147
     *
148
     * @return TokenCredentials
149
     */
150
    public function refreshToken(TokenCredentials $tokenCredentials, $sessionHandle)
151
    {
152
        $client = $this->createHttpClient();
153
        $url = $this->urlTokenCredentials();
154
155
        $parameters = [
156
            'oauth_session_handle' => $sessionHandle,
157
        ];
158
159
        $headers = $this->getHeaders($tokenCredentials, 'POST', $url, $parameters);
160
161
        try {
162
            $response = $client->post($url, [
163
                'headers' => $headers,
164
                'form_params' => $parameters,
165
            ]);
166
        } catch (BadResponseException $e) {
167
            $this->handleTokenCredentialsBadResponse($e);
168
        }
169
170
        return $this->createTokenCredentials((string) $response->getBody());
171
    }
172
173
    protected function notSupportedByXero()
174
    {
175
        throw new Exception("Xero's API does not support retrieving the current user. Please see https://xero.uservoice.com/forums/5528-xero-accounting-api/suggestions/5688571-expose-which-user-connected-the-organization-via-o");
176
    }
177
178
    /**
179
     * Parse configuration array to set attributes.
180
     *
181
     * @param array $configuration
182
     */
183
    private function parseConfiguration(array $configuration = [])
184
    {
185
        $configToPropertyMap = [
186
            'http_client' => 'httpClientOptions',
187
        ];
188
        foreach ($configToPropertyMap as $config => $property) {
189
            if (isset($configuration[$config])) {
190
                $this->$property = $configuration[$config];
191
            }
192
        }
193
    }
194
195
    /**
196
     * Creates a client credentials instance from an array of credentials.
197
     *
198
     * @param array $clientCredentials
199
     *
200
     * @return ClientCredentials
201
     */
202
    protected function createClientCredentials(array $clientCredentials)
203
    {
204
        $keys = ['identifier', 'secret'];
205
206
        foreach ($keys as $key) {
207
            if (!isset($clientCredentials[$key])) {
208
                throw new InvalidArgumentException("Missing client credentials key [$key] from options.");
209
            }
210
        }
211
212
        if (isset($clientCredentials['rsa_private_key']) && isset($clientCredentials['rsa_public_key'])) {
213
            $_clientCredentials = new RsaClientCredentials();
214
            $_clientCredentials->setRsaPrivateKey($clientCredentials['rsa_private_key']);
215
            $_clientCredentials->setRsaPublicKey($clientCredentials['rsa_public_key']);
216
        } else {
217
            $_clientCredentials = new ClientCredentials();
218
        }
219
220
        $_clientCredentials->setIdentifier($clientCredentials['identifier']);
221
        $_clientCredentials->setSecret($clientCredentials['secret']);
222
223
        if (isset($clientCredentials['callback_uri'])) {
224
            $_clientCredentials->setCallbackUri($clientCredentials['callback_uri']);
225
        }
226
227
        return $_clientCredentials;
228
    }
229
230
    /**
231
     * Creates token credentials from the body response.
232
     *
233
     * @param string $body
234
     *
235
     * @return TokenCredentials
236
     */
237
    protected function createTokenCredentials($body)
238
    {
239
        parse_str($body, $data);
240
        $this->lastTokenCredentialsResponse = $data;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data can be null. However, the property $lastTokenCredentialsResponse is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
241
242
        return parent::createTokenCredentials($body);
243
    }
244
}
245