Passed
Pull Request — develop (#890)
by Tito
03:54
created

RWebservicesBase::generateToken()   F

Complexity

Conditions 27
Paths 9793

Size

Total Lines 114
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 59
c 1
b 0
f 0
dl 0
loc 114
rs 0
cc 27
nc 9793
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @package     Redcore
4
 * @subpackage  Factory
5
 *
6
 * @copyright   Copyright (C) 2008 - 2020 redWEB.dk. All rights reserved.
7
 * @license     GNU General Public License version 2 or later, see LICENSE.
8
 */
9
10
defined('JPATH_REDCORE') or die;
11
12
use Joomla\Registry\Registry;
13
14
/**
15
 * Class for interacting with webservices.
16
 *
17
 * @package     Redcore
18
 * @subpackage  webservices
19
 * @since       1.8
20
 */
21
class RWebservicesBase
22
{
23
	/**
24
	 * @var    Registry  Options for the RWebservicesBase object.
25
	 * @since  1.8
26
	 */
27
	protected $options;
28
29
	/**
30
	 * @var    JHttp  The HTTP client object to use in sending HTTP requests.
31
	 * @since  1.8
32
	 */
33
	protected $http;
34
35
	/**
36
	 * @var    JInput  The input object to use in retrieving GET/POST data.
37
	 * @since  1.8
38
	 */
39
	protected $input;
40
41
	/**
42
	 * @var    JApplicationWeb  The application object to send HTTP headers for redirects.
43
	 * @since  1.8
44
	 */
45
	protected $application;
46
47
	/**
48
	 * @var    string  Authentication URL
49
	 */
50
	protected $token;
51
52
	/**
53
	 * Constructor.
54
	 *
55
	 * @param   Registry         $options      RWebservicesBase options object
56
	 * @param   JHttp            $http         The HTTP client object
57
	 * @param   JInput           $input        The input object
58
	 * @param   JApplicationWeb  $application  The application object
59
	 *
60
	 * @since   1.8
61
	 */
62
	public function __construct($options = null, $http = null, $input = null, $application = null)
63
	{
64
		$this->options = isset($options) ? $options : new Registry;
65
		$this->http = isset($http) ? $http : new JHttp($this->options);
66
		$this->application = isset($application) ? $application : new JApplicationWeb;
67
		$this->input = isset($input) ? $input : $this->application->input;
68
	}
69
70
	/**
71
	 * Gets already existing token array or generates new one
72
	 *
73
	 * @return array  Token array
74
	 */
75
	public function getToken()
76
	{
77
		$recreate = false;
78
79
		// If token key is already defined, we dont have to generate a new one
80
		if ($this->getOption('accesstoken'))
81
		{
82
			return $this->getOption('accesstoken');
83
		}
84
		// If there is no token we will fetch it we have access point for authentication
85
		elseif (!$this->token)
86
		{
87
			$recreate = true;
88
		}
89
		// We check if the token is expired
90
		elseif ($this->token)
91
		{
92
			// Different values of token expires
93
			if (array_key_exists('expires_in', $this->token) && array_key_exists('created', $this->token))
0 ignored issues
show
Bug introduced by
$this->token of type string is incompatible with the type array expected by parameter $search of array_key_exists(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

93
			if (array_key_exists('expires_in', /** @scrutinizer ignore-type */ $this->token) && array_key_exists('created', $this->token))
Loading history...
94
			{
95
				if ($this->token['created'] + $this->token['expires_in'] < time() + 20)
96
				{
97
					$recreate = true;
98
				}
99
			}
100
			elseif (isset($this->token['expireTimeFormatted']))
101
			{
102
				if ($this->token['expireTimeFormatted'] <= date('Y-m-d H:i:s'))
103
				{
104
					$recreate = true;
105
				}
106
			}
107
			elseif (isset($this->token['expires']))
108
			{
109
				if ($this->token['expires'] <= date('Y-m-d H:i:s'))
110
				{
111
					$recreate = true;
112
				}
113
			}
114
		}
115
116
		if ($recreate)
117
		{
118
			$this->token = $this->generateToken();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->generateToken() of type array is incompatible with the declared type string of property $token.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

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

Loading history...
119
		}
120
121
		return $this->token;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->token returns the type string which is incompatible with the documented return type array.
Loading history...
122
	}
123
124
	/**
125
	 * Generate the access token
126
	 *
127
	 * @return  array|boolean  The access token
128
	 *
129
	 * @since   1.8
130
	 * @throws  Exception
131
	 * @throws  RuntimeException
132
	 * @throws  InvalidArgumentException
133
	 */
134
	public function generateToken()
135
	{
136
		if ($this->token && array_key_exists('expires_in', $this->token) && array_key_exists('created', $this->token)
0 ignored issues
show
Bug introduced by
$this->token of type string is incompatible with the type array expected by parameter $search of array_key_exists(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

136
		if ($this->token && array_key_exists('expires_in', /** @scrutinizer ignore-type */ $this->token) && array_key_exists('created', $this->token)
Loading history...
137
			&& $this->token['created'] + $this->token['expires_in'] < time() + 20)
138
		{
139
			if ($this->getOption('authorization.userefresh', true))
140
			{
141
				$token = $this->refreshToken($this->token['refresh_token']);
142
143
				if ($token)
144
				{
145
					$this->token = $token;
0 ignored issues
show
Documentation Bug introduced by
It seems like $token of type array is incompatible with the declared type string of property $token.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

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

Loading history...
146
147
					return $this->token;
148
				}
149
			}
150
		}
151
152
		// It can be client, password, refresh_token, ...
153
		$data = array();
154
		$data['grant_type'] = $this->getOption('authorization.granttype', 'client_credentials');
155
		$data['client_id'] = $this->getOption('authorization.clientid');
156
		$data['client_secret'] = $this->getOption('authorization.clientsecret');
157
		$url = $this->getOption('authorization.tokenurl', $this->getOption('authorization.authurl'));
158
		$method = $this->getOption('authorization.authmethod', 'post');
159
160
		if ($this->getOption('authorization.redirecturi'))
161
		{
162
			$data['redirect_uri'] = $this->getOption('authorization.redirecturi');
163
		}
164
165
		if ($this->getOption('authorization.scope'))
166
		{
167
			$scope = is_array($this->getOption('authorization.scope')) ?
168
				implode(' ', $this->getOption('authorization.scope')) : $this->getOption('authorization.scope');
169
			$data['scope'] = $scope;
170
		}
171
172
		if ($this->getOption('authorization.state'))
173
		{
174
			$data['state'] = $this->getOption('authorization.state');
175
		}
176
177
		if ($this->getOption('authorization.username'))
178
		{
179
			$data['username'] = $this->getOption('authorization.username');
180
		}
181
182
		if ($this->getOption('authorization.password'))
183
		{
184
			$data['password'] = $this->getOption('authorization.password');
185
		}
186
187
		if ($this->getOption('authorization.requestparams'))
188
		{
189
			$requestParams = (array) $this->getOption('authorization.requestparams');
190
191
			foreach ($requestParams as $key => $value)
192
			{
193
				$data[$key] = $value;
194
			}
195
		}
196
197
		switch ($method)
198
		{
199
			case 'head':
200
			case 'get':
201
			case 'delete':
202
			case 'trace':
203
204
				if (strpos($url, '?'))
205
				{
206
					$url .= '&';
207
				}
208
				else
209
				{
210
					$url .= '?1=1';
211
				}
212
213
				foreach ($data as $key => $value)
214
				{
215
					$url .= '&' . $key . '=' . urlencode($value);
216
				}
217
218
				$response = $this->http->{$method}($url);
219
				break;
220
			case 'post':
221
			case 'put':
222
			case 'patch':
223
				$response = $this->http->{$method}($url, $data);
224
				break;
225
			default:
226
				throw new InvalidArgumentException('Unknown HTTP request method: ' . $method . '.');
227
		}
228
229
		if ($response->code >= 200 || $response->code < 400)
230
		{
231
			if ($response->headers['Content-Type'] == 'application/json')
232
			{
233
				$token = array_merge(json_decode($response->body, true), array('created' => time()));
234
			}
235
			else
236
			{
237
				parse_str($response->body, $token);
238
				$token = array_merge($token, array('created' => time()));
239
			}
240
241
			$this->setToken($token);
0 ignored issues
show
Bug introduced by
$token of type array is incompatible with the type string expected by parameter $token of RWebservicesBase::setToken(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

241
			$this->setToken(/** @scrutinizer ignore-type */ $token);
Loading history...
242
243
			return $this->token;
244
		}
245
		else
246
		{
247
			throw new Exception('Error code ' . $response->code . ' received refreshing token: ' . $response->body . '.');
248
		}
249
	}
250
251
	/**
252
	 * Refresh the access token instance.
253
	 *
254
	 * @param   string  $token  The refresh token
255
	 *
256
	 * @return  array|boolean  The new access token or false if failed
257
	 *
258
	 * @since   1.8
259
	 * @throws  Exception
260
	 */
261
	public function refreshToken($token = null)
262
	{
263
		if (!$this->getOption('authorization.userefresh', true))
264
		{
265
			return false;
266
		}
267
268
		$data['grant_type'] = 'refresh_token';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Loading history...
269
		$data['refresh_token'] = $token;
270
		$data['client_id'] = $this->getOption('authorization.clientid');
271
		$data['client_secret'] = $this->getOption('authorization.clientsecret');
272
		$url = $this->getOption('authorization.tokenurl', $this->getOption('authorization.authurl'));
273
		$response = $this->http->post($url, $data);
274
275
		if ($response->code >= 200 || $response->code < 400)
276
		{
277
			if ($response->headers['Content-Type'] == 'application/json')
278
			{
279
				$token = array_merge(json_decode($response->body, true), array('created' => time()));
280
			}
281
			else
282
			{
283
				parse_str($response->body, $token);
0 ignored issues
show
Bug introduced by
It seems like $token can also be of type string; however, parameter $arr of parse_str() does only seem to accept array|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

283
				parse_str($response->body, /** @scrutinizer ignore-type */ $token);
Loading history...
284
				$token = array_merge($token, array('created' => time()));
285
			}
286
287
			$this->setToken($token);
0 ignored issues
show
Bug introduced by
$token of type array is incompatible with the type string expected by parameter $token of RWebservicesBase::setToken(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

287
			$this->setToken(/** @scrutinizer ignore-type */ $token);
Loading history...
288
289
			return $this->token;
290
		}
291
		else
292
		{
293
			throw new Exception('Error code ' . $response->code . ' received refreshing token: ' . $response->body . '.');
294
		}
295
	}
296
297
	/**
298
	 * Sets token parameter
299
	 *
300
	 * @param   string  $token  Token
301
	 *
302
	 * @return  void
303
	 */
304
	public function setToken($token)
305
	{
306
		$this->token = $token;
307
	}
308
309
	/**
310
	 * Verify if the client has been authenticated
311
	 *
312
	 * @return  boolean  Is authenticated
313
	 *
314
	 * @since   1.8
315
	 */
316
	public function isAuthenticated()
317
	{
318
		$token = $this->getToken();
319
320
		if (!$token || !array_key_exists('access_token', $token))
0 ignored issues
show
Bug Best Practice introduced by
The expression $token 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...
321
		{
322
			return false;
323
		}
324
		elseif (array_key_exists('expires_in', $token) && $token['created'] + $token['expires_in'] < time() + 20)
325
		{
326
			return false;
327
		}
328
		else
329
		{
330
			return true;
331
		}
332
	}
333
334
	/**
335
	 * Get an option from the RWebservicesBase instance.
336
	 *
337
	 * @param   string  $key      The name of the option to get
338
	 * @param   mixed   $default  Default value
339
	 *
340
	 * @return  mixed  The option value
341
	 *
342
	 * @since   1.8
343
	 */
344
	public function getOption($key, $default = null)
345
	{
346
		return $this->options->get($key, $default);
347
	}
348
349
	/**
350
	 * Set an option for the RWebservicesBase instance.
351
	 *
352
	 * @param   string  $key    The name of the option to set
353
	 * @param   mixed   $value  The option value to set
354
	 *
355
	 * @return  RWebservicesBase  This object for method chaining
356
	 *
357
	 * @since   1.8
358
	 */
359
	public function setOption($key, $value)
360
	{
361
		$this->options->set($key, $value);
362
363
		return $this;
364
	}
365
}
366