GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( e2250a...6bb0fd )
by Lonnie
08:13
created

APIAuthentication::tryBasicAuthentication()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 37
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 37
rs 8.439
c 2
b 0
f 0
cc 6
eloc 18
nc 8
nop 0
1
<?php namespace Myth\Api\Auth;
2
/**
3
 * Sprint
4
 *
5
 * A set of power tools to enhance the CodeIgniter framework and provide consistent workflow.
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
 * THE SOFTWARE.
24
 *
25
 * @package     Sprint
26
 * @author      Lonnie Ezell
27
 * @copyright   Copyright 2014-2015, New Myth Media, LLC (http://newmythmedia.com)
28
 * @license     http://opensource.org/licenses/MIT  (MIT)
29
 * @link        http://sprintphp.com
30
 * @since       Version 1.0
31
 */
32
33
use Myth\Auth\LocalAuthentication;
34
use Myth\Events;
35
36
/**
37
 * Class APIAuthentication
38
 * 
39
 * @package Myth\Api\Auth
40
 */
41
class APIAuthentication extends LocalAuthentication {
42
43
	protected $logged_in = false;
44
45
	protected $realm = 'WallyWorld';
46
47
	//--------------------------------------------------------------------
48
49
	public function __construct($ci=null)
50
	{
51
		parent::__construct($ci);
52
53
		$this->ci->config->load('api');
54
		$this->ci->lang->load('api');
55
56
		// Has the IP address been blacklisted?
57
		if (config_item('auth.ip_blacklist_enabled'))
58
		{
59
			$this->checkIPBlacklist();
60
		}
61
62
		// Do we need to do whitelisting?
63
		if (config_item('auth.ip_whitelist_enabled'))
64
		{
65
			$this->checkIPWhitelist();
66
		}
67
	}
68
69
	//--------------------------------------------------------------------
70
71
	/**
72
	 * Sets the realm used by the authentication. The system truly only
73
	 * supports a single realm across the entire application, but this
74
	 * allows it to be set by the controller.
75
	 *
76
	 * @param $realm
77
	 *
78
	 * @return $this
79
	 */
80
	public function setRealm($realm)
81
	{
82
	    $this->realm = $realm;
83
		return $this;
84
	}
85
86
	//--------------------------------------------------------------------
87
88
	/**
89
	 * Checks to see if someone is authorized via HTTP Basic Authentication.
90
	 *
91
	 * @return bool
92
	 */
93
	public function tryBasicAuthentication()
94
	{
95
		$username = null;
96
		$password = null;
97
98
		// mod_php
99
		if ($this->ci->input->server('PHP_AUTH_USER')) {
100
			$username = $this->ci->input->server('PHP_AUTH_USER');
101
			$password = $this->ci->input->server('PHP_AUTH_PW');
102
		}
103
104
		// most other servers
105
		elseif ($this->ci->input->server('HTTP_AUTHENTICATION')) {
106
			if (strpos(strtolower($this->ci->input->server('HTTP_AUTHENTICATION')), 'basic') === 0) {
107
				list($username, $password) = explode(':', base64_decode(substr($this->ci->input->server('HTTP_AUTHORIZATION'), 6)));
108
			}
109
		}
110
111
		// If credentials weren't provided, we can't do anything
112
		// so request authorization by the client.
113
		if (empty($username) || empty($password))
114
		{
115
			$this->ci->output->set_header('WWW-Authenticate: Basic realm="'. config_item('api.realm') .'"');
116
			return false;
117
		}
118
119
		$data = [
120
			config_item('api.auth_field') => $username,
121
			'password'  => $password
122
		];
123
124
	    $user = $this->validate($data, true);
125
126
		$this->user = $user;
127
128
		return $user;
129
	}
130
131
	//--------------------------------------------------------------------
132
133
	/**
134
	 * Checks to see if someone is authorized via HTTP Digest Authentication.
135
	 *
136
	 * NOTE: This requires that a new field, 'digest_key', be added to the user's
137
	 * table and, during new user creation, or password reset, that the digest_key
138
	 * be calculated as md5({username}:{realm}:{password})
139
	 *
140
	 * References:
141
	 *  - http://www.faqs.org/rfcs/rfc2617.html
142
	 *  - http://www.sitepoint.com/understanding-http-digest-access-authentication/
143
	 */
144
	public function tryDigestAuthentication()
145
	{
146
		$digest_string = '';
147
148
		// We need to test which server authentication variable to use
149
		// because the PHP ISAPI module in IIS acts different from CGI
150
		if ($this->ci->input->server('PHP_AUTH_DIGEST'))
151
		{
152
			$digest_string = $this->ci->input->server('PHP_AUTH_DIGEST');
153
		}
154
		elseif ($this->ci->input->server('HTTP_AUTHORIZATION'))
155
		{
156
			$digest_string = $this->ci->input->server('HTTP_AUTHORIZATION');
157
		}
158
159
		$nonce = md5(uniqid());
160
		$opaque = md5(uniqid());
161
162
		// No digest string? Then you're done. Go home.
163 View Code Duplication
		if (empty($digest_string))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164
		{
165
			$this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) );
166
			return false;
167
		}
