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