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
myth/Api/Auth/APIAuthentication.php 2 patches
Indentation   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -74,7 +74,7 @@  discard block
 block discarded – undo
74 74
 	 */
75 75
 	public function setRealm($realm)
76 76
 	{
77
-	    $this->realm = $realm;
77
+		$this->realm = $realm;
78 78
 		return $this;
79 79
 	}
80 80
 
@@ -116,7 +116,7 @@  discard block
 block discarded – undo
116 116
 			'password'  => $password
117 117
 		];
118 118
 
119
-	    $user = $this->validate($data, true);
119
+		$user = $this->validate($data, true);
120 120
 
121 121
 		$this->user = $user;
122 122
 
@@ -272,7 +272,7 @@  discard block
 block discarded – undo
272 272
 	 */
273 273
 	public function checkIPBlacklist()
274 274
 	{
275
-	    $blacklist = explode(',', config_item('api.ip_blacklist'));
275
+		$blacklist = explode(',', config_item('api.ip_blacklist'));
276 276
 
277 277
 		array_walk($blacklist, function (&$item, $key) {
278 278
 			$item = trim($item);
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -46,7 +46,7 @@  discard block
 block discarded – undo
46 46
 
47 47
 	//--------------------------------------------------------------------
48 48
 
49
-	public function __construct($ci=null)
49
+	public function __construct($ci = null)
50 50
 	{
51 51
 		parent::__construct($ci);
52 52
 
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
 		// so request authorization by the client.
113 113
 		if (empty($username) || empty($password))
114 114
 		{
115
-			$this->ci->output->set_header('WWW-Authenticate: Basic realm="'. config_item('api.realm') .'"');
115
+			$this->ci->output->set_header('WWW-Authenticate: Basic realm="'.config_item('api.realm').'"');
116 116
 			return false;
117 117
 		}
118 118
 
@@ -162,7 +162,7 @@  discard block
 block discarded – undo
162 162
 		// No digest string? Then you're done. Go home.
163 163
 		if (empty($digest_string))
164 164
 		{
165
-			$this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) );
165
+			$this->ci->output->set_header(sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque));
166 166
 			return false;
167 167
 		}
168 168
 
@@ -172,18 +172,18 @@  discard block
 block discarded – undo
172 172
 		preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches);
173 173
 		$digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]);
174 174
 
175
-		if (! array_key_exists('username', $digest))
175
+		if ( ! array_key_exists('username', $digest))
176 176
 		{
177
-			$this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) );
177
+			$this->ci->output->set_header(sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque));
178 178
 			return false;
179 179
 		}
180 180
 
181 181
 		// Grab the user that corresponds to that "username"
182 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)
183
+		$user = $this->user_model->as_array()->find_by(config_item('api.auth_field'), $digest['username']);
184
+		if ( ! $user)
185 185
 		{
186
-			$this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) );          
186
+			$this->ci->output->set_header(sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque));          
187 187
             $this->ci->login_model->recordLoginAttempt($_SERVER['REMOTE_ADDR']);
188 188
 			return false;
189 189
 		}
@@ -193,16 +193,16 @@  discard block
 block discarded – undo
193 193
 
194 194
 		if ($digest['qop'] == 'auth')
195 195
 		{
196
-			$A2 = md5( strtoupper( $_SERVER['REQUEST_METHOD'] ) .':'. $digest['uri'] );
196
+			$A2 = md5(strtoupper($_SERVER['REQUEST_METHOD']).':'.$digest['uri']);
197 197
 		} else {
198 198
 			$body = file_get_contents('php://input');
199
-			$A2 = md5( strtoupper( $_SERVER['REQUEST_METHOD'] ) .':'. $digest['uri'] .':'. md5($body) );
199
+			$A2 = md5(strtoupper($_SERVER['REQUEST_METHOD']).':'.$digest['uri'].':'.md5($body));
200 200
 		}
201
-		$valid_response = md5($A1 .':'. $digest['nonce'].':'. $digest['nc'] .':'. $digest['cnonce'] .':'. $digest['qop'] .':'. $A2);
201
+		$valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2);
202 202
 
203 203
 		if ($digest['response'] != $valid_response)
204 204
 		{
205
-			$this->ci->output->set_header( sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque) );
205
+			$this->ci->output->set_header(sprintf('WWW-Authenticate: Digest realm="%s", nonce="%s", opaque="%s"', config_item('api.realm'), $nonce, $opaque));
206 206
 			$this->ci->login_model->recordLoginAttempt($_SERVER['REMOTE_ADDR'], $user['id']);
207 207
 			return false;
208 208
 		}
@@ -247,13 +247,13 @@  discard block
 block discarded – undo
247 247
 
248 248
 		// If throttling time is above zero, we can't allow
249 249
 		// logins now.
250
-		if ($time = (int)$this->isThrottled($user) > 0)
250
+		if ($time = (int) $this->isThrottled($user) > 0)
251 251
 		{
252 252
 			$this->error = sprintf(lang('api.throttled'), $time);
253 253
 			return false;
254 254
 		}
255 255
 