168
169
		// Grab the parts from the digest string.
170
		// They will be provided as an array of the parts: username, nonce, uri, nc, cnonce, qop, response
171
		$matches = [];
172
		preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches);
173
		$digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]);
174
175 View Code Duplication
		if (! array_key_exists('username', $digest))
176
		{
177
			$this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) );
178
			return false;
179
		}
180
181
		// Grab the user that corresponds to that "username"
182
		// exact field determined in the api config file - api.auth_field setting.
183
		$user = $this->user_model->as_array()->find_by( config_item('api.auth_field'), $digest['username'] );
184
		if (!  $user)
185
		{
186
			$this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) );          
187
			$this->ci->login_model->recordLoginAttempt($this->ci->input->ip_address());
188
			return false;
189
		}
190
191
		// Calc the correct response
192
		$A1 = $user['digest_key'];
193
194
		if ($digest['qop'] == 'auth')
195
		{
196
			$A2 = md5( strtoupper( $_SERVER['REQUEST_METHOD'] ) .':'. $digest['uri'] );
197
		} else {
198
			$body = file_get_contents('php://input');
199
			$A2 = md5( strtoupper( $_SERVER['REQUEST_METHOD'] ) .':'. $digest['uri'] .':'. md5($body) );
200
		}
201
		$valid_response = md5($A1 .':'. $digest['nonce'].':'. $digest['nc'] .':'. $digest['cnonce'] .':'. $digest['qop'] .':'. $A2);
202
203
		if ($digest['response'] != $valid_response)
204
		{
205
			$this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) );
206
			$this->ci->login_model->recordLoginAttempt($this->ci->input->ip_address(), $user['id']);
207
			return false;
208
		}
209
210
		$this->user = $user;
211
212
		return $user;
213
	}
214
215
	//--------------------------------------------------------------------
216
217
	/**
218
	 * Attempts to log a user into the API via the configured 'api.auth_type'
219
	 * config variable in config/api.php.
220
	 *
221
	 * NOTE: Since this is intended for API use, it is a STATELESS implementation
222
	 * and does not support remember me functionality.
223
	 *
224
	 * This basically replaces the login() method due to the way the AuthTrait
225
	 * works.
226
	 *
227
	 * @return bool
228
	 */
229
	public function viaRemember()
230
	{
231
		$user = false;
232
233
		switch (config_item('api.auth_type'))
234
		{
235
			case 'basic':
236
				$user = $this->tryBasicAuthentication();
237
				break;
238
			case 'digest':
239
				$user = $this->tryDigestAuthentication();
240
				break;
241
		}
242
243
		// If the user is throttled due to too many invalid logins
244
		// or the system is under attack, kick them back.
245
		// We need to test for this after validation because we
246
		// don't want it to affect a valid login.
247
248
		// If throttling time is above zero, we can't allow
249
		// logins now.
250
		if ($time = (int)$this->isThrottled($user) > 0)
251
		{
252
			$this->error = sprintf(lang('api.throttled'), $time);
253
			return false;
254
		}
255
256
		if (! $user)
257
		{
258
			$this->user = null;
259
			return $user;
260
		}
261
262
		$this->loginUser($user);
263
264
		Events::trigger('didLogin', [$user]);
265
266
		return true;
267
	}
268
269
	//--------------------------------------------------------------------
270
271
	//--------------------------------------------------------------------
272
	// Protected Methods
273
	//--------------------------------------------------------------------
274
275
	/**
276
	 * Checks the client's IP address against any IP addresses specified
277
	 * in the api config file. If any are found, the client is refused
278
	 * access immediately.
279
	 */
280 View Code Duplication
	public function checkIPBlacklist()
281
	{
282
	    $blacklist = explode(',', config_item('api.ip_blacklist'));
283
284
		array_walk($blacklist, function (&$item, $key) {
285
			$item = trim($item);
286
		});
287
288
		if (in_array($this->ci->input->ip_address(), $blacklist))
289
		{
290
			throw new \Exception( lang('api.ip_denied'), 401);
291
		}
292
293
		return true;
294
	}
295
	
296
	//--------------------------------------------------------------------
297
298
	/**
299
	 * Checks the client's IP address against any IP addresses specified
300
	 * in the api config file. If the client is not accessing the site
301
	 * from one of those addresses then their access is denied.
302
	 */
303 View Code Duplication
	public function checkIPWhitelist()
304
	{
305
		$whitelist = explode(',', config_item('api.ip_whitelist'));
306
307
		array_push($whitelist, '127.0.0.1', '0.0.0.0');
308
309
		array_walk($whitelist, function (&$item, $key) {
310
			$item = trim($item);
311
		});
312
313
		if (! in_array($this->ci->input->ip_address(), $whitelist))
314
		{
315
			throw new \Exception( lang('api.ip_denied'), 401);
316
		}
317
318
		return true;
319
	}
320
321
	//--------------------------------------------------------------------
322
323
	/**
324
	 * Handles the nitty gritty of actually logging our user into the system.
325
	 * Does NOT perform the authentication, just sets the system up so that
326
	 * it knows we're here.
327
	 *
328
	 * @param $user
329
	 */
330
	protected function loginUser($user)
331
	{
332
		// Save the user for later access
333
		$this->user = $user;
334
335
		// Clear our login attempts
336
		$this->ci->login_model->purgeLoginAttempts($user['email']);
337
338
		// We'll give a 20% chance to need to do a purge since we
339
		// don't need to purge THAT often, it's just a maintenance issue.
340
		// to keep the table from getting out of control.
341
		if (mt_rand(1, 100) < 20)
342
		{
343
			$this->ci->login_model->purgeOldRememberTokens();
344
		}
345
	}
346
347
	//--------------------------------------------------------------------
348
	
349
	//--------------------------------------------------------------------
350
	// UNUSED METHOD OVERRIDES
351
	//--------------------------------------------------------------------
352
353
	/**
354
	 * Attempt to log a user into the system.
355
	 *
356
	 * $credentials is an array of key/value pairs needed to log the user in.
357
	 * This is often email/password, or username/password.
358
	 *
359
	 * NOTE: Since this is intended for API use, it is a STATELESS implementation
360
	 * and does not support remember me functionality.
361
	 *
362
	 * Valid credentials:
363
	 *  - username
364
	 *  - email
365
	 *  - realm
366
	 *
367
	 * @param $credentials
368
	 * @param bool $remember
369
	 *
370
	 * @return bool|mixed|void
371
	 */
372
	public function login($credentials, $remember=false)
373
	{
374
		throw new \BadMethodCallException( lang('api.unused_method') );
375
	}
376
377
	//--------------------------------------------------------------------
378
379
	/**
380
	 * Logs a user out and removes all session information.
381
	 *
382
	 * NOTE: Since this is intended for API use, it is a STATELESS implementation
383
	 * and does not support remember me functionality.
384
	 *
385
	 * @return mixed
386
	 */
387
	public function logout()
388
	{
389
		throw new \BadMethodCallException( lang('api.unused_method') );
390
	}
391
392
	//--------------------------------------------------------------------
393
394
	/**
395
	 * Checks whether a user is logged in or not.
396
	 *
397
	 * @return bool
398
	 */
399
	public function isLoggedIn()
400
	{
401
		return $this->logged_in;
402
	}
403
404
	//--------------------------------------------------------------------
405
406
}
407