256
-		if (! $user)
256
+		if ( ! $user)
257 257
 		{
258 258
 			$this->user = null;
259 259
 			return $user;
@@ -281,13 +281,13 @@  discard block
 block discarded – undo
281 281
 	{
282 282
 	    $blacklist = explode(',', config_item('api.ip_blacklist'));
283 283
 
284
-		array_walk($blacklist, function (&$item, $key) {
284
+		array_walk($blacklist, function(&$item, $key) {
285 285
 			$item = trim($item);
286 286
 		});
287 287
 
288 288
 		if (in_array($this->ci->input->ip_address(), $blacklist))
289 289
 		{
290
-			throw new \Exception( lang('api.ip_denied'), 401);
290
+			throw new \Exception(lang('api.ip_denied'), 401);
291 291
 		}
292 292
 
293 293
 		return true;
@@ -306,13 +306,13 @@  discard block
 block discarded – undo
306 306
 
307 307
 		array_push($whitelist, '127.0.0.1', '0.0.0.0');
308 308
 
309
-		array_walk($whitelist, function (&$item, $key) {
309
+		array_walk($whitelist, function(&$item, $key) {
310 310
 			$item = trim($item);
311 311
 		});
312 312
 
313
-		if (! in_array($this->ci->input->ip_address(), $whitelist))
313
+		if ( ! in_array($this->ci->input->ip_address(), $whitelist))
314 314
 		{
315
-			throw new \Exception( lang('api.ip_denied'), 401);
315
+			throw new \Exception(lang('api.ip_denied'), 401);
316 316
 		}
317 317
 
318 318
 		return true;
@@ -369,9 +369,9 @@  discard block
 block discarded – undo
369 369
 	 *
370 370
 	 * @return bool|mixed|void
371 371
 	 */
372
-	public function login($credentials, $remember=false)
372
+	public function login($credentials, $remember = false)
373 373
 	{
374
-		throw new \BadMethodCallException( lang('api.unused_method') );
374
+		throw new \BadMethodCallException(lang('api.unused_method'));
375 375
 	}
376 376
 
377 377
 	//--------------------------------------------------------------------
@@ -386,7 +386,7 @@  discard block
 block discarded – undo
386 386
 	 */
387 387
 	public function logout()
388 388
 	{
389
-		throw new \BadMethodCallException( lang('api.unused_method') );
389
+		throw new \BadMethodCallException(lang('api.unused_method'));
390 390
 	}
391 391
 
392 392
 	//--------------------------------------------------------------------
Please login to merge, or discard this patch.
myth/Auth/LocalAuthentication.php 2 patches
Spacing   +59 added lines, -59 removed lines patch added patch discarded remove patch
@@ -62,21 +62,21 @@  discard block
 block discarded – undo
62 62
 
63 63
     //--------------------------------------------------------------------
64 64
 
65
-    public function __construct( $ci=null )
65
+    public function __construct($ci = null)
66 66
     {
67 67
         if ($ci)
68 68
         {
69
-            $this->ci= $ci;
69
+            $this->ci = $ci;
70 70
         }
71 71
         else
72 72
         {
73
-            $this->ci =& get_instance();
73
+            $this->ci = & get_instance();
74 74
         }
75 75
 
76 76
         // Get our compatibility password file loaded up.
77
-        if (! function_exists('password_hash'))
77
+        if ( ! function_exists('password_hash'))
78 78
         {
79
-            require_once dirname(__FILE__) .'password.php';
79
+            require_once dirname(__FILE__).'password.php';
80 80
         }
81 81
 
82 82
         if (empty($this->ci->session))
@@ -101,7 +101,7 @@  discard block
 block discarded – undo
101 101
      * @param bool  $remember
102 102
      * @return bool|mixed
103 103
      */
104
-    public function login($credentials, $remember=false)
104
+    public function login($credentials, $remember = false)
105 105
     {
106 106
         $user = $this->validate($credentials, true);
107 107
 
@@ -112,14 +112,14 @@  discard block
 block discarded – undo
112 112
 
113 113
         // If throttling time is above zero, we can't allow
114 114
         // logins now.
115
-        $time = (int)$this->isThrottled($user);
115
+        $time = (int) $this->isThrottled($user);
116 116
         if ($time > 0)
117 117
         {
118 118
             $this->error = sprintf(lang('auth.throttled'), $time);
119 119
             return false;
120 120
         }
121 121
 
122
-        if (! $user)
122
+        if ( ! $user)
123 123
         {
124 124
         	if (empty($this->error))
125 125
             {
@@ -154,14 +154,14 @@  discard block
 block discarded – undo
154 154
      * @param bool $return_user
155 155
      * @return mixed
156 156
      */
157
-    public function validate($credentials, $return_user=false)
157
+    public function validate($credentials, $return_user = false)
158 158
     {
159 159
         // Get ip address
160 160
         $ip_address = $this->ci->input->server('REMOTE_ADDR');
161 161
 
162 162
         // We do not want to force case-sensitivity on things
163 163
         // like username and email for usability sake.
164
-        if (! empty($credentials['email']))
164
+        if ( ! empty($credentials['email']))
165 165
         {
166 166
             $credentials['email'] = strtolower($credentials['email']);
167 167
         }
@@ -186,7 +186,7 @@  discard block
 block discarded – undo
186 186
         }
187 187
 
188 188
         // Ensure that the fields are allowed validation fields
189
-        if (! in_array(key($credentials), config_item('auth.valid_fields')) )
189
+        if ( ! in_array(key($credentials), config_item('auth.valid_fields')))
190 190
         {
191 191
             $this->error = lang('auth.invalid_credentials');
192 192
             $this->ci->login_model->recordLoginAttempt($ip_address);
@@ -198,7 +198,7 @@  discard block
 block discarded – undo
198 198
                                  ->where($credentials)
199 199
                                  ->first();
200 200
 
201
-        if (! $user)
201
+        if ( ! $user)
202 202
         {
203 203
             $this->error = lang('auth.invalid_user');
204 204
             $this->ci->login_model->recordLoginAttempt($ip_address);
@@ -206,9 +206,9 @@  discard block
 block discarded – undo
206 206
         }
207 207
 
208 208
         // Now, try matching the passwords.
209
-        $result =  password_verify($password, $user['password_hash']);
209
+        $result = password_verify($password, $user['password_hash']);
210 210
 
211
-        if (! $result)
211
+        if ( ! $result)
212 212
         {
213 213
             $this->error = lang('auth.invalid_password');
214 214
             $this->ci->login_model->recordLoginAttempt($ip_address, $user['id']);
@@ -219,7 +219,7 @@  discard block
 block discarded – undo
219 219
         // This would be due to the hash algorithm or hash
220 220
         // cost changing since the last time that a user
221 221
         // logged in.
222
-        if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT, ['cost' => config_item('auth.hash_cost')] ))
222
+        if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT, ['cost' => config_item('auth.hash_cost')]))
223 223
         {
224 224
             $new_hash = Password::hashPassword($password);
225 225
             $this->user_model->skip_validation()
@@ -228,7 +228,7 @@  discard block
 block discarded – undo
228 228
         }
229 229
 
230 230
         // Is the user active?
231
-        if (! $user['active'])
231
+        if ( ! $user['active'])
232 232
         {
233 233
             $this->error = lang('auth.inactive_account');
234 234
             return false;
@@ -248,7 +248,7 @@  discard block
 block discarded – undo
248 248
     {
249 249
         $this->ci->load->helper('cookie');
250 250
 
251
-        if (! Events::trigger('beforeLogout', [$this->user]))
251
+        if ( ! Events::trigger('beforeLogout', [$this->user]))
252 252
         {
253 253
             return false;
254 254
         }
@@ -257,10 +257,10 @@  discard block
 block discarded – undo
257 257
         // available for flash messages, etc.
258 258
         if (isset($_SESSION))
259 259
         {
260
-            foreach ( $_SESSION as $key => $value )
260
+            foreach ($_SESSION as $key => $value)
261 261
             {
262
-                $_SESSION[ $key ] = NULL;
263
-                unset( $_SESSION[ $key ] );
262
+                $_SESSION[$key] = NULL;
263
+                unset($_SESSION[$key]);
264 264
             }
265 265
         }
266 266
         // Also, regenerate the session ID for a touch of added safety.
@@ -286,7 +286,7 @@  discard block
 block discarded – undo
286 286
     {
287 287
         $id = $this->ci->session->userdata('logged_in');
288 288
 
289
-        if (! $id)
289
+        if ( ! $id)
290 290
         {
291 291
             return false;
292 292
         }
@@ -294,10 +294,10 @@  discard block
 block discarded – undo
294 294
         // If the user var hasn't been filled in, we need to fill it in,
295 295
         // since this method will typically be used as the only method
296 296
         // to determine whether a user is logged in or not.
297
-        if (! $this->user)
297
+        if ( ! $this->user)
298 298
         {
299 299
             $this->user = $this->user_model->as_array()
300
-                                           ->find_by('id', (int)$id);
300
+                                           ->find_by('id', (int) $id);
301 301
 
302 302
             if (empty($this->user))
303 303
             {
@@ -321,14 +321,14 @@  discard block
 block discarded – undo
321 321
      */
322 322
     public function viaRemember()
323 323
     {
324
-        if (! config_item('auth.allow_remembering'))
324
+        if ( ! config_item('auth.allow_remembering'))
325 325
         {
326 326
             return false;
327 327
         }
328 328
 
329 329
         $this->ci->load->helper('cookie');
330 330
 
331
-        if (! $token = get_cookie('remember'))
331
+        if ( ! $token = get_cookie('remember'))
332 332
         {
333 333
             return false;
334 334
         }
@@ -337,7 +337,7 @@  discard block
 block discarded – undo
337 337
         $query = $this->ci->db->where('hash', $this->ci->login_model->hashRememberToken($token))
338 338
                               ->get('auth_tokens');
339 339
 
340
-        if (! $query->num_rows())
340
+        if ( ! $query->num_rows())
341 341
         {
342 342
             return false;
343 343
         }
@@ -375,16 +375,16 @@  discard block
 block discarded – undo
375 375
         // If via email, we need to generate a hash
376 376
         $this->ci->load->helper('string');
377 377
         $token = random_string('alnum', 24);
378
-        $user_data['activate_hash'] = hash('sha1', config_item('auth.salt') . $token);
378
+        $user_data['activate_hash'] = hash('sha1', config_item('auth.salt').$token);
379 379
 
380 380
         // Email should NOT be case sensitive.
381
-        if (! empty($user_data['email']))
381
+        if ( ! empty($user_data['email']))
382 382
         {
383 383
             $user_data['email'] = strtolower($user_data['email']);
384 384
         }
385 385
 
386 386
         // Save the user
387
-        if (! $id = $this->user_model->insert($user_data))
387
+        if ( ! $id = $this->user_model->insert($user_data))
388 388
         {
389 389
             $this->error = $this->user_model->error();
390 390
             return false;
@@ -415,25 +415,25 @@  discard block
 block discarded – undo
415 415
     {
416 416
         $post = [
417 417
             'email'         => $data['email'],
418
-            'activate_hash' => hash('sha1', config_item('auth.salt') . $data['code'])
418
+            'activate_hash' => hash('sha1', config_item('auth.salt').$data['code'])
419 419
         ];
420 420
 
421 421
         $user = $this->user_model->where($post)
422 422
                                  ->first();
423 423
 
424
-        if (! $user) {
424
+        if ( ! $user) {
425 425
             $this->error = $this->user_model->error() ? $this->user_model->error() : lang('auth.activate_no_user');
426 426
 
427 427
             return false;
428 428
         }
429 429
 
430
-        if (! $this->user_model->update($user->id, ['active' => 1, 'activate_hash' => null]))
430
+        if ( ! $this->user_model->update($user->id, ['active' => 1, 'activate_hash' => null]))
431 431
         {
432 432
             $this->error = $this->user_model->error();
433 433
             return false;
434 434
         }
435 435
 
436
-        Events::trigger('didActivate', [(array)$user]);
436
+        Events::trigger('didActivate', [(array) $user]);
437 437
 
438 438
         return true;
439 439
     }
@@ -448,7 +448,7 @@  discard block
 block discarded – undo
448 448
      */
449 449
     public function activateUserById($id)
450 450
     {
451
-        if (! $this->user_model->update($id, ['active' => 1, 'activate_hash' => null]))
451
+        if ( ! $this->user_model->update($id, ['active' => 1, 'activate_hash' => null]))
452 452
         {
453 453
             $this->error = $this->user_model->error();
454 454
             return false;
@@ -480,12 +480,12 @@  discard block
 block discarded – undo
480 480
      */
481 481
     public function id()
482 482
     {
483
-        if (! is_array($this->user) || empty($this->user['id']))
483
+        if ( ! is_array($this->user) || empty($this->user['id']))
484 484
         {
485 485
             return null;
486 486
         }
487 487
 
488
-        return (int)$this->user['id'];
488
+        return (int) $this->user['id'];
489 489
     }
490 490
 
491 491
     //--------------------------------------------------------------------
@@ -502,7 +502,7 @@  discard block
 block discarded – undo
502 502
     public function isThrottled($user)
503 503
     {
504 504
         // Not throttling? Get outta here!
505
-        if (! config_item('auth.allow_throttling'))
505
+        if ( ! config_item('auth.allow_throttling'))
506 506
         {
507 507
             return false;
508 508
         }
@@ -570,7 +570,7 @@  discard block
 block discarded – undo
570 570
         {
571 571
             $this->error = lang('auth.bruteBan_notice');
572 572
 
573
-            $ban_time = 60 * 15;    // 15 minutes
573
+            $ban_time = 60 * 15; // 15 minutes
574 574
             $_SESSION['bruteBan'] = time() + $ban_time;
575 575
             return $ban_time;
576 576
         }
@@ -618,7 +618,7 @@  discard block
 block discarded – undo
618 618
         // Is it a valid user?
619 619
         $user = $this->user_model->find_by('email', $email);
620 620
 
621
-        if (! $user)
621
+        if ( ! $user)
622 622
         {
623 623
             $this->error = lang('auth.invalid_email');
624 624
             return false;
@@ -627,17 +627,17 @@  discard block
 block discarded – undo
627 627
         // Generate/store our codes
628 628
         $this->ci->load->helper('string');
629 629
         $token = random_string('alnum', 24);
630
-        $hash = hash('sha1', config_item('auth.salt') .$token);
630
+        $hash = hash('sha1', config_item('auth.salt').$token);
631 631
 
632 632
         $result = $this->user_model->update($user->id, ['reset_hash' => $hash]);
633 633
 
634
-        if (! $result)
634
+        if ( ! $result)
635 635
         {
636 636
             $this->error = $this->user_model->error();
637 637
             return false;
638 638
         }
639 639
 
640
-        Events::trigger('didRemindUser', [(array)$user, $token]);
640
+        Events::trigger('didRemindUser', [(array) $user, $token]);
641 641
 
642 642
         return true;
643 643
     }
@@ -664,10 +664,10 @@  discard block
 block discarded – undo
664 664
         }
665 665
 
666 666
         // Generate a hash to match against the table.
667
-        $credentials['reset_hash'] = hash('sha1', config_item('auth.salt') .$credentials['code']);
667
+        $credentials['reset_hash'] = hash('sha1', config_item('auth.salt').$credentials['code']);
668 668
         unset($credentials['code']);
669 669
 
670
-        if (! empty($credentials['email']))
670
+        if ( ! empty($credentials['email']))
671 671
         {
672 672
             $credentials['email'] = strtolower($credentials['email']);
673 673
         }
@@ -675,7 +675,7 @@  discard block
 block discarded – undo
675 675
         // Is there a matching user?
676 676
         $user = $this->user_model->find_by($credentials);
677 677
 
678
-        if (! $user)
678
+        if ( ! $user)
679 679
         {
680 680
             $this->error = lang('auth.reset_no_user');
681 681
             return false;
@@ -688,13 +688,13 @@  discard block
 block discarded – undo
688 688
             'reset_hash'   => null
689 689
         ];
690 690
 
691
-        if (! $this->user_model->update($user->id, $data))
691
+        if ( ! $this->user_model->update($user->id, $data))
692 692
         {
693 693
             $this->error = $this->user_model->error();
694 694
             return false;
695 695
         }
696 696
 
697
-        Events::trigger('didResetPassword', [(array)$user]);
697
+        Events::trigger('didResetPassword', [(array) $user]);
698 698
 
699 699
         return true;
700 700
     }
@@ -710,7 +710,7 @@  discard block
 block discarded – undo
710 710
      * @param null $message
711 711
      * @return mixed
712 712
      */
713
-    public function changeStatus($newStatus, $message=null)
713
+    public function changeStatus($newStatus, $message = null)
714 714
     {
715 715
         // todo actually record new users status!
716 716
     }
@@ -727,14 +727,14 @@  discard block
 block discarded – undo
727 727
      * @param bool $allow_any_parent
728 728
      * @return mixed
729 729
      */
730
-    public function useModel($model, $allow_any_parent=false)
730
+    public function useModel($model, $allow_any_parent = false)
731 731
     {
732
-        if (! $allow_any_parent && get_parent_class($model) != 'Myth\Models\CIDbModel')
732
+        if ( ! $allow_any_parent && get_parent_class($model) != 'Myth\Models\CIDbModel')
733 733
         {
734 734
             throw new \RuntimeException('Models passed into LocalAuthenticate MUST extend Myth\Models\CIDbModel');
735 735
         }
736 736
 
737
-        $this->user_model =& $model;
737
+        $this->user_model = & $model;
738 738
 
739 739
         return $this;
740 740
     }
@@ -806,7 +806,7 @@  discard block
 block discarded – undo
806 806
      */
807 807
     protected function rememberUser($user)
808 808
     {
809
-        if (! config_item('auth.allow_remembering'))
809
+        if ( ! config_item('auth.allow_remembering'))
810 810
         {
811 811
             log_message('debug', 'Auth library set to refuse "Remember Me" functionality.');
812 812
             return false;
@@ -825,13 +825,13 @@  discard block
 block discarded – undo
825 825
      * @param null $token
826 826
      * @return mixed
827 827
      */
828
-    protected function refreshRememberCookie($user, $token=null)
828
+    protected function refreshRememberCookie($user, $token = null)
829 829
     {
830 830
         $this->ci->load->helper('cookie');
831 831
 
832 832
         // If a token is passed in, we know we're removing the
833 833
         // old one.
834
-        if (! empty($token))
834
+        if ( ! empty($token))
835 835
         {
836 836
             $this->invalidateRememberCookie($user['email'], $token);
837 837
         }
@@ -841,7 +841,7 @@  discard block
 block discarded – undo
841 841
         // Save the token to the database.
842 842
         $data = [
843 843
             'email'   => $user['email'],
844
-            'hash'    => sha1(config_item('auth.salt') . $new_token),
844
+            'hash'    => sha1(config_item('auth.salt').$new_token),
845 845
             'created' => date('Y-m-d H:i:s')
846 846
         ];
847 847
 
@@ -849,13 +849,13 @@  discard block
 block discarded – undo
849 849
 
850 850
         // Create the cookie
851 851
         set_cookie(
852
-            'remember',                             // Cookie Name
853
-            $new_token,                             // Value
854
-            config_item('auth.remember_length'),    // # Seconds until it expires
852
+            'remember', // Cookie Name
853
+            $new_token, // Value
854
+            config_item('auth.remember_length'), // # Seconds until it expires
855 855
             config_item('cookie_domain'),
856 856
             config_item('cookie_path'),
857 857
             config_item('cookie_prefix'),
858
-            false,                                  // Only send over HTTPS?
858
+            false, // Only send over HTTPS?
859 859
             true                                    // Hide from Javascript?
860 860
         );
861 861
 
Please login to merge, or discard this patch.
Indentation   +894 added lines, -894 removed lines patch added patch discarded remove patch
@@ -52,901 +52,901 @@
 block discarded – undo
52 52
  */
53 53
 class LocalAuthentication implements AuthenticateInterface {
54 54
 
55
-    protected $ci;
56
-
57
-    protected $user = null;
58
-
59
-    public $user_model = null;
60
-
61
-    public $error = null;
62
-
63
-    //--------------------------------------------------------------------
64
-
65
-    public function __construct( $ci=null )
66
-    {
67
-        if ($ci)
68
-        {
69
-            $this->ci= $ci;
70
-        }
71
-        else
72
-        {
73
-            $this->ci =& get_instance();
74
-        }
75
-
76
-        // Get our compatibility password file loaded up.
77
-        if (! function_exists('password_hash'))
78
-        {
79
-            require_once dirname(__FILE__) .'password.php';
80
-        }
81
-
82
-        if (empty($this->ci->session))
83
-        {
84
-            $this->ci->load->library('session');
85
-        }
86
-
87
-        $this->ci->config->load('auth');
88
-        $this->ci->load->model('auth/login_model');
89
-        $this->ci->load->language('auth/auth');
90
-    }
91
-
92
-    //--------------------------------------------------------------------
93
-
94
-    /**
95
-     * Attempt to log a user into the system.
96
-     *
97
-     * $credentials is an array of key/value pairs needed to log the user in.
98
-     * This is often email/password, or username/password.
99
-     *
100
-     * @param array $credentials
101
-     * @param bool  $remember
102
-     * @return bool|mixed
103
-     */
104
-    public function login($credentials, $remember=false)
105
-    {
106
-        $user = $this->validate($credentials, true);
107
-
108
-        // If the user is throttled due to too many invalid logins
109
-        // or the system is under attack, kick them back.
110
-        // We need to test for this after validation because we
111
-        // don't want it to affect a valid login.
112
-
113
-        // If throttling time is above zero, we can't allow
114
-        // logins now.
115
-        $time = (int)$this->isThrottled($user);
116
-        if ($time > 0)
117
-        {
118
-            $this->error = sprintf(lang('auth.throttled'), $time);
119
-            return false;
120
-        }
121
-
122
-        if (! $user)
123
-        {
124
-            if (empty($this->error))
125
-            {
126
-                // We need to set an error if there is no one
127
-                $this->error = lang('auth.invalid_user');
128
-            }
129
-            $this->user = null;
130
-            return $user;
131
-        }       
132
-
133
-        $this->loginUser($user);
134
-
135
-        if ($remember)
136
-        {
137
-            $this->rememberUser($user);
138
-        }
139
-
140
-        Events::trigger('didLogin', [$user]);
141
-
142
-        return true;
143
-    }
144
-
145
-    //--------------------------------------------------------------------
146
-
147
-    /**
148
-     * Validates user login information without logging them in.
149
-     *
150
-     * $credentials is an array of key/value pairs needed to log the user in.
151
-     * This is often email/password, or username/password.
152
-     *
153
-     * @param $credentials
154
-     * @param bool $return_user
155
-     * @return mixed
156
-     */
157
-    public function validate($credentials, $return_user=false)
158
-    {
159
-        // Get ip address
160
-        $ip_address = $this->ci->input->ip_address();
161
-
162
-        // We do not want to force case-sensitivity on things
163
-        // like username and email for usability sake.
164
-        if (! empty($credentials['email']))
165
-        {
166
-            $credentials['email'] = strtolower($credentials['email']);
167
-        }
168
-
169
-        // Can't validate without a password.
170
-        if (empty($credentials['password']) || count($credentials) < 2)
171
-        {
172
-            $this->ci->login_model->recordLoginAttempt($ip_address);
173
-            return null;
174
-        }
175
-
176
-        $password = $credentials['password'];
177
-        unset($credentials['password']);
178
-
179
-        // We should only be allowed 1 single other credential to
180
-        // test against.
181
-        if (count($credentials) > 1)
182
-        {
183
-            $this->error = lang('auth.too_many_credentials');
184
-            $this->ci->login_model->recordLoginAttempt($ip_address);
185
-            return false;
186
-        }
187
-
188
-        // Ensure that the fields are allowed validation fields
189
-        if (! in_array(key($credentials), config_item('auth.valid_fields')) )
190
-        {
191
-            $this->error = lang('auth.invalid_credentials');
192
-            $this->ci->login_model->recordLoginAttempt($ip_address);
193
-            return false;
194
-        }
195
-
196
-        // Can we find a user with those credentials?
197
-        $user = $this->user_model->as_array()
198
-                                 ->where($credentials)
199
-                                 ->first();
200
-
201
-        if (! $user)
202
-        {
203
-            $this->error = lang('auth.invalid_user');
204
-            $this->ci->login_model->recordLoginAttempt($ip_address);
205
-            return false;
206
-        }
207
-
208
-        // Now, try matching the passwords.
209
-        $result =  password_verify($password, $user['password_hash']);
210
-
211
-        if (! $result)
212
-        {
213
-            $this->error = lang('auth.invalid_password');
214
-            $this->ci->login_model->recordLoginAttempt($ip_address, $user['id']);
215
-            return false;
216
-        }
217
-
218
-        // Check to see if the password needs to be rehashed.
219
-        // This would be due to the hash algorithm or hash
220
-        // cost changing since the last time that a user
221
-        // logged in.
222
-        if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT, ['cost' => config_item('auth.hash_cost')] ))
223
-        {
224
-            $new_hash = Password::hashPassword($password);
225
-            $this->user_model->skip_validation()
226
-                             ->update($user['id'], ['password_hash' => $new_hash]);
227
-            unset($new_hash);
228
-        }
229
-
230
-        // Is the user active?
231
-        if (! $user['active'])
232
-        {
233
-            $this->error = lang('auth.inactive_account');
234
-            return false;
235
-        }
236
-
237
-        return $return_user ? $user : true;
238
-    }
239
-
240
-    //--------------------------------------------------------------------
241
-
242
-    /**
243
-     * Logs a user out and removes all session information.
244
-     *
245
-     * @return mixed
246
-     */
247
-    public function logout()
248
-    {
249
-        $this->ci->load->helper('cookie');
250
-
251
-        if (! Events::trigger('beforeLogout', [$this->user]))
252
-        {
253
-            return false;
254
-        }
255
-
256
-        // Destroy the session data - but ensure a session is still
257
-        // available for flash messages, etc.
258
-        if (isset($_SESSION))
259
-        {
260
-            foreach ( $_SESSION as $key => $value )
261
-            {
262
-                $_SESSION[ $key ] = NULL;
263
-                unset( $_SESSION[ $key ] );
264
-            }
265
-        }
266
-        // Also, regenerate the session ID for a touch of added safety.
267
-        $this->ci->session->sess_regenerate(true);
268
-
269
-        // Take care of any rememberme functionality.
270
-        if (config_item('auth.allow_remembering'))
271
-        {
272
-            $token = get_cookie('remember');
273
-
274
-            $this->invalidateRememberCookie($this->user['email'], $token);
275
-        }
276
-    }
277
-
278
-    //--------------------------------------------------------------------
279
-
280
-    /**
281
-     * Checks whether a user is logged in or not.
282
-     *
283
-     * @return bool
284
-     */
285
-    public function isLoggedIn()
286
-    {
287
-        $id = $this->ci->session->userdata('logged_in');
288
-
289
-        if (! $id)
290
-        {
291
-            return false;
292
-        }
293
-
294
-        // If the user var hasn't been filled in, we need to fill it in,
295
-        // since this method will typically be used as the only method
296
-        // to determine whether a user is logged in or not.
297
-        if (! $this->user)
298
-        {
299
-            $this->user = $this->user_model->as_array()
300
-                                           ->find_by('id', (int)$id);
301
-
302
-            if (empty($this->user))
303
-            {
304
-                return false;
305
-            }
306
-        }
307
-
308
-        // If logged in, ensure cache control
309
-        // headers are in place
310
-        $this->setHeaders();
311
-
312
-        return true;
313
-    }
314
-
315
-    //--------------------------------------------------------------------
316
-
317
-    /**
318
-     * Attempts to log a user in based on the "remember me" cookie.
319
-     *
320
-     * @return bool
321
-     */
322
-    public function viaRemember()
323
-    {
324
-        if (! config_item('auth.allow_remembering'))
325
-        {
326
-            return false;
327
-        }
328
-
329
-        $this->ci->load->helper('cookie');
330
-
331
-        if (! $token = get_cookie('remember'))
332
-        {
333
-            return false;
334
-        }
335
-
336
-        // Attempt to match the token against our auth_tokens table.
337
-        $query = $this->ci->db->where('hash', $this->ci->login_model->hashRememberToken($token))
338
-                              ->get('auth_tokens');
339
-
340
-        if (! $query->num_rows())
341
-        {
342
-            return false;
343
-        }
344
-
345
-        // Grab the user
346
-        $email = $query->row()->email;
347
-
348
-        $user = $this->user_model->as_array()
349
-                                 ->find_by('email', $email);
350
-
351
-        $this->loginUser($user);
352
-
353
-        // We only want our remember me tokens to be valid
354
-        // for a single use.
355
-        $this->refreshRememberCookie($user, $token);
356
-
357
-        return true;
358
-    }
359
-
360
-    //--------------------------------------------------------------------
361
-
362
-    /**
363
-     * Registers a new user and handles activation method.
364
-     *
365
-     * @param $user_data
366
-     * @return bool
367
-     */
368
-    public function registerUser($user_data)
369
-    {
370
-        // Anything special needed for Activation?
371
-        $method = config_item('auth.activation_method');
372
-
373
-        $user_data['active'] = $method == 'auto' ? 1 : 0;
374
-
375
-        // If via email, we need to generate a hash
376
-        $this->ci->load->helper('string');
377
-        $token = random_string('alnum', 24);
378
-        $user_data['activate_hash'] = hash('sha1', config_item('auth.salt') . $token);
379
-
380
-        // Email should NOT be case sensitive.
381
-        if (! empty($user_data['email']))
382
-        {
383
-            $user_data['email'] = strtolower($user_data['email']);
384
-        }
385
-
386
-        // Save the user
387
-        if (! $id = $this->user_model->insert($user_data))
388
-        {
389
-            $this->error = $this->user_model->error();
390
-            return false;
391
-        }
392
-
393
-        $data = [
394
-            'user_id' => $id,
395
-            'email'   => $user_data['email'],
396
-            'token'   => $token,
397
-            'method'  => $method
398
-        ];
399
-
400
-        Events::trigger('didRegisterUser', [$data]);
401
-
402
-        return true;
403
-    }
404
-
405
-    //--------------------------------------------------------------------
406
-
407
-    /**
408
-     * Used to verify the user values and activate a user so they can
409
-     * visit the site.
410
-     *
411
-     * @param $data
412
-     * @return bool
413
-     */
414
-    public function activateUser($data)
415
-    {
416
-        $post = [
417
-            'email'         => $data['email'],
418
-            'activate_hash' => hash('sha1', config_item('auth.salt') . $data['code'])
419
-        ];
420
-
421
-        $user = $this->user_model->where($post)
422
-                                 ->first();
423
-
424
-        if (! $user) {
425
-            $this->error = $this->user_model->error() ? $this->user_model->error() : lang('auth.activate_no_user');
426
-
427
-            return false;
428
-        }
429
-
430
-        if (! $this->user_model->update($user->id, ['active' => 1, 'activate_hash' => null]))
431
-        {
432
-            $this->error = $this->user_model->error();
433
-            return false;
434
-        }
435
-
436
-        Events::trigger('didActivate', [(array)$user]);
437
-
438
-        return true;
439
-    }
440
-
441
-    //--------------------------------------------------------------------
442
-
443
-    /**
444
-     * Used to allow manual activation of a user with a known ID.
445
-     *
446
-     * @param $id
447
-     * @return bool
448
-     */
449
-    public function activateUserById($id)
450
-    {
451
-        if (! $this->user_model->update($id, ['active' => 1, 'activate_hash' => null]))
452
-        {
453
-            $this->error = $this->user_model->error();
454
-            return false;
455
-        }
456
-
457
-        Events::trigger('didActivate', [$this->user_model->as_array()->find($id)]);
458
-
459
-        return true;
460
-    }
461
-
462
-    //--------------------------------------------------------------------
463
-
464
-    /**
465
-     * Grabs the current user object. Returns NULL if nothing found.
466
-     *
467
-     * @return array|null
468
-     */
469
-    public function user()
470
-    {
471
-        return $this->user;
472
-    }
473
-
474
-    //--------------------------------------------------------------------
475
-
476
-    /**
477
-     * A convenience method to grab the current user's ID.
478
-     *
479
-     * @return int|null
480
-     */
481
-    public function id()
482
-    {
483
-        if (! is_array($this->user) || empty($this->user['id']))
484
-        {
485
-            return null;
486
-        }
487
-
488
-        return (int)$this->user['id'];
489
-    }
490
-
491
-    //--------------------------------------------------------------------
492
-
493
-    /**
494
-     * Checks to see if the user is currently being throttled.
495
-     *
496
-     *  - If they are NOT, will return FALSE.
497
-     *  - If they ARE, will return the number of seconds until they can try again.
498
-     *
499
-     * @param $email
500
-     * @return mixed
501
-     */
502
-    public function isThrottled($user)
503
-    {
504
-        // Not throttling? Get outta here!
505
-        if (! config_item('auth.allow_throttling'))
506
-        {
507
-            return false;
508
-        }
509
-
510
-        // Get user_id
511
-        $user_id = $user ? $user['id'] : null;
55
+	protected $ci;
56
+
57
+	protected $user = null;
58
+
59
+	public $user_model = null;
60
+
61
+	public $error = null;
62
+
63
+	//--------------------------------------------------------------------
64
+
65
+	public function __construct( $ci=null )
66
+	{
67
+		if ($ci)
68
+		{
69
+			$this->ci= $ci;
70
+		}
71
+		else
72
+		{
73
+			$this->ci =& get_instance();
74
+		}
75
+
76
+		// Get our compatibility password file loaded up.
77
+		if (! function_exists('password_hash'))
78
+		{
79
+			require_once dirname(__FILE__) .'password.php';
80
+		}
81
+
82
+		if (empty($this->ci->session))
83
+		{
84
+			$this->ci->load->library('session');
85
+		}
86
+
87
+		$this->ci->config->load('auth');
88
+		$this->ci->load->model('auth/login_model');
89
+		$this->ci->load->language('auth/auth');
90
+	}
91
+
92
+	//--------------------------------------------------------------------
93
+
94
+	/**
95
+	 * Attempt to log a user into the system.
96
+	 *
97
+	 * $credentials is an array of key/value pairs needed to log the user in.
98
+	 * This is often email/password, or username/password.
99
+	 *
100
+	 * @param array $credentials
101
+	 * @param bool  $remember
102
+	 * @return bool|mixed
103
+	 */
104
+	public function login($credentials, $remember=false)
105
+	{
106
+		$user = $this->validate($credentials, true);
107
+
108
+		// If the user is throttled due to too many invalid logins
109
+		// or the system is under attack, kick them back.
110
+		// We need to test for this after validation because we
111
+		// don't want it to affect a valid login.
112
+
113
+		// If throttling time is above zero, we can't allow
114
+		// logins now.
115
+		$time = (int)$this->isThrottled($user);
116
+		if ($time > 0)
117
+		{
118
+			$this->error = sprintf(lang('auth.throttled'), $time);
119
+			return false;
120
+		}
121
+
122
+		if (! $user)
123
+		{
124
+			if (empty($this->error))
125
+			{
126
+				// We need to set an error if there is no one
127
+				$this->error = lang('auth.invalid_user');
128
+			}
129
+			$this->user = null;
130
+			return $user;
131
+		}       
132
+
133
+		$this->loginUser($user);
134
+
135
+		if ($remember)
136
+		{
137
+			$this->rememberUser($user);
138
+		}
139
+
140
+		Events::trigger('didLogin', [$user]);
141
+
142
+		return true;
143
+	}
144
+
145
+	//--------------------------------------------------------------------
146
+
147
+	/**
148
+	 * Validates user login information without logging them in.
149
+	 *
150
+	 * $credentials is an array of key/value pairs needed to log the user in.
151
+	 * This is often email/password, or username/password.
152
+	 *
153
+	 * @param $credentials
154
+	 * @param bool $return_user
155
+	 * @return mixed
156
+	 */
157
+	public function validate($credentials, $return_user=false)
158
+	{
159
+		// Get ip address
160
+		$ip_address = $this->ci->input->ip_address();
161
+
162
+		// We do not want to force case-sensitivity on things
163
+		// like username and email for usability sake.
164
+		if (! empty($credentials['email']))
165
+		{
166
+			$credentials['email'] = strtolower($credentials['email']);
167
+		}
168
+
169
+		// Can't validate without a password.
170
+		if (empty($credentials['password']) || count($credentials) < 2)
171
+		{
172
+			$this->ci->login_model->recordLoginAttempt($ip_address);
173
+			return null;
174
+		}
175
+
176
+		$password = $credentials['password'];
177
+		unset($credentials['password']);
178
+
179
+		// We should only be allowed 1 single other credential to
180
+		// test against.
181
+		if (count($credentials) > 1)
182
+		{
183
+			$this->error = lang('auth.too_many_credentials');
184
+			$this->ci->login_model->recordLoginAttempt($ip_address);
185
+			return false;
186
+		}
187
+
188
+		// Ensure that the fields are allowed validation fields
189
+		if (! in_array(key($credentials), config_item('auth.valid_fields')) )
190
+		{
191
+			$this->error = lang('auth.invalid_credentials');
192
+			$this->ci->login_model->recordLoginAttempt($ip_address);
193
+			return false;
194
+		}
195
+
196
+		// Can we find a user with those credentials?
197
+		$user = $this->user_model->as_array()
198
+								 ->where($credentials)
199
+								 ->first();
200
+
201
+		if (! $user)
202
+		{
203
+			$this->error = lang('auth.invalid_user');
204
+			$this->ci->login_model->recordLoginAttempt($ip_address);
205
+			return false;
206
+		}
207
+
208
+		// Now, try matching the passwords.
209
+		$result =  password_verify($password, $user['password_hash']);
210
+
211
+		if (! $result)
212
+		{
213
+			$this->error = lang('auth.invalid_password');
214
+			$this->ci->login_model->recordLoginAttempt($ip_address, $user['id']);
215
+			return false;
216
+		}
217
+
218
+		// Check to see if the password needs to be rehashed.
219
+		// This would be due to the hash algorithm or hash
220
+		// cost changing since the last time that a user
221
+		// logged in.
222
+		if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT, ['cost' => config_item('auth.hash_cost')] ))
223
+		{
224
+			$new_hash = Password::hashPassword($password);
225
+			$this->user_model->skip_validation()
226
+							 ->update($user['id'], ['password_hash' => $new_hash]);
227
+			unset($new_hash);
228
+		}
229
+
230
+		// Is the user active?
231
+		if (! $user['active'])
232
+		{
233
+			$this->error = lang('auth.inactive_account');
234
+			return false;
235
+		}
236
+
237
+		return $return_user ? $user : true;
238
+	}
239
+
240
+	//--------------------------------------------------------------------
241
+
242
+	/**
243
+	 * Logs a user out and removes all session information.
244
+	 *
245
+	 * @return mixed
246
+	 */
247
+	public function logout()
248
+	{
249
+		$this->ci->load->helper('cookie');
250
+
251
+		if (! Events::trigger('beforeLogout', [$this->user]))
252
+		{
253
+			return false;
254
+		}
255
+
256
+		// Destroy the session data - but ensure a session is still
257
+		// available for flash messages, etc.
258
+		if (isset($_SESSION))
259
+		{
260
+			foreach ( $_SESSION as $key => $value )
261
+			{
262
+				$_SESSION[ $key ] = NULL;
263
+				unset( $_SESSION[ $key ] );
264
+			}
265
+		}
266
+		// Also, regenerate the session ID for a touch of added safety.
267
+		$this->ci->session->sess_regenerate(true);
268
+
269
+		// Take care of any rememberme functionality.
270
+		if (config_item('auth.allow_remembering'))
271
+		{
272
+			$token = get_cookie('remember');
273
+
274
+			$this->invalidateRememberCookie($this->user['email'], $token);
275
+		}
276
+	}
277
+
278
+	//--------------------------------------------------------------------
279
+
280
+	/**
281
+	 * Checks whether a user is logged in or not.
282
+	 *
283
+	 * @return bool
284
+	 */
285
+	public function isLoggedIn()
286
+	{
287
+		$id = $this->ci->session->userdata('logged_in');
288
+
289
+		if (! $id)
290
+		{
291
+			return false;
292
+		}
293
+
294
+		// If the user var hasn't been filled in, we need to fill it in,
295
+		// since this method will typically be used as the only method
296
+		// to determine whether a user is logged in or not.
297
+		if (! $this->user)
298
+		{
299
+			$this->user = $this->user_model->as_array()
300
+										   ->find_by('id', (int)$id);
301
+
302
+			if (empty($this->user))
303
+			{
304
+				return false;
305
+			}
306
+		}
307
+
308
+		// If logged in, ensure cache control
309
+		// headers are in place
310
+		$this->setHeaders();
311
+
312
+		return true;
313
+	}
314
+
315
+	//--------------------------------------------------------------------
316
+
317
+	/**
318
+	 * Attempts to log a user in based on the "remember me" cookie.
319
+	 *
320
+	 * @return bool
321
+	 */
322
+	public function viaRemember()
323
+	{
324
+		if (! config_item('auth.allow_remembering'))
325
+		{
326
+			return false;
327
+		}
328
+
329
+		$this->ci->load->helper('cookie');
330
+
331
+		if (! $token = get_cookie('remember'))
332
+		{
333
+			return false;
334
+		}
335
+
336
+		// Attempt to match the token against our auth_tokens table.
337
+		$query = $this->ci->db->where('hash', $this->ci->login_model->hashRememberToken($token))
338
+							  ->get('auth_tokens');
339
+
340
+		if (! $query->num_rows())
341
+		{
342
+			return false;
343
+		}
344
+
345
+		// Grab the user
346
+		$email = $query->row()->email;
347
+
348
+		$user = $this->user_model->as_array()
349
+								 ->find_by('email', $email);
350
+
351
+		$this->loginUser($user);
352
+
353
+		// We only want our remember me tokens to be valid
354
+		// for a single use.
355
+		$this->refreshRememberCookie($user, $token);
356
+
357
+		return true;
358
+	}
359
+
360
+	//--------------------------------------------------------------------
361
+
362
+	/**
363
+	 * Registers a new user and handles activation method.
364
+	 *
365
+	 * @param $user_data
366
+	 * @return bool
367
+	 */
368
+	public function registerUser($user_data)
369
+	{
370
+		// Anything special needed for Activation?
371
+		$method = config_item('auth.activation_method');
372
+
373
+		$user_data['active'] = $method == 'auto' ? 1 : 0;
374
+
375
+		// If via email, we need to generate a hash
376
+		$this->ci->load->helper('string');
377
+		$token = random_string('alnum', 24);
378
+		$user_data['activate_hash'] = hash('sha1', config_item('auth.salt') . $token);
379
+
380
+		// Email should NOT be case sensitive.
381
+		if (! empty($user_data['email']))
382
+		{
383
+			$user_data['email'] = strtolower($user_data['email']);
384
+		}
385
+
386
+		// Save the user
387
+		if (! $id = $this->user_model->insert($user_data))
388
+		{
389
+			$this->error = $this->user_model->error();
390
+			return false;
391
+		}
392
+
393
+		$data = [
394
+			'user_id' => $id,
395
+			'email'   => $user_data['email'],
396
+			'token'   => $token,
397
+			'method'  => $method
398
+		];
399
+
400
+		Events::trigger('didRegisterUser', [$data]);
401
+
402
+		return true;
403
+	}
404
+
405
+	//--------------------------------------------------------------------
406
+
407
+	/**
408
+	 * Used to verify the user values and activate a user so they can
409
+	 * visit the site.
410
+	 *
411
+	 * @param $data
412
+	 * @return bool
413
+	 */
414
+	public function activateUser($data)
415
+	{
416
+		$post = [
417
+			'email'         => $data['email'],
418
+			'activate_hash' => hash('sha1', config_item('auth.salt') . $data['code'])
419
+		];
420
+
421
+		$user = $this->user_model->where($post)
422
+								 ->first();
423
+
424
+		if (! $user) {
425
+			$this->error = $this->user_model->error() ? $this->user_model->error() : lang('auth.activate_no_user');
426
+
427
+			return false;
428
+		}
429
+
430
+		if (! $this->user_model->update($user->id, ['active' => 1, 'activate_hash' => null]))
431
+		{
432
+			$this->error = $this->user_model->error();
433
+			return false;
434
+		}
435
+
436
+		Events::trigger('didActivate', [(array)$user]);
437
+
438
+		return true;
439
+	}
440
+
441
+	//--------------------------------------------------------------------
442
+
443
+	/**
444
+	 * Used to allow manual activation of a user with a known ID.
445
+	 *
446
+	 * @param $id
447
+	 * @return bool
448
+	 */
449
+	public function activateUserById($id)
450
+	{
451
+		if (! $this->user_model->update($id, ['active' => 1, 'activate_hash' => null]))
452
+		{
453
+			$this->error = $this->user_model->error();
454
+			return false;
455
+		}
456
+
457
+		Events::trigger('didActivate', [$this->user_model->as_array()->find($id)]);
458
+
459
+		return true;
460
+	}
461
+
462
+	//--------------------------------------------------------------------
463
+
464
+	/**
465
+	 * Grabs the current user object. Returns NULL if nothing found.
466
+	 *
467
+	 * @return array|null
468
+	 */
469
+	public function user()
470
+	{
471
+		return $this->user;
472
+	}
473
+
474
+	//--------------------------------------------------------------------
475
+
476
+	/**
477
+	 * A convenience method to grab the current user's ID.
478
+	 *
479
+	 * @return int|null
480
+	 */
481
+	public function id()
482
+	{
483
+		if (! is_array($this->user) || empty($this->user['id']))
484
+		{
485
+			return null;
486
+		}
487
+
488
+		return (int)$this->user['id'];
489
+	}
490
+
491
+	//--------------------------------------------------------------------
492
+
493
+	/**
494
+	 * Checks to see if the user is currently being throttled.
495
+	 *
496
+	 *  - If they are NOT, will return FALSE.
497
+	 *  - If they ARE, will return the number of seconds until they can try again.
498
+	 *
499
+	 * @param $email
500
+	 * @return mixed
501
+	 */
502
+	public function isThrottled($user)
503
+	{
504
+		// Not throttling? Get outta here!
505
+		if (! config_item('auth.allow_throttling'))
506
+		{
507
+			return false;
508
+		}
509
+
510
+		// Get user_id
511
+		$user_id = $user ? $user['id'] : null;
512 512
         
513
-        // Get ip address
514
-        $ip_address = $this->ci->input->ip_address();
515
-
516
-        // Have any attempts been made?
517
-        $attempts = $this->ci->login_model->countLoginAttempts($ip_address, $user_id);
518
-
519
-        // Grab the amount of time to add if the system thinks we're
520
-        // under a distributed brute force attack.
521
-        // Affect users that have at least 1 failure login attempt
522
-        $dbrute_time = ($attempts === 0) ? 0 : $this->ci->login_model->distributedBruteForceTime();
523
-
524
-        // If this user was found to possibly be under a brute
525
-        // force attack, their account would have been banned
526
-        // for 15 minutes.
527
-        if ($time = isset($_SESSION['bruteBan']) ? $_SESSION['bruteBan'] : false)
528
-        {
529
-            // If the current time is less than the
530
-            // the ban expiration, plus any distributed time
531
-            // then the user can't login just yet.
532
-            if ($time + $dbrute_time > time())
533
-            {
534
-                // The user is banned still...
535
-                $this->error = lang('auth.bruteBan_notice');
536
-                return ($time + $dbrute_time) - time();
537
-            }
538
-
539
-            // Still here? The the ban time is over...
540
-            unset($_SESSION['bruteBan']);
541
-        }
542
-
543
-        // Grab the time of last attempt and
544
-        // determine if we're throttled by amount of time passed.
545
-        $last_time = $this->ci->login_model->lastLoginAttemptTime($ip_address, $user_id);
546
-
547
-        $allowed = config_item('auth.allowed_login_attempts');
548
-
549
-        // We're not throttling if there are 0 attempts or
550
-        // the number is less than or equal to the allowed free attempts
551
-        if ($attempts === 0 || $attempts <= $allowed)
552
-        {
553
-            // Before we can say there's nothing up here,
554
-            // we need to check dbrute time.
555
-            $time_left = $last_time + $dbrute_time - time();
556
-
557
-            if ($time_left > 0)
558
-            {
559
-                return $time_left;
560
-            }
561
-
562
-            return false;
563
-        }
564
-
565
-        // If the number of attempts is excessive (above 100) we need
566
-        // to check the elapsed time of all of these attacks. If they are
567
-        // less than 1 minute it's obvious this is a brute force attack,
568
-        // so we'll set a session flag and block that user for 15 minutes.
569
-        if ($attempts > 100 && $this->ci->login_model->isBruteForced($ip_address, $user_id))
570
-        {
571
-            $this->error = lang('auth.bruteBan_notice');
572
-
573
-            $ban_time = 60 * 15;    // 15 minutes
574
-            $_SESSION['bruteBan'] = time() + $ban_time;
575
-            return $ban_time;
576
-        }
577
-
578
-        // Get our allowed attempts out of the picture.
579
-        $attempts = $attempts - $allowed;
580
-
581
-        $max_time = config_item('auth.max_throttle_time');
582
-
583
-        $add_time = 5 * pow(2, $attempts - 1);
584
-
585
-        if ($add_time > $max_time)
586
-        {
587
-            $add_time = $max_time;
588
-        }
589
-
590
-        $next_time = $last_time + $add_time + $dbrute_time;
591
-
592
-        $current = time();
593
-
594
-        // We are NOT throttled if we are already
595
-        // past the allowed time.
596
-        if ($current > $next_time)
597
-        {
598
-            return false;
599
-        }
600
-
601
-        return $next_time - $current;
602
-    }
603
-
604
-    //--------------------------------------------------------------------
605
-
606
-    /**
607
-     * Sends a password reset link email to the user associated with
608
-     * the passed in $email.
609
-     *
610
-     * @param $email
611
-     * @return mixed
612
-     */
613
-    public function remindUser($email)
614
-    {
615
-        // Emails should NOT be case sensitive.
616
-        $email = strtolower($email);
617
-
618
-        // Is it a valid user?
619
-        $user = $this->user_model->find_by('email', $email);
620
-
621
-        if (! $user)
622
-        {
623
-            $this->error = lang('auth.invalid_email');
624
-            return false;
625
-        }
626
-
627
-        // Generate/store our codes
628
-        $this->ci->load->helper('string');
629
-        $token = random_string('alnum', 24);
630
-        $hash = hash('sha1', config_item('auth.salt') .$token);
631
-
632
-        $result = $this->user_model->update($user->id, ['reset_hash' => $hash]);
633
-
634
-        if (! $result)
635
-        {
636
-            $this->error = $this->user_model->error();
637
-            return false;
638
-        }
639
-
640
-        Events::trigger('didRemindUser', [(array)$user, $token]);
641
-
642
-        return true;
643
-    }
644
-
645
-    //--------------------------------------------------------------------
646
-
647
-    /**
648
-     * Validates the credentials provided and, if valid, resets the password.
649
-     *
650
-     * The $credentials array MUST contain a 'code' key with the string to
651
-     * hash and check against the reset_hash.
652
-     *
653
-     * @param $credentials
654
-     * @param $password
655
-     * @param $passConfirm
656
-     * @return mixed
657
-     */
658
-    public function resetPassword($credentials, $password, $passConfirm)
659
-    {
660
-        if (empty($credentials['code']))
661
-        {
662
-            $this->error = lang('auth.need_reset_code');
663
-            return false;
664
-        }
665
-
666
-        // Generate a hash to match against the table.
667
-        $credentials['reset_hash'] = hash('sha1', config_item('auth.salt') .$credentials['code']);
668
-        unset($credentials['code']);
669
-
670
-        if (! empty($credentials['email']))
671
-        {
672
-            $credentials['email'] = strtolower($credentials['email']);
673
-        }
674
-
675
-        // Is there a matching user?
676
-        $user = $this->user_model->find_by($credentials);
677
-
678
-        if (! $user)
679
-        {
680
-            $this->error = lang('auth.reset_no_user');
681
-            return false;
682
-        }
683
-
684
-        // Update their password and reset their reset_hash
685
-        $data = [
686
-            'password'     => $password,
687
-            'pass_confirm' => $passConfirm,
688
-            'reset_hash'   => null
689
-        ];
690
-
691
-        if (! $this->user_model->update($user->id, $data))
692
-        {
693
-            $this->error = $this->user_model->error();
694
-            return false;
695
-        }
696
-
697
-        Events::trigger('didResetPassword', [(array)$user]);
698
-
699
-        return true;
700
-    }
701
-
702
-    //--------------------------------------------------------------------
703
-
704
-    /**
705
-     * Provides a way for implementations to allow new statuses to be set
706
-     * on the user. The details will vary based upon implementation, but
707
-     * will often allow for banning or suspending users.
708
-     *
709
-     * @param $newStatus
710
-     * @param null $message
711
-     * @return mixed
712
-     */
713
-    public function changeStatus($newStatus, $message=null)
714
-    {
715
-        // todo actually record new users status!
716
-    }
717
-
718
-    //--------------------------------------------------------------------
719
-
720
-    /**
721
-     * Allows the consuming application to pass in a reference to the
722
-     * model that should be used.
723
-     *
724
-     * The model MUST extend Myth\Models\CIDbModel.
725
-     *
726
-     * @param $model
727
-     * @param bool $allow_any_parent
728
-     * @return mixed
729
-     */
730
-    public function useModel($model, $allow_any_parent=false)
731
-    {
732
-        if (! $allow_any_parent && get_parent_class($model) != 'Myth\Models\CIDbModel')
733
-        {
734
-            throw new \RuntimeException('Models passed into LocalAuthenticate MUST extend Myth\Models\CIDbModel');
735
-        }
736
-
737
-        $this->user_model =& $model;
738
-
739
-        return $this;
740
-    }
741
-
742
-    //--------------------------------------------------------------------
743
-
744
-    public function error()
745
-    {
746
-        if (validation_errors())
747
-        {
748
-            return validation_errors();
749
-        }
750
-
751
-        return $this->error;
752
-    }
753
-
754
-    //--------------------------------------------------------------------
755
-
756
-    //--------------------------------------------------------------------
757
-    // Login Records
758
-    //--------------------------------------------------------------------
759
-
760
-    /**
761
-     * Purges all login attempt records from the database.
762
-     *
763
-     * @param $email
764
-     */
765
-    public function purgeLoginAttempts($email)
766
-    {
767
-        // Emails should NOT be case sensitive.
768
-        $email = strtolower($email);
769
-
770
-        $this->ci->login_model->purgeLoginAttempts($email);
771
-
772
-        // @todo record activity of login attempts purge.
773
-        Events::trigger('didPurgeLoginAttempts', [$email]);
774
-    }
775
-
776
-    //--------------------------------------------------------------------
777
-
778
-    /**
779
-     * Purges all remember tokens for a single user. Effectively logs
780
-     * a user out of all devices. Intended to allow users to log themselves
781
-     * out of all devices as a security measure.
782
-     *
783
-     * @param $email
784
-     */
785
-    public function purgeRememberTokens($email)
786
-    {
787
-        // Emails should NOT be case sensitive.
788
-        $email = strtolower($email);
789
-
790
-        $this->ci->login_model->purgeRememberTokens($email);
791
-
792
-        // todo record activity of remember me purges.
793
-        Events::trigger('didPurgeRememberTokens', [$email]);
794
-    }
795
-
796
-    //--------------------------------------------------------------------
797
-
798
-    //--------------------------------------------------------------------
799
-    // Protected Methods
800
-    //--------------------------------------------------------------------
801
-
802
-    /**
803
-     * Check if Allow Persistent Login Cookies is enable
804
-     *
805
-     * @param $user
806
-     */
807
-    protected function rememberUser($user)
808
-    {
809
-        if (! config_item('auth.allow_remembering'))
810
-        {
811
-            log_message('debug', 'Auth library set to refuse "Remember Me" functionality.');
812
-            return false;
813
-        }
814
-
815
-        $this->refreshRememberCookie($user);
816
-    }
817
-
818
-    //--------------------------------------------------------------------
819
-
820
-    /**
821
-     * Invalidates the current rememberme cookie/database entry, creates
822
-     * a new one, stores it and returns the new value.
823
-     *
824
-     * @param $user
825
-     * @param null $token
826
-     * @return mixed
827
-     */
828
-    protected function refreshRememberCookie($user, $token=null)
829
-    {
830
-        $this->ci->load->helper('cookie');
831
-
832
-        // If a token is passed in, we know we're removing the
833
-        // old one.
834
-        if (! empty($token))
835
-        {
836
-            $this->invalidateRememberCookie($user['email'], $token);
837
-        }
838
-
839
-        $new_token = $this->ci->login_model->generateRememberToken($user);
840
-
841
-        // Save the token to the database.
842
-        $data = [
843
-            'email'   => $user['email'],
844
-            'hash'    => sha1(config_item('auth.salt') . $new_token),
845
-            'created' => date('Y-m-d H:i:s')
846
-        ];
847
-
848
-        $this->ci->db->insert('auth_tokens', $data);
849
-
850
-        // Create the cookie
851
-        set_cookie(
852
-            'remember',                             // Cookie Name
853
-            $new_token,                             // Value
854
-            config_item('auth.remember_length'),    // # Seconds until it expires
855
-            config_item('cookie_domain'),
856
-            config_item('cookie_path'),
857
-            config_item('cookie_prefix'),
858
-            false,                                  // Only send over HTTPS?
859
-            true                                    // Hide from Javascript?
860
-        );
861
-
862
-        return $new_token;
863
-    }
864
-
865
-    //--------------------------------------------------------------------
866
-
867
-    /**
868
-     * Deletes any current remember me cookies and database entries.
869
-     *
870
-     * @param $email
871
-     * @param $token
872
-     * @return string The new token (not the hash).
873
-     */
874
-    protected function invalidateRememberCookie($email, $token)
875
-    {
876
-        // Emails should NOT be case sensitive.
877
-        $email = strtolower($email);
878
-
879
-        // Remove from the database
880
-        $this->ci->login_model->deleteRememberToken($email, $token);
881
-
882
-        // Remove the cookie
883
-        delete_cookie(
884
-            'remember',
885
-            config_item('cookie_domain'),
886
-            config_item('cookie_path'),
887
-            config_item('cookie_prefix')
888
-        );
889
-    }
890
-
891
-    //--------------------------------------------------------------------
892
-
893
-    /**
894
-     * Handles the nitty gritty of actually logging our user into the system.
895
-     * Does NOT perform the authentication, just sets the system up so that
896
-     * it knows we're here.
897
-     *
898
-     * @param $user
899
-     */
900
-    protected function loginUser($user)
901
-    {
902
-        // Save the user for later access
903
-        $this->user = $user;
904
-
905
-        // Get ip address
906
-        $ip_address = $this->ci->input->ip_address();
907
-
908
-        // Regenerate the session ID to help protect
909
-        // against session fixation
910
-        $this->ci->session->sess_regenerate();
911
-
912
-        // Let the session know that we're logged in.
913
-        $this->ci->session->set_userdata('logged_in', $user['id']);
914
-
915
-        // Clear our login attempts
916
-        $this->ci->login_model->purgeLoginAttempts($ip_address, $user['id']);
917
-
918
-        // Record a new Login
919
-        $this->ci->login_model->recordLogin($user);
920
-
921
-        // If logged in, ensure cache control
922
-        // headers are in place
923
-        $this->setHeaders();
924
-
925
-        // We'll give a 20% chance to need to do a purge since we
926
-        // don't need to purge THAT often, it's just a maintenance issue.
927
-        // to keep the table from getting out of control.
928
-        if (mt_rand(1, 100) < 20)
929
-        {
930
-            $this->ci->login_model->purgeOldRememberTokens();
931
-        }
932
-    }
933
-
934
-    //--------------------------------------------------------------------
935
-
936
-    /**
937
-     * Sets the headers to ensure that pages are not cached when a user
938
-     * is logged in, helping to protect against logging out and then
939
-     * simply hitting the Back button on the browser and getting private
940
-     * information because the page was loaded from cache.
941
-     */
942
-    protected function setHeaders()
943
-    {
944
-        $this->ci->output->set_header('Cache-Control: no-store, no-cache, must-revalidate');
945
-        $this->ci->output->set_header('Cache-Control: post-check=0, pre-check=0');
946
-        $this->ci->output->set_header('Pragma: no-cache');
947
-    }
948
-
949
-    //--------------------------------------------------------------------
513
+		// Get ip address
514
+		$ip_address = $this->ci->input->ip_address();
515
+
516
+		// Have any attempts been made?
517
+		$attempts = $this->ci->login_model->countLoginAttempts($ip_address, $user_id);
518
+
519
+		// Grab the amount of time to add if the system thinks we're
520
+		// under a distributed brute force attack.
521
+		// Affect users that have at least 1 failure login attempt
522
+		$dbrute_time = ($attempts === 0) ? 0 : $this->ci->login_model->distributedBruteForceTime();
523
+
524
+		// If this user was found to possibly be under a brute
525
+		// force attack, their account would have been banned
526
+		// for 15 minutes.
527
+		if ($time = isset($_SESSION['bruteBan']) ? $_SESSION['bruteBan'] : false)
528
+		{
529
+			// If the current time is less than the
530
+			// the ban expiration, plus any distributed time
531
+			// then the user can't login just yet.
532
+			if ($time + $dbrute_time > time())
533
+			{
534
+				// The user is banned still...
535
+				$this->error = lang('auth.bruteBan_notice');
536
+				return ($time + $dbrute_time) - time();
537
+			}
538
+
539
+			// Still here? The the ban time is over...
540
+			unset($_SESSION['bruteBan']);
541
+		}
542
+
543
+		// Grab the time of last attempt and
544
+		// determine if we're throttled by amount of time passed.
545
+		$last_time = $this->ci->login_model->lastLoginAttemptTime($ip_address, $user_id);
546
+
547
+		$allowed = config_item('auth.allowed_login_attempts');
548
+
549
+		// We're not throttling if there are 0 attempts or
550
+		// the number is less than or equal to the allowed free attempts
551
+		if ($attempts === 0 || $attempts <= $allowed)
552
+		{
553
+			// Before we can say there's nothing up here,
554
+			// we need to check dbrute time.
555
+			$time_left = $last_time + $dbrute_time - time();
556
+
557
+			if ($time_left > 0)
558
+			{
559
+				return $time_left;
560
+			}
561
+
562
+			return false;
563
+		}
564
+
565
+		// If the number of attempts is excessive (above 100) we need
566
+		// to check the elapsed time of all of these attacks. If they are
567
+		// less than 1 minute it's obvious this is a brute force attack,
568
+		// so we'll set a session flag and block that user for 15 minutes.
569
+		if ($attempts > 100 && $this->ci->login_model->isBruteForced($ip_address, $user_id))
570
+		{
571
+			$this->error = lang('auth.bruteBan_notice');
572
+
573
+			$ban_time = 60 * 15;    // 15 minutes
574
+			$_SESSION['bruteBan'] = time() + $ban_time;
575
+			return $ban_time;
576
+		}
577
+
578
+		// Get our allowed attempts out of the picture.
579
+		$attempts = $attempts - $allowed;
580
+
581
+		$max_time = config_item('auth.max_throttle_time');
582
+
583
+		$add_time = 5 * pow(2, $attempts - 1);
584
+
585
+		if ($add_time > $max_time)
586
+		{
587
+			$add_time = $max_time;
588
+		}
589
+
590
+		$next_time = $last_time + $add_time + $dbrute_time;
591
+
592
+		$current = time();
593
+
594
+		// We are NOT throttled if we are already
595
+		// past the allowed time.
596
+		if ($current > $next_time)
597
+		{
598
+			return false;
599
+		}
600
+
601
+		return $next_time - $current;
602
+	}
603
+
604
+	//--------------------------------------------------------------------
605
+
606
+	/**
607
+	 * Sends a password reset link email to the user associated with
608
+	 * the passed in $email.
609
+	 *
610
+	 * @param $email
611
+	 * @return mixed
612
+	 */
613
+	public function remindUser($email)
614
+	{
615
+		// Emails should NOT be case sensitive.
616
+		$email = strtolower($email);
617
+
618
+		// Is it a valid user?
619
+		$user = $this->user_model->find_by('email', $email);
620
+
621
+		if (! $user)
622
+		{
623
+			$this->error = lang('auth.invalid_email');
624
+			return false;
625
+		}
626
+
627
+		// Generate/store our codes
628
+		$this->ci->load->helper('string');
629
+		$token = random_string('alnum', 24);
630
+		$hash = hash('sha1', config_item('auth.salt') .$token);
631
+
632
+		$result = $this->user_model->update($user->id, ['reset_hash' => $hash]);
633
+
634
+		if (! $result)
635
+		{
636
+			$this->error = $this->user_model->error();
637
+			return false;
638
+		}
639
+
640
+		Events::trigger('didRemindUser', [(array)$user, $token]);
641
+
642
+		return true;
643
+	}
644
+
645
+	//--------------------------------------------------------------------
646
+
647
+	/**
648
+	 * Validates the credentials provided and, if valid, resets the password.
649
+	 *
650
+	 * The $credentials array MUST contain a 'code' key with the string to
651
+	 * hash and check against the reset_hash.
652
+	 *
653
+	 * @param $credentials
654
+	 * @param $password
655
+	 * @param $passConfirm
656
+	 * @return mixed
657
+	 */
658
+	public function resetPassword($credentials, $password, $passConfirm)
659
+	{
660
+		if (empty($credentials['code']))
661
+		{
662
+			$this->error = lang('auth.need_reset_code');
663
+			return false;
664
+		}
665
+
666
+		// Generate a hash to match against the table.
667
+		$credentials['reset_hash'] = hash('sha1', config_item('auth.salt') .$credentials['code']);
668
+		unset($credentials['code']);
669
+
670
+		if (! empty($credentials['email']))
671
+		{
672
+			$credentials['email'] = strtolower($credentials['email']);
673
+		}
674
+
675
+		// Is there a matching user?
676
+		$user = $this->user_model->find_by($credentials);
677
+
678
+		if (! $user)
679
+		{
680
+			$this->error = lang('auth.reset_no_user');
681
+			return false;
682
+		}
683
+
684
+		// Update their password and reset their reset_hash
685
+		$data = [
686
+			'password'     => $password,
687
+			'pass_confirm' => $passConfirm,
688
+			'reset_hash'   => null
689
+		];
690
+
691
+		if (! $this->user_model->update($user->id, $data))
692
+		{
693
+			$this->error = $this->user_model->error();
694
+			return false;
695
+		}
696
+
697
+		Events::trigger('didResetPassword', [(array)$user]);
698
+
699
+		return true;
700
+	}
701
+
702
+	//--------------------------------------------------------------------
703
+
704
+	/**
705
+	 * Provides a way for implementations to allow new statuses to be set
706
+	 * on the user. The details will vary based upon implementation, but
707
+	 * will often allow for banning or suspending users.
708
+	 *
709
+	 * @param $newStatus
710
+	 * @param null $message
711
+	 * @return mixed
712
+	 */
713
+	public function changeStatus($newStatus, $message=null)
714
+	{
715
+		// todo actually record new users status!
716
+	}
717
+
718
+	//--------------------------------------------------------------------
719
+
720
+	/**
721
+	 * Allows the consuming application to pass in a reference to the
722
+	 * model that should be used.
723
+	 *
724
+	 * The model MUST extend Myth\Models\CIDbModel.
725
+	 *
726
+	 * @param $model
727
+	 * @param bool $allow_any_parent
728
+	 * @return mixed
729
+	 */
730
+	public function useModel($model, $allow_any_parent=false)
731
+	{
732
+		if (! $allow_any_parent && get_parent_class($model) != 'Myth\Models\CIDbModel')
733
+		{
734
+			throw new \RuntimeException('Models passed into LocalAuthenticate MUST extend Myth\Models\CIDbModel');
735
+		}
736
+
737
+		$this->user_model =& $model;
738
+
739
+		return $this;
740
+	}
741
+
742
+	//--------------------------------------------------------------------
743
+
744
+	public function error()
745
+	{
746
+		if (validation_errors())
747
+		{
748
+			return validation_errors();
749
+		}
750
+
751
+		return $this->error;
752
+	}
753
+
754
+	//--------------------------------------------------------------------
755
+
756
+	//--------------------------------------------------------------------
757
+	// Login Records
758
+	//--------------------------------------------------------------------
759
+
760
+	/**
761
+	 * Purges all login attempt records from the database.
762
+	 *
763
+	 * @param $email
764
+	 */
765
+	public function purgeLoginAttempts($email)
766
+	{
767
+		// Emails should NOT be case sensitive.
768
+		$email = strtolower($email);
769
+
770
+		$this->ci->login_model->purgeLoginAttempts($email);
771
+
772
+		// @todo record activity of login attempts purge.
773
+		Events::trigger('didPurgeLoginAttempts', [$email]);
774
+	}
775
+
776
+	//--------------------------------------------------------------------
777
+
778
+	/**
779
+	 * Purges all remember tokens for a single user. Effectively logs
780
+	 * a user out of all devices. Intended to allow users to log themselves
781
+	 * out of all devices as a security measure.
782
+	 *
783
+	 * @param $email
784
+	 */
785
+	public function purgeRememberTokens($email)
786
+	{
787
+		// Emails should NOT be case sensitive.
788
+		$email = strtolower($email);
789
+
790
+		$this->ci->login_model->purgeRememberTokens($email);
791
+
792
+		// todo record activity of remember me purges.
793
+		Events::trigger('didPurgeRememberTokens', [$email]);
794
+	}
795
+
796
+	//--------------------------------------------------------------------
797
+
798
+	//--------------------------------------------------------------------
799
+	// Protected Methods
800
+	//--------------------------------------------------------------------
801
+
802
+	/**
803
+	 * Check if Allow Persistent Login Cookies is enable
804
+	 *
805
+	 * @param $user
806
+	 */
807
+	protected function rememberUser($user)
808
+	{
809
+		if (! config_item('auth.allow_remembering'))
810
+		{
811
+			log_message('debug', 'Auth library set to refuse "Remember Me" functionality.');
812
+			return false;
813
+		}
814
+
815
+		$this->refreshRememberCookie($user);
816
+	}
817
+
818
+	//--------------------------------------------------------------------
819
+
820
+	/**
821
+	 * Invalidates the current rememberme cookie/database entry, creates
822
+	 * a new one, stores it and returns the new value.
823
+	 *
824
+	 * @param $user
825
+	 * @param null $token
826
+	 * @return mixed
827
+	 */
828
+	protected function refreshRememberCookie($user, $token=null)
829
+	{
830
+		$this->ci->load->helper('cookie');
831
+
832
+		// If a token is passed in, we know we're removing the
833
+		// old one.
834
+		if (! empty($token))
835
+		{
836
+			$this->invalidateRememberCookie($user['email'], $token);
837
+		}
838
+
839
+		$new_token = $this->ci->login_model->generateRememberToken($user);
840
+
841
+		// Save the token to the database.
842
+		$data = [
843
+			'email'   => $user['email'],
844
+			'hash'    => sha1(config_item('auth.salt') . $new_token),
845
+			'created' => date('Y-m-d H:i:s')
846
+		];
847
+
848
+		$this->ci->db->insert('auth_tokens', $data);
849
+
850
+		// Create the cookie
851
+		set_cookie(
852
+			'remember',                             // Cookie Name
853
+			$new_token,                             // Value
854
+			config_item('auth.remember_length'),    // # Seconds until it expires
855
+			config_item('cookie_domain'),
856
+			config_item('cookie_path'),
857
+			config_item('cookie_prefix'),
858
+			false,                                  // Only send over HTTPS?
859
+			true                                    // Hide from Javascript?
860
+		);
861
+
862
+		return $new_token;
863
+	}
864
+
865
+	//--------------------------------------------------------------------
866
+
867
+	/**
868
+	 * Deletes any current remember me cookies and database entries.
869
+	 *
870
+	 * @param $email
871
+	 * @param $token
872
+	 * @return string The new token (not the hash).
873
+	 */
874
+	protected function invalidateRememberCookie($email, $token)
875
+	{
876
+		// Emails should NOT be case sensitive.
877
+		$email = strtolower($email);
878
+
879
+		// Remove from the database
880
+		$this->ci->login_model->deleteRememberToken($email, $token);
881
+
882
+		// Remove the cookie
883
+		delete_cookie(
884
+			'remember',
885
+			config_item('cookie_domain'),
886
+			config_item('cookie_path'),
887
+			config_item('cookie_prefix')
888
+		);
889
+	}
890
+
891
+	//--------------------------------------------------------------------
892
+
893
+	/**
894
+	 * Handles the nitty gritty of actually logging our user into the system.
895
+	 * Does NOT perform the authentication, just sets the system up so that
896
+	 * it knows we're here.
897
+	 *
898
+	 * @param $user
899
+	 */
900
+	protected function loginUser($user)
901
+	{
902
+		// Save the user for later access
903
+		$this->user = $user;
904
+
905
+		// Get ip address
906
+		$ip_address = $this->ci->input->ip_address();
907
+
908
+		// Regenerate the session ID to help protect
909
+		// against session fixation
910
+		$this->ci->session->sess_regenerate();
911
+
912
+		// Let the session know that we're logged in.
913
+		$this->ci->session->set_userdata('logged_in', $user['id']);
914
+
915
+		// Clear our login attempts
916
+		$this->ci->login_model->purgeLoginAttempts($ip_address, $user['id']);
917
+
918
+		// Record a new Login
919
+		$this->ci->login_model->recordLogin($user);
920
+
921
+		// If logged in, ensure cache control
922
+		// headers are in place
923
+		$this->setHeaders();
924
+
925
+		// We'll give a 20% chance to need to do a purge since we
926
+		// don't need to purge THAT often, it's just a maintenance issue.
927
+		// to keep the table from getting out of control.
928
+		if (mt_rand(1, 100) < 20)
929
+		{
930
+			$this->ci->login_model->purgeOldRememberTokens();
931
+		}
932
+	}
933
+
934
+	//--------------------------------------------------------------------
935
+
936
+	/**
937
+	 * Sets the headers to ensure that pages are not cached when a user
938
+	 * is logged in, helping to protect against logging out and then
939
+	 * simply hitting the Back button on the browser and getting private
940
+	 * information because the page was loaded from cache.
941
+	 */
942
+	protected function setHeaders()
943
+	{
944
+		$this->ci->output->set_header('Cache-Control: no-store, no-cache, must-revalidate');
945
+		$this->ci->output->set_header('Cache-Control: post-check=0, pre-check=0');
946
+		$this->ci->output->set_header('Pragma: no-cache');
947
+	}
948
+
949
+	//--------------------------------------------------------------------
950 950
 
951 951
 
952 952
 }
Please login to merge, or discard this patch.
myth/CIModules/auth/models/Login_model.php 3 patches
Indentation   +344 added lines, -344 removed lines patch added patch discarded remove patch
@@ -41,349 +41,349 @@
 block discarded – undo
41 41
  */
42 42
 class Login_model extends \Myth\Models\CIDbModel {
43 43
 
44
-    protected $table_name = 'auth_logins';
45
-
46
-    protected $set_created = false;
47
-    protected $set_modified = false;
48
-
49
-    //--------------------------------------------------------------------
50
-
51
-    //--------------------------------------------------------------------
52
-    // Login Attempts
53
-    //--------------------------------------------------------------------
54
-
55
-    /**
56
-     * Records a login attempt. This is used to implement
57
-     * throttling of application, ip address and user login attempts.
58
-     *
59
-     * @param $ip_address
60
-     * @param $user_id
61
-     * @return void
62
-     */
63
-    public function recordLoginAttempt($ip_address, $user_id = null)
64
-    {
65
-        $datetime = date('Y-m-d H:i:s');
66
-
67
-        // log attempt for app
68
-        $data = [
69
-            'type' => 'app',
70
-            'datetime' => $datetime
71
-        ];
72
-
73
-        $this->db->insert('auth_login_attempts', $data);
74
-
75
-        // log attempt for ip address
76
-        if (! empty($ip_address))
77
-        {
78
-            $data = [
79
-                'type' => 'ip',
80
-                'ip_address' => $ip_address,
81
-                'datetime' => $datetime
82
-            ];
83
-
84
-            $this->db->insert('auth_login_attempts', $data);
85
-        }
86
-
87
-        // log attempt for user
88
-        if ($user_id)
89
-        {
90
-            $data = [
91
-                'type' => 'user',
92
-                'user_id' => $user_id,
93
-                'datetime' => $datetime
94
-            ];
95
-
96
-            $this->db->insert('auth_login_attempts', $data);
97
-        }
98
-    }
99
-
100
-    //--------------------------------------------------------------------
101
-
102
-    /**
103
-     * Purges all login attempt records from the database.
104
-     *
105
-     * @param $ip_address
106
-     * @param $user_id
107
-     * @return mixed
108
-     */
109
-    public function purgeLoginAttempts($ip_address, $user_id)
110
-    {
111
-        return $this->db->where('ip_address', $ip_address)
112
-                        ->or_where('user_id', $user_id)
113
-                        ->delete('auth_login_attempts');
114
-    }
115
-
116
-    //--------------------------------------------------------------------
117
-
118
-    /**
119
-     * Checks to see if how many login attempts have been attempted in the
120
-     * last 60 seconds. If over 100, it is considered to be under a
121
-     * brute force attempt.
122
-     *
123
-     * @param $ip_address
124
-     * @param $user_id
125
-     * @return bool
126
-     */
127
-    public function isBruteForced($ip_address, $user_id)
128
-    {
129
-        $start_time = date('Y-m-d H:i:s', time() - 60);
130
-
131
-        $attempts_ip = $this->db->where('type', 'ip')
132
-                                ->where('ip_address', $ip_address)
133
-                                ->where('datetime >=', $start_time)
134
-                                ->count_all_results('auth_login_attempts');
135
-
136
-        if (! $user_id)
137
-        {
138
-            return $attempts_ip > 100;
139
-        }
140
-
141
-        $attempts_user = $this->db->where('type', 'user')
142
-                                  ->where('user_id', $user_id)
143
-                                  ->where('datetime >=', $start_time)
144
-                                  ->count_all_results('auth_login_attempts');
145
-
146
-        if ($attempts_user > $attempts_ip) 
147
-        {
148
-            return $attempts_user > 100;
149
-        }
150
-        else
151
-        {
152
-            return $attempts_ip > 100;
153
-        }
154
-    }
155
-
156
-    //--------------------------------------------------------------------
157
-
158
-    /**
159
-     * Attempts to determine if the system is under a distributed
160
-     * brute force attack.
161
-     *
162
-     * To determine if we are in under a brute force attack, we first
163
-     * find the average number of bad logins per day that never converted to
164
-     * successful logins over the last 3 months. Then we compare
165
-     * that to the the average number of logins in the past 24 hours.
166
-     *
167
-     * If the number of attempts in the last 24 hours is more than X (see config)
168
-     * times the average, then institute additional throttling.
169
-     *
170
-     * @return int The time to add to any throttling.
171
-     */
172
-    public function distributedBruteForceTime()
173
-    {
174
-        if (! $time = $this->cache->get('dbrutetime'))
175
-        {
176
-            $time = 0;
177
-
178
-            // Compute our daily average over the last 3 months.
179
-            $avg_start_time = date('Y-m-d 00:00:00', strtotime('-3 months'));
180
-
181
-            $query = $this->db->query("SELECT COUNT(*) / COUNT(DISTINCT DATE(`datetime`)) as num_rows FROM `auth_login_attempts` WHERE `type` = 'app' AND `datetime` >= ?", $avg_start_time);
182
-
183
-            if (! $query->num_rows())
184
-            {
185
-                $average = 0;
186
-            }
187
-            else
188
-            {
189
-                $average = $query->row()->num_rows;
190
-            }
191
-
192
-            // Get the total in the last 24 hours
193
-            $today_start_time = date('Y-m-d H:i:s', strtotime('-24 hours'));
194
-
195
-            $attempts = $this->db->where('type', 'app')
196
-                                 ->where('datetime >=', $today_start_time)
197
-                                 ->count_all_results('auth_login_attempts');
198
-
199
-            if ($attempts > (config_item('auth.dbrute_multiplier') * $average))
200
-            {
201
-                $time = config_item('auth.distributed_brute_add_time');
202
-            }
203
-
204
-            // Cache it for 3 hours.
205
-            $this->cache->save('dbrutetime', $time, 60*60*3);
206
-        }
207
-
208
-        return $time;
209
-    }
210
-
211
-    //--------------------------------------------------------------------
212
-
213
-    //--------------------------------------------------------------------
214
-    // Logins
215
-    //--------------------------------------------------------------------
216
-
217
-    /**
218
-     * Records a successful login. This stores in a table so that a
219
-     * history can be pulled up later if needed for security analyses.
220
-     *
221
-     * @param $user
222
-     */
223
-    public function recordLogin($user)
224
-    {
225
-        $data = [
226
-            'user_id'    => (int)$user['id'],
227
-            'datetime'   => date('Y-m-d H:i:s'),
228
-            'ip_address' => $this->input->ip_address()
229
-        ];
230
-
231
-        return $this->db->insert('auth_logins', $data);
232
-    }
233
-
234
-    //--------------------------------------------------------------------
235
-
236
-    //--------------------------------------------------------------------
237
-    // Tokens
238
-    //--------------------------------------------------------------------
239
-
240
-    /**
241
-     * Generates a new token for the rememberme cookie.
242
-     *
243
-     * The token is based on the user's email address (since everyone will have one)
244
-     * with the '@' turned to a '.', followed by a pipe (|) and a random 128-character
245
-     * string with letters and numbers.
246
-     *
247
-     * @param $user
248
-     * @return mixed
249
-     */
250
-    public function generateRememberToken($user)
251
-    {
252
-        $this->load->helper('string');
253
-
254
-        return str_replace('@', '.', $user['email']) .'|' . random_string('alnum', 128);
255
-    }
256
-
257
-    //--------------------------------------------------------------------
258
-
259
-    /**
260
-     * Hashes the token for the Remember Me Functionality.
261
-     *
262
-     * @param $token
263
-     * @return string
264
-     */
265
-    public function hashRememberToken($token)
266
-    {
267
-        return sha1(config_item('auth.salt') . $token);
268
-    }
269
-
270
-    //--------------------------------------------------------------------
271
-
272
-    /**
273
-     * Deletes a single token that matches the email/token combo.
274
-     *
275
-     * @param $email
276
-     * @param $token
277
-     * @return mixed
278
-     */
279
-    public function deleteRememberToken($email, $token)
280
-    {
281
-        $where = [
282
-            'email' => $email,
283
-            'hash'  => $this->hashRememberToken($token)
284
-        ];
285
-
286
-        $this->db->delete('auth_tokens', $where);
287
-    }
288
-
289
-    //--------------------------------------------------------------------
290
-
291
-    /**
292
-     * Removes all persistent login tokens (RememberMe) for a single user
293
-     * across all devices they may have logged in with.
294
-     *
295
-     * @param $email
296
-     * @return mixed
297
-     */
298
-    public function purgeRememberTokens($email)
299
-    {
300
-        return $this->db->delete('auth_tokens', ['email' => $email]);
301
-    }
302
-
303
-    //--------------------------------------------------------------------
304
-
305
-
306
-    /**
307
-     * Purges the 'auth_tokens' table of any records that are too old
308
-     * to be of any use anymore. This equates to 1 week older than
309
-     * the remember_length set in the config file.
310
-     */
311
-    public function purgeOldRememberTokens()
312
-    {
313
-        if (! config_item('auth.allow_remembering'))
314
-        {
315
-            return;
316
-        }
317
-
318
-        $date = time() - config_item('auth.remember_length') - 604800; // 1 week
319
-        $date = date('Y-m-d 00:00:00', $date);
320
-
321
-        $this->db->where('created <=', $date)
322
-                 ->delete('auth_tokens');
323
-    }
324
-
325
-    //--------------------------------------------------------------------
326
-
327
-    /**
328
-     * Gets the timestamp of the last attempted login for this user.
329
-     *
330
-     * @param $ip_address
331
-     * @param $user_id
332
-     * @return int|null
333
-     */
334
-    public function lastLoginAttemptTime($ip_address, $user_id)
335
-    {
336
-        $query = $this->db->where('type', 'ip')
337
-                          ->where('ip_address', $ip_address)
338
-                          ->order_by('datetime', 'desc')
339
-                          ->limit(1)
340
-                          ->get('auth_login_attempts');
341
-
342
-        $last_ip = ! $query->num_rows() ? 0 : strtotime($query->row()->datetime);
343
-
344
-        if (! $user_id)
345
-        {
346
-            return $last_ip;
347
-        }
348
-
349
-        $query = $this->db->where('type', 'user')
350
-                          ->where('user_id', $user_id)
351
-                          ->order_by('datetime', 'desc')
352
-                          ->limit(1)
353
-                          ->get('auth_login_attempts');
354
-
355
-        $last_user = ! $query->num_rows() ? 0 : strtotime($query->row()->datetime);
356
-
357
-        return ($last_user > $last_ip) ? $last_user : $last_ip;
358
-    }
359
-
360
-    //--------------------------------------------------------------------
361
-
362
-    /**
363
-     * Returns the number of failed login attempts for a given type.
364
-     *
365
-     * @param $ip_address
366
-     * @param $user_id
367
-     * @return int
368
-     */
369
-    public function countLoginAttempts($ip_address, $user_id)
370
-    {
371
-        $count_ip = $this->db->where('type', 'ip')
372
-                             ->where('ip_address', $ip_address)
373
-                             ->count_all_results('auth_login_attempts');
374
-
375
-        if (! $user_id)
376
-        {
377
-            return $count_ip;
378
-        }
379
-
380
-        $count_user = $this->db->where('type', 'user')
381
-                               ->where('user_id', $user_id)
382
-                               ->count_all_results('auth_login_attempts');
383
-
384
-        return ($count_user > $count_ip) ? $count_user : $count_ip;
385
-    }
386
-
387
-    //--------------------------------------------------------------------
44
+	protected $table_name = 'auth_logins';
45
+
46
+	protected $set_created = false;
47
+	protected $set_modified = false;
48
+
49
+	//--------------------------------------------------------------------
50
+
51
+	//--------------------------------------------------------------------
52
+	// Login Attempts
53
+	//--------------------------------------------------------------------
54
+
55
+	/**
56
+	 * Records a login attempt. This is used to implement
57
+	 * throttling of application, ip address and user login attempts.
58
+	 *
59
+	 * @param $ip_address
60
+	 * @param $user_id
61
+	 * @return void
62
+	 */
63
+	public function recordLoginAttempt($ip_address, $user_id = null)
64
+	{
65
+		$datetime = date('Y-m-d H:i:s');
66
+
67
+		// log attempt for app
68
+		$data = [
69
+			'type' => 'app',
70
+			'datetime' => $datetime
71
+		];
72
+
73
+		$this->db->insert('auth_login_attempts', $data);
74
+
75
+		// log attempt for ip address
76
+		if (! empty($ip_address))
77
+		{
78
+			$data = [
79
+				'type' => 'ip',
80
+				'ip_address' => $ip_address,
81
+				'datetime' => $datetime
82
+			];
83
+
84
+			$this->db->insert('auth_login_attempts', $data);
85
+		}
86
+
87
+		// log attempt for user
88
+		if ($user_id)
89
+		{
90
+			$data = [
91
+				'type' => 'user',
92
+				'user_id' => $user_id,
93
+				'datetime' => $datetime
94
+			];
95
+
96
+			$this->db->insert('auth_login_attempts', $data);
97
+		}
98
+	}
99
+
100
+	//--------------------------------------------------------------------
101
+
102
+	/**
103
+	 * Purges all login attempt records from the database.
104
+	 *
105
+	 * @param $ip_address
106
+	 * @param $user_id
107
+	 * @return mixed
108
+	 */
109
+	public function purgeLoginAttempts($ip_address, $user_id)
110
+	{
111
+		return $this->db->where('ip_address', $ip_address)
112
+						->or_where('user_id', $user_id)
113
+						->delete('auth_login_attempts');
114
+	}
115
+
116
+	//--------------------------------------------------------------------
117
+
118
+	/**
119
+	 * Checks to see if how many login attempts have been attempted in the
120
+	 * last 60 seconds. If over 100, it is considered to be under a
121
+	 * brute force attempt.
122
+	 *
123
+	 * @param $ip_address
124
+	 * @param $user_id
125
+	 * @return bool
126
+	 */
127
+	public function isBruteForced($ip_address, $user_id)
128
+	{
129
+		$start_time = date('Y-m-d H:i:s', time() - 60);
130
+
131
+		$attempts_ip = $this->db->where('type', 'ip')
132
+								->where('ip_address', $ip_address)
133
+								->where('datetime >=', $start_time)
134
+								->count_all_results('auth_login_attempts');
135
+
136
+		if (! $user_id)
137
+		{
138
+			return $attempts_ip > 100;
139
+		}
140
+
141
+		$attempts_user = $this->db->where('type', 'user')
142
+								  ->where('user_id', $user_id)
143
+								  ->where('datetime >=', $start_time)
144
+								  ->count_all_results('auth_login_attempts');
145
+
146
+		if ($attempts_user > $attempts_ip) 
147
+		{
148
+			return $attempts_user > 100;
149
+		}
150
+		else
151
+		{
152
+			return $attempts_ip > 100;
153
+		}
154
+	}
155
+
156
+	//--------------------------------------------------------------------
157
+
158
+	/**
159
+	 * Attempts to determine if the system is under a distributed
160
+	 * brute force attack.
161
+	 *
162
+	 * To determine if we are in under a brute force attack, we first
163
+	 * find the average number of bad logins per day that never converted to
164
+	 * successful logins over the last 3 months. Then we compare
165
+	 * that to the the average number of logins in the past 24 hours.
166
+	 *
167
+	 * If the number of attempts in the last 24 hours is more than X (see config)
168
+	 * times the average, then institute additional throttling.
169
+	 *
170
+	 * @return int The time to add to any throttling.
171
+	 */
172
+	public function distributedBruteForceTime()
173
+	{
174
+		if (! $time = $this->cache->get('dbrutetime'))
175
+		{
176
+			$time = 0;
177
+
178
+			// Compute our daily average over the last 3 months.
179
+			$avg_start_time = date('Y-m-d 00:00:00', strtotime('-3 months'));
180
+
181
+			$query = $this->db->query("SELECT COUNT(*) / COUNT(DISTINCT DATE(`datetime`)) as num_rows FROM `auth_login_attempts` WHERE `type` = 'app' AND `datetime` >= ?", $avg_start_time);
182
+
183
+			if (! $query->num_rows())
184
+			{
185
+				$average = 0;
186
+			}
187
+			else
188
+			{
189
+				$average = $query->row()->num_rows;
190
+			}
191
+
192
+			// Get the total in the last 24 hours
193
+			$today_start_time = date('Y-m-d H:i:s', strtotime('-24 hours'));
194
+
195
+			$attempts = $this->db->where('type', 'app')
196
+								 ->where('datetime >=', $today_start_time)
197
+								 ->count_all_results('auth_login_attempts');
198
+
199
+			if ($attempts > (config_item('auth.dbrute_multiplier') * $average))
200
+			{
201
+				$time = config_item('auth.distributed_brute_add_time');
202
+			}
203
+
204
+			// Cache it for 3 hours.
205
+			$this->cache->save('dbrutetime', $time, 60*60*3);
206
+		}
207
+
208
+		return $time;
209
+	}
210
+
211
+	//--------------------------------------------------------------------
212
+
213
+	//--------------------------------------------------------------------
214
+	// Logins
215
+	//--------------------------------------------------------------------
216
+
217
+	/**
218
+	 * Records a successful login. This stores in a table so that a
219
+	 * history can be pulled up later if needed for security analyses.
220
+	 *
221
+	 * @param $user
222
+	 */
223
+	public function recordLogin($user)
224
+	{
225
+		$data = [
226
+			'user_id'    => (int)$user['id'],
227
+			'datetime'   => date('Y-m-d H:i:s'),
228
+			'ip_address' => $this->input->ip_address()
229
+		];
230
+
231
+		return $this->db->insert('auth_logins', $data);
232
+	}
233
+
234
+	//--------------------------------------------------------------------
235
+
236
+	//--------------------------------------------------------------------
237
+	// Tokens
238
+	//--------------------------------------------------------------------
239
+
240
+	/**
241
+	 * Generates a new token for the rememberme cookie.
242
+	 *
243
+	 * The token is based on the user's email address (since everyone will have one)
244
+	 * with the '@' turned to a '.', followed by a pipe (|) and a random 128-character
245
+	 * string with letters and numbers.
246
+	 *
247
+	 * @param $user
248
+	 * @return mixed
249
+	 */
250
+	public function generateRememberToken($user)
251
+	{
252
+		$this->load->helper('string');
253
+
254
+		return str_replace('@', '.', $user['email']) .'|' . random_string('alnum', 128);
255
+	}
256
+
257
+	//--------------------------------------------------------------------
258
+
259
+	/**
260
+	 * Hashes the token for the Remember Me Functionality.
261
+	 *
262
+	 * @param $token
263
+	 * @return string
264
+	 */
265
+	public function hashRememberToken($token)
266
+	{
267
+		return sha1(config_item('auth.salt') . $token);
268
+	}
269
+
270
+	//--------------------------------------------------------------------
271
+
272
+	/**
273
+	 * Deletes a single token that matches the email/token combo.
274
+	 *
275
+	 * @param $email
276
+	 * @param $token
277
+	 * @return mixed
278
+	 */
279
+	public function deleteRememberToken($email, $token)
280
+	{
281
+		$where = [
282
+			'email' => $email,
283
+			'hash'  => $this->hashRememberToken($token)
284
+		];
285
+
286
+		$this->db->delete('auth_tokens', $where);
287
+	}
288
+
289
+	//--------------------------------------------------------------------
290
+
291
+	/**
292
+	 * Removes all persistent login tokens (RememberMe) for a single user
293
+	 * across all devices they may have logged in with.
294
+	 *
295
+	 * @param $email
296
+	 * @return mixed
297
+	 */
298
+	public function purgeRememberTokens($email)
299
+	{
300
+		return $this->db->delete('auth_tokens', ['email' => $email]);
301
+	}
302
+
303
+	//--------------------------------------------------------------------
304
+
305
+
306
+	/**
307
+	 * Purges the 'auth_tokens' table of any records that are too old
308
+	 * to be of any use anymore. This equates to 1 week older than
309
+	 * the remember_length set in the config file.
310
+	 */
311
+	public function purgeOldRememberTokens()
312
+	{
313
+		if (! config_item('auth.allow_remembering'))
314
+		{
315
+			return;
316
+		}
317
+
318
+		$date = time() - config_item('auth.remember_length') - 604800; // 1 week
319
+		$date = date('Y-m-d 00:00:00', $date);
320
+
321
+		$this->db->where('created <=', $date)
322
+				 ->delete('auth_tokens');
323
+	}
324
+
325
+	//--------------------------------------------------------------------
326
+
327
+	/**
328
+	 * Gets the timestamp of the last attempted login for this user.
329
+	 *
330
+	 * @param $ip_address
331
+	 * @param $user_id
332
+	 * @return int|null
333
+	 */
334
+	public function lastLoginAttemptTime($ip_address, $user_id)
335
+	{
336
+		$query = $this->db->where('type', 'ip')
337
+						  ->where('ip_address', $ip_address)
338
+						  ->order_by('datetime', 'desc')
339
+						  ->limit(1)
340
+						  ->get('auth_login_attempts');
341
+
342
+		$last_ip = ! $query->num_rows() ? 0 : strtotime($query->row()->datetime);
343
+
344
+		if (! $user_id)
345
+		{
346
+			return $last_ip;
347
+		}
348
+
349
+		$query = $this->db->where('type', 'user')
350
+						  ->where('user_id', $user_id)
351
+						  ->order_by('datetime', 'desc')
352
+						  ->limit(1)
353
+						  ->get('auth_login_attempts');
354
+
355
+		$last_user = ! $query->num_rows() ? 0 : strtotime($query->row()->datetime);
356
+
357
+		return ($last_user > $last_ip) ? $last_user : $last_ip;
358
+	}
359
+
360
+	//--------------------------------------------------------------------
361
+
362
+	/**
363
+	 * Returns the number of failed login attempts for a given type.
364
+	 *
365
+	 * @param $ip_address
366
+	 * @param $user_id
367
+	 * @return int
368
+	 */
369
+	public function countLoginAttempts($ip_address, $user_id)
370
+	{
371
+		$count_ip = $this->db->where('type', 'ip')
372
+							 ->where('ip_address', $ip_address)
373
+							 ->count_all_results('auth_login_attempts');
374
+
375
+		if (! $user_id)
376
+		{
377
+			return $count_ip;
378
+		}
379
+
380
+		$count_user = $this->db->where('type', 'user')
381
+							   ->where('user_id', $user_id)
382
+							   ->count_all_results('auth_login_attempts');
383
+
384
+		return ($count_user > $count_ip) ? $count_user : $count_ip;
385
+	}
386
+
387
+	//--------------------------------------------------------------------
388 388
 
389 389
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -73,7 +73,7 @@  discard block
 block discarded – undo
73 73
         $this->db->insert('auth_login_attempts', $data);
74 74
 
75 75
         // log attempt for ip address
76
-        if (! empty($ip_address))
76
+        if ( ! empty($ip_address))
77 77
         {
78 78
             $data = [
79 79
                 'type' => 'ip',
@@ -133,7 +133,7 @@  discard block
 block discarded – undo
133 133
                                 ->where('datetime >=', $start_time)
134 134
                                 ->count_all_results('auth_login_attempts');
135 135
 
136
-        if (! $user_id)
136
+        if ( ! $user_id)
137 137
         {
138 138
             return $attempts_ip > 100;
139 139
         }
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
      */
172 172
     public function distributedBruteForceTime()
173 173
     {
174
-        if (! $time = $this->cache->get('dbrutetime'))
174
+        if ( ! $time = $this->cache->get('dbrutetime'))
175 175
         {
176 176
             $time = 0;
177 177
 
@@ -180,7 +180,7 @@  discard block
 block discarded – undo
180 180
 
181 181
             $query = $this->db->query("SELECT COUNT(*) / COUNT(DISTINCT DATE(`datetime`)) as num_rows FROM `auth_login_attempts` WHERE `type` = 'app' AND `datetime` >= ?", $avg_start_time);
182 182
 
183
-            if (! $query->num_rows())
183
+            if ( ! $query->num_rows())
184 184
             {
185 185
                 $average = 0;
186 186
             }
@@ -202,7 +202,7 @@  discard block
 block discarded – undo
202 202
             }
203 203
 
204 204
             // Cache it for 3 hours.
205
-            $this->cache->save('dbrutetime', $time, 60*60*3);
205
+            $this->cache->save('dbrutetime', $time, 60 * 60 * 3);
206 206
         }
207 207
 
208 208
         return $time;
@@ -223,7 +223,7 @@  discard block
 block discarded – undo
223 223
     public function recordLogin($user)
224 224
     {
225 225
         $data = [
226
-            'user_id'    => (int)$user['id'],
226
+            'user_id'    => (int) $user['id'],
227 227
             'datetime'   => date('Y-m-d H:i:s'),
228 228
             'ip_address' => $this->input->ip_address()
229 229
         ];
@@ -251,7 +251,7 @@  discard block
 block discarded – undo
251 251
     {
252 252
         $this->load->helper('string');
253 253
 
254
-        return str_replace('@', '.', $user['email']) .'|' . random_string('alnum', 128);
254
+        return str_replace('@', '.', $user['email']).'|'.random_string('alnum', 128);
255 255
     }
256 256
 
257 257
     //--------------------------------------------------------------------
@@ -264,7 +264,7 @@  discard block
 block discarded – undo
264 264
      */
265 265
     public function hashRememberToken($token)
266 266
     {
267
-        return sha1(config_item('auth.salt') . $token);
267
+        return sha1(config_item('auth.salt').$token);
268 268
     }
269 269
 
270 270
     //--------------------------------------------------------------------
@@ -310,7 +310,7 @@  discard block
 block discarded – undo
310 310
      */
311 311
     public function purgeOldRememberTokens()
312 312
     {
313
-        if (! config_item('auth.allow_remembering'))
313
+        if ( ! config_item('auth.allow_remembering'))
314 314
         {
315 315
             return;
316 316
         }
@@ -341,7 +341,7 @@  discard block
 block discarded – undo
341 341
 
342 342
         $last_ip = ! $query->num_rows() ? 0 : strtotime($query->row()->datetime);
343 343
 
344
-        if (! $user_id)
344
+        if ( ! $user_id)
345 345
         {
346 346
             return $last_ip;
347 347
         }
@@ -372,7 +372,7 @@  discard block
 block discarded – undo
372 372
                              ->where('ip_address', $ip_address)
373 373
                              ->count_all_results('auth_login_attempts');
374 374
 
375
-        if (! $user_id)
375
+        if ( ! $user_id)
376 376
         {
377 377
             return $count_ip;
378 378
         }
Please login to merge, or discard this patch.
Braces   +2 added lines, -4 removed lines patch added patch discarded remove patch
@@ -146,8 +146,7 @@  discard block
 block discarded – undo
146 146
         if ($attempts_user > $attempts_ip) 
147 147
         {
148 148
             return $attempts_user > 100;
149
-        }
150
-        else
149
+        } else
151 150
         {
152 151
             return $attempts_ip > 100;
153 152
         }
@@ -183,8 +182,7 @@  discard block
 block discarded – undo
183 182
             if (! $query->num_rows())
184 183
             {
185 184
                 $average = 0;
186
-            }
187
-            else
185
+            } else
188 186
             {
189 187
                 $average = $query->row()->num_rows;
190 188
             }
Please login to merge, or discard this patch.
application/database/migrations/20160111121139_UpdateLoginAttemptsTable.php 1 patch
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -10,57 +10,57 @@
 block discarded – undo
10 10
  */
11 11
 class Migration_UpdateLoginAttemmptsTable extends CI_Migration {
12 12
 
13
-    public function up()
14
-    {
15
-        $fields = array(
16
-            'type' => array(
17
-                'type'       => 'varchar',
18
-                'constraint' => 64,
19
-                'null'       => false,
20
-                'default'    => 'app',
21
-                'after'      => 'id'
22
-            ),
23
-            'ip_address' => array(
24
-                'type'       => 'varchar',
25
-                'constraint' => 255,
26
-                'null'       => true,
27
-                'after'      => 'type'
28
-            ),
29
-            'user_id' => array(
30
-                'type'       => 'int',
31
-                'constraint' => 11,
32
-                'unsigned'   => true,
33
-                'null'       => true,
34
-                'after'      => 'ip_address'
35
-            )
36
-        );
13
+	public function up()
14
+	{
15
+		$fields = array(
16
+			'type' => array(
17
+				'type'       => 'varchar',
18
+				'constraint' => 64,
19
+				'null'       => false,
20
+				'default'    => 'app',
21
+				'after'      => 'id'
22
+			),
23
+			'ip_address' => array(
24
+				'type'       => 'varchar',
25
+				'constraint' => 255,
26
+				'null'       => true,
27
+				'after'      => 'type'
28
+			),
29
+			'user_id' => array(
30
+				'type'       => 'int',
31
+				'constraint' => 11,
32
+				'unsigned'   => true,
33
+				'null'       => true,
34
+				'after'      => 'ip_address'
35
+			)
36
+		);
37 37
 
38
-        $this->dbforge->add_column('auth_login_attempts', $fields);
38
+		$this->dbforge->add_column('auth_login_attempts', $fields);
39 39
 
40
-        $this->db->query('ALTER TABLE `auth_login_attempts` ADD KEY (`user_id`)');
40
+		$this->db->query('ALTER TABLE `auth_login_attempts` ADD KEY (`user_id`)');
41 41
 
42
-        $this->dbforge->drop_column('auth_login_attempts', 'email');
43
-    }
42
+		$this->dbforge->drop_column('auth_login_attempts', 'email');
43
+	}
44 44
 
45
-    //--------------------------------------------------------------------
45
+	//--------------------------------------------------------------------
46 46
 
47
-    public function down()
48
-    {
49
-        $this->dbforge->drop_column('auth_login_attempts', 'type');
50
-        $this->dbforge->drop_column('auth_login_attempts', 'ip_address');
51
-        $this->dbforge->drop_column('auth_login_attempts', 'user_id');
47
+	public function down()
48
+	{
49
+		$this->dbforge->drop_column('auth_login_attempts', 'type');
50
+		$this->dbforge->drop_column('auth_login_attempts', 'ip_address');
51
+		$this->dbforge->drop_column('auth_login_attempts', 'user_id');
52 52
 
53
-        $column = ['email' => [
54
-            'type'       => 'varchar',
55
-            'constraint' => 255,
56
-            'after'      => 'id'
57
-        ]];
53
+		$column = ['email' => [
54
+			'type'       => 'varchar',
55
+			'constraint' => 255,
56
+			'after'      => 'id'
57
+		]];
58 58
 
59
-        $this->dbforge->add_column('auth_login_attempts', $column);
59
+		$this->dbforge->add_column('auth_login_attempts', $column);
60 60
 
61
-        $this->db->query('ALTER TABLE `auth_login_attempts` ADD KEY (`email`)');
62
-    }
61
+		$this->db->query('ALTER TABLE `auth_login_attempts` ADD KEY (`email`)');
62
+	}
63 63
 
64
-    //--------------------------------------------------------------------
64
+	//--------------------------------------------------------------------
65 65
 
66 66
 }
67 67
\ No newline at end of file
Please login to merge, or discard this patch.