· 8 years ago · Jul 16, 2018, 06:54 AM
1<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3* Name: Ion Auth Model
4*
5* Version: 2.5.2
6*
7* Author: Ben Edmunds
8* ben.edmunds@gmail.com
9* @benedmunds
10*
11* Added Awesomeness: Phil Sturgeon
12*
13* Location: http://github.com/benedmunds/CodeIgniter-Ion-Auth
14*
15* Created: 10.01.2009
16*
17* Last Change: 3.22.13
18*
19* Changelog:
20* * 3-22-13 - Additional entropy added - 52aa456eef8b60ad6754b31fbdcc77bb
21*
22* Description: Modified auth system based on redux_auth with extensive customization. This is basically what Redux Auth 2 should be.
23* Original Author name has been kept but that does not mean that the method has not been modified.
24*
25* Requirements: PHP5 or above
26*
27*/
28
29class Ion_auth_model extends CI_Model
30{
31 /**
32 * Holds an array of tables used
33 *
34 * @var array
35 **/
36 public $tables = array();
37
38 /**
39 * activation code
40 *
41 * @var string
42 **/
43 public $activation_code;
44
45 /**
46 * forgotten password key
47 *
48 * @var string
49 **/
50 public $forgotten_password_code;
51
52 /**
53 * new password
54 *
55 * @var string
56 **/
57 public $new_password;
58
59 /**
60 * Identity
61 *
62 * @var string
63 **/
64 public $identity;
65
66 /**
67 * Where
68 *
69 * @var array
70 **/
71 public $_ion_where = array();
72
73 /**
74 * Select
75 *
76 * @var array
77 **/
78 public $_ion_select = array();
79
80 /**
81 * Like
82 *
83 * @var array
84 **/
85 public $_ion_like = array();
86
87 /**
88 * Limit
89 *
90 * @var string
91 **/
92 public $_ion_limit = NULL;
93
94 /**
95 * Offset
96 *
97 * @var string
98 **/
99 public $_ion_offset = NULL;
100
101 /**
102 * Order By
103 *
104 * @var string
105 **/
106 public $_ion_order_by = NULL;
107
108 /**
109 * Order
110 *
111 * @var string
112 **/
113 public $_ion_order = NULL;
114
115 /**
116 * Hooks
117 *
118 * @var object
119 **/
120 protected $_ion_hooks;
121
122 /**
123 * Response
124 *
125 * @var string
126 **/
127 protected $response = NULL;
128
129 /**
130 * message (uses lang file)
131 *
132 * @var string
133 **/
134 protected $messages;
135
136 /**
137 * error message (uses lang file)
138 *
139 * @var string
140 **/
141 protected $errors;
142
143 /**
144 * error start delimiter
145 *
146 * @var string
147 **/
148 protected $error_start_delimiter;
149
150 /**
151 * error end delimiter
152 *
153 * @var string
154 **/
155 protected $error_end_delimiter;
156
157 /**
158 * caching of users and their groups
159 *
160 * @var array
161 **/
162 public $_cache_user_in_group = array();
163
164 /**
165 * caching of groups
166 *
167 * @var array
168 **/
169 protected $_cache_groups = array();
170
171 public function __construct()
172 {
173 parent::__construct();
174 $this->load->database();
175 $this->load->config('ion_auth', TRUE);
176 $this->load->helper('cookie');
177 $this->load->helper('date');
178 $this->lang->load('ion_auth');
179
180 // initialize db tables data
181 $this->tables = $this->config->item('tables', 'ion_auth');
182
183 //initialize data
184 $this->identity_column = $this->config->item('identity', 'ion_auth');
185 $this->store_salt = $this->config->item('store_salt', 'ion_auth');
186 $this->salt_length = $this->config->item('salt_length', 'ion_auth');
187 $this->join = $this->config->item('join', 'ion_auth');
188
189
190 // initialize hash method options (Bcrypt)
191 $this->hash_method = $this->config->item('hash_method', 'ion_auth');
192 $this->default_rounds = $this->config->item('default_rounds', 'ion_auth');
193 $this->random_rounds = $this->config->item('random_rounds', 'ion_auth');
194 $this->min_rounds = $this->config->item('min_rounds', 'ion_auth');
195 $this->max_rounds = $this->config->item('max_rounds', 'ion_auth');
196
197
198 // initialize messages and error
199 $this->messages = array();
200 $this->errors = array();
201 $delimiters_source = $this->config->item('delimiters_source', 'ion_auth');
202
203 // load the error delimeters either from the config file or use what's been supplied to form validation
204 if ($delimiters_source === 'form_validation')
205 {
206 // load in delimiters from form_validation
207 // to keep this simple we'll load the value using reflection since these properties are protected
208 $this->load->library('form_validation');
209 $form_validation_class = new ReflectionClass("CI_Form_validation");
210
211 $error_prefix = $form_validation_class->getProperty("_error_prefix");
212 $error_prefix->setAccessible(TRUE);
213 $this->error_start_delimiter = $error_prefix->getValue($this->form_validation);
214 $this->message_start_delimiter = $this->error_start_delimiter;
215
216 $error_suffix = $form_validation_class->getProperty("_error_suffix");
217 $error_suffix->setAccessible(TRUE);
218 $this->error_end_delimiter = $error_suffix->getValue($this->form_validation);
219 $this->message_end_delimiter = $this->error_end_delimiter;
220 }
221 else
222 {
223 // use delimiters from config
224 $this->message_start_delimiter = $this->config->item('message_start_delimiter', 'ion_auth');
225 $this->message_end_delimiter = $this->config->item('message_end_delimiter', 'ion_auth');
226 $this->error_start_delimiter = $this->config->item('error_start_delimiter', 'ion_auth');
227 $this->error_end_delimiter = $this->config->item('error_end_delimiter', 'ion_auth');
228 }
229
230
231 // initialize our hooks object
232 $this->_ion_hooks = new stdClass;
233
234 // load the bcrypt class if needed
235 if ($this->hash_method == 'bcrypt') {
236 if ($this->random_rounds)
237 {
238 $rand = rand($this->min_rounds,$this->max_rounds);
239 $params = array('rounds' => $rand);
240 }
241 else
242 {
243 $params = array('rounds' => $this->default_rounds);
244 }
245
246 $params['salt_prefix'] = $this->config->item('salt_prefix', 'ion_auth');
247 $this->load->library('bcrypt',$params);
248 }
249
250 $this->trigger_events('model_constructor');
251 }
252
253 /**
254 * Misc functions
255 *
256 * Hash password : Hashes the password to be stored in the database.
257 * Hash password db : This function takes a password and validates it
258 * against an entry in the users table.
259 * Salt : Generates a random salt value.
260 *
261 * @author Mathew
262 */
263
264 /**
265 * Hashes the password to be stored in the database.
266 *
267 * @return void
268 * @author Mathew
269 **/
270 public function hash_password($password, $salt=false, $use_sha1_override=FALSE)
271 {
272 if (empty($password))
273 {
274 return FALSE;
275 }
276
277 // bcrypt
278 if ($use_sha1_override === FALSE && $this->hash_method == 'bcrypt')
279 {
280 return $this->bcrypt->hash($password);
281 }
282
283
284 if ($this->store_salt && $salt)
285 {
286 return sha1($password . $salt);
287 }
288 else
289 {
290 $salt = $this->salt();
291 return $salt . substr(sha1($salt . $password), 0, -$this->salt_length);
292 }
293 }
294
295 /**
296 * This function takes a password and validates it
297 * against an entry in the users table.
298 *
299 * @return void
300 * @author Mathew
301 **/
302 public function hash_password_db($id, $password, $use_sha1_override=FALSE)
303 {
304 if (empty($id) || empty($password))
305 {
306 return FALSE;
307 }
308
309 $this->trigger_events('extra_where');
310
311 $query = $this->db->select('password, salt')
312 ->where('id', $id)
313 ->limit(1)
314 ->order_by('id', 'desc')
315 ->get($this->tables['users']);
316
317 $hash_password_db = $query->row();
318
319 if ($query->num_rows() !== 1)
320 {
321 return FALSE;
322 }
323
324 // bcrypt
325 if ($use_sha1_override === FALSE && $this->hash_method == 'bcrypt')
326 {
327 if ($this->bcrypt->verify($password,$hash_password_db->password))
328 {
329 return TRUE;
330 }
331
332 return FALSE;
333 }
334
335 // sha1
336 if ($this->store_salt)
337 {
338 $db_password = sha1($password . $hash_password_db->salt);
339 }
340 else
341 {
342 $salt = substr($hash_password_db->password, 0, $this->salt_length);
343
344 $db_password = $salt . substr(sha1($salt . $password), 0, -$this->salt_length);
345 }
346
347 if($db_password == $hash_password_db->password)
348 {
349 return TRUE;
350 }
351 else
352 {
353 return FALSE;
354 }
355 }
356
357 /**
358 * Generates a random salt value for forgotten passwords or any other keys. Uses SHA1.
359 *
360 * @return void
361 * @author Mathew
362 **/
363 public function hash_code($password)
364 {
365 return $this->hash_password($password, FALSE, TRUE);
366 }
367
368 /**
369 * Generates a random salt value.
370 *
371 * Salt generation code taken from https://github.com/ircmaxell/password_compat/blob/master/lib/password.php
372 *
373 * @return void
374 * @author Anthony Ferrera
375 **/
376 public function salt()
377 {
378
379 $raw_salt_len = 16;
380
381 $buffer = '';
382 $buffer_valid = false;
383
384 if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
385 $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
386 if ($buffer) {
387 $buffer_valid = true;
388 }
389 }
390
391 if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
392 $buffer = openssl_random_pseudo_bytes($raw_salt_len);
393 if ($buffer) {
394 $buffer_valid = true;
395 }
396 }
397
398 if (!$buffer_valid && @is_readable('/dev/urandom')) {
399 $f = fopen('/dev/urandom', 'r');
400 $read = strlen($buffer);
401 while ($read < $raw_salt_len) {
402 $buffer .= fread($f, $raw_salt_len - $read);
403 $read = strlen($buffer);
404 }
405 fclose($f);
406 if ($read >= $raw_salt_len) {
407 $buffer_valid = true;
408 }
409 }
410
411 if (!$buffer_valid || strlen($buffer) < $raw_salt_len) {
412 $bl = strlen($buffer);
413 for ($i = 0; $i < $raw_salt_len; $i++) {
414 if ($i < $bl) {
415 $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
416 } else {
417 $buffer .= chr(mt_rand(0, 255));
418 }
419 }
420 }
421
422 $salt = $buffer;
423
424 // encode string with the Base64 variant used by crypt
425 $base64_digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
426 $bcrypt64_digits = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
427 $base64_string = base64_encode($salt);
428 $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
429
430 $salt = substr($salt, 0, $this->salt_length);
431
432
433 return $salt;
434
435 }
436
437 /**
438 * Activation functions
439 *
440 * Activate : Validates and removes activation code.
441 * Deactivae : Updates a users row with an activation code.
442 *
443 * @author Mathew
444 */
445
446 /**
447 * activate
448 *
449 * @return void
450 * @author Mathew
451 **/
452 public function activate($id, $code = false)
453 {
454 $this->trigger_events('pre_activate');
455
456 if ($code !== FALSE)
457 {
458 $query = $this->db->select($this->identity_column)
459 ->where('activation_code', $code)
460 ->where('id', $id)
461 ->limit(1)
462 ->order_by('id', 'desc')
463 ->get($this->tables['users']);
464
465 $result = $query->row();
466
467 if ($query->num_rows() !== 1)
468 {
469 $this->trigger_events(array('post_activate', 'post_activate_unsuccessful'));
470 $this->set_error('activate_unsuccessful');
471 return FALSE;
472 }
473
474 $data = array(
475 'activation_code' => NULL,
476 'active' => 1
477 );
478
479 $this->trigger_events('extra_where');
480 $this->db->update($this->tables['users'], $data, array('id' => $id));
481 }
482 else
483 {
484 $data = array(
485 'activation_code' => NULL,
486 'active' => 1
487 );
488
489
490 $this->trigger_events('extra_where');
491 $this->db->update($this->tables['users'], $data, array('id' => $id));
492 }
493
494
495 $return = $this->db->affected_rows() == 1;
496 if ($return)
497 {
498 $this->trigger_events(array('post_activate', 'post_activate_successful'));
499 $this->set_message('activate_successful');
500 }
501 else
502 {
503 $this->trigger_events(array('post_activate', 'post_activate_unsuccessful'));
504 $this->set_error('activate_unsuccessful');
505 }
506
507
508 return $return;
509 }
510
511
512 /**
513 * Deactivate
514 *
515 * @return void
516 * @author Mathew
517 **/
518 public function deactivate($id = NULL)
519 {
520 $this->trigger_events('deactivate');
521
522 if (!isset($id))
523 {
524 $this->set_error('deactivate_unsuccessful');
525 return FALSE;
526 }
527
528 $activation_code = sha1(md5(microtime()));
529 $this->activation_code = $activation_code;
530
531 $data = array(
532 'activation_code' => $activation_code,
533 'active' => 0
534 );
535
536 $this->trigger_events('extra_where');
537 $this->db->update($this->tables['users'], $data, array('id' => $id));
538
539 $return = $this->db->affected_rows() == 1;
540 if ($return)
541 $this->set_message('deactivate_successful');
542 else
543 $this->set_error('deactivate_unsuccessful');
544
545 return $return;
546 }
547
548 public function clear_forgotten_password_code($code) {
549
550 if (empty($code))
551 {
552 return FALSE;
553 }
554
555 $this->db->where('forgotten_password_code', $code);
556
557 if ($this->db->count_all_results($this->tables['users']) > 0)
558 {
559 $data = array(
560 'forgotten_password_code' => NULL,
561 'forgotten_password_time' => NULL
562 );
563
564 $this->db->update($this->tables['users'], $data, array('forgotten_password_code' => $code));
565
566 return TRUE;
567 }
568
569 return FALSE;
570 }
571
572 /**
573 * reset password
574 *
575 * @return bool
576 * @author Mathew
577 **/
578 public function reset_password($identity, $new) {
579 $this->trigger_events('pre_change_password');
580
581 if (!$this->identity_check($identity)) {
582 $this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
583 return FALSE;
584 }
585
586 $this->trigger_events('extra_where');
587
588 $query = $this->db->select('id, password, salt')
589 ->where($this->identity_column, $identity)
590 ->limit(1)
591 ->order_by('id', 'desc')
592 ->get($this->tables['users']);
593
594 if ($query->num_rows() !== 1)
595 {
596 $this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
597 $this->set_error('password_change_unsuccessful');
598 return FALSE;
599 }
600
601 $result = $query->row();
602
603 $new = $this->hash_password($new, $result->salt);
604
605 // store the new password and reset the remember code so all remembered instances have to re-login
606 // also clear the forgotten password code
607 $data = array(
608 'password' => $new,
609 'remember_code' => NULL,
610 'forgotten_password_code' => NULL,
611 'forgotten_password_time' => NULL,
612 );
613
614 $this->trigger_events('extra_where');
615 $this->db->update($this->tables['users'], $data, array($this->identity_column => $identity));
616
617 $return = $this->db->affected_rows() == 1;
618 if ($return)
619 {
620 $this->trigger_events(array('post_change_password', 'post_change_password_successful'));
621 $this->set_message('password_change_successful');
622 }
623 else
624 {
625 $this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
626 $this->set_error('password_change_unsuccessful');
627 }
628
629 return $return;
630 }
631
632 /**
633 * change password
634 *
635 * @return bool
636 * @author Mathew
637 **/
638 public function change_password($identity, $old, $new)
639 {
640 $this->trigger_events('pre_change_password');
641
642 $this->trigger_events('extra_where');
643
644 $query = $this->db->select('id, password, salt')
645 ->where($this->identity_column, $identity)
646 ->limit(1)
647 ->order_by('id', 'desc')
648 ->get($this->tables['users']);
649
650 if ($query->num_rows() !== 1)
651 {
652 $this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
653 $this->set_error('password_change_unsuccessful');
654 return FALSE;
655 }
656
657 $user = $query->row();
658
659 $old_password_matches = $this->hash_password_db($user->id, $old);
660
661 if ($old_password_matches === TRUE)
662 {
663 // store the new password and reset the remember code so all remembered instances have to re-login
664 $hashed_new_password = $this->hash_password($new, $user->salt);
665 $data = array(
666 'password' => $hashed_new_password,
667 'remember_code' => NULL,
668 );
669
670 $this->trigger_events('extra_where');
671
672 $successfully_changed_password_in_db = $this->db->update($this->tables['users'], $data, array($this->identity_column => $identity));
673 if ($successfully_changed_password_in_db)
674 {
675 $this->trigger_events(array('post_change_password', 'post_change_password_successful'));
676 $this->set_message('password_change_successful');
677 }
678 else
679 {
680 $this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
681 $this->set_error('password_change_unsuccessful');
682 }
683
684 return $successfully_changed_password_in_db;
685 }
686
687 $this->set_error('password_change_unsuccessful');
688 return FALSE;
689 }
690
691 /**
692 * Checks username
693 *
694 * @return bool
695 * @author Mathew
696 **/
697 public function username_check($username = '')
698 {
699 $this->trigger_events('username_check');
700
701 if (empty($username))
702 {
703 return FALSE;
704 }
705
706 $this->trigger_events('extra_where');
707
708 return $this->db->where('username', $username)
709 ->group_by("id")
710 ->order_by("id", "ASC")
711 ->limit(1)
712 ->count_all_results($this->tables['users']) > 0;
713 }
714
715 /**
716 * Checks email
717 *
718 * @return bool
719 * @author Mathew
720 **/
721 public function email_check($email = '')
722 {
723 $this->trigger_events('email_check');
724
725 if (empty($email))
726 {
727 return FALSE;
728 }
729
730 $this->trigger_events('extra_where');
731
732 return $this->db->where('email', $email)
733 ->group_by("id")
734 ->order_by("id", "ASC")
735 ->limit(1)
736 ->count_all_results($this->tables['users']) > 0;
737 }
738
739 /**
740 * Identity check
741 *
742 * @return bool
743 * @author Mathew
744 **/
745 public function identity_check($identity = '')
746 {
747 $this->trigger_events('identity_check');
748
749 if (empty($identity))
750 {
751 return FALSE;
752 }
753
754 return $this->db->where($this->identity_column, $identity)
755 ->count_all_results($this->tables['users']) > 0;
756 }
757
758 /**
759 * Insert a forgotten password key.
760 *
761 * @return bool
762 * @author Mathew
763 * @updated Ryan
764 * @updated 52aa456eef8b60ad6754b31fbdcc77bb
765 **/
766 public function forgotten_password($identity)
767 {
768 if (empty($identity))
769 {
770 $this->trigger_events(array('post_forgotten_password', 'post_forgotten_password_unsuccessful'));
771 return FALSE;
772 }
773
774 // All some more randomness
775 $activation_code_part = "";
776 if(function_exists("openssl_random_pseudo_bytes")) {
777 $activation_code_part = openssl_random_pseudo_bytes(128);
778 }
779
780 for($i=0;$i<1024;$i++) {
781 $activation_code_part = sha1($activation_code_part . mt_rand() . microtime());
782 }
783
784 $key = $this->hash_code($activation_code_part.$identity);
785
786 // If enable query strings is set, then we need to replace any unsafe characters so that the code can still work
787 if ($key != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
788 {
789 // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
790 // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
791 if ( ! preg_match("|^[".str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-'))."]+$|i", $key))
792 {
793 $key = preg_replace("/[^".$this->config->item('permitted_uri_chars')."]+/i", "-", $key);
794 }
795 }
796
797 $this->forgotten_password_code = $key;
798
799 $this->trigger_events('extra_where');
800
801 $update = array(
802 'forgotten_password_code' => $key,
803 'forgotten_password_time' => time()
804 );
805
806 $this->db->update($this->tables['users'], $update, array($this->identity_column => $identity));
807
808 $return = $this->db->affected_rows() == 1;
809
810 if ($return)
811 $this->trigger_events(array('post_forgotten_password', 'post_forgotten_password_successful'));
812 else
813 $this->trigger_events(array('post_forgotten_password', 'post_forgotten_password_unsuccessful'));
814
815 return $return;
816 }
817
818 /**
819 * Forgotten Password Complete
820 *
821 * @return string
822 * @author Mathew
823 **/
824 public function forgotten_password_complete($code, $salt=FALSE)
825 {
826 $this->trigger_events('pre_forgotten_password_complete');
827
828 if (empty($code))
829 {
830 $this->trigger_events(array('post_forgotten_password_complete', 'post_forgotten_password_complete_unsuccessful'));
831 return FALSE;
832 }
833
834 $profile = $this->where('forgotten_password_code', $code)->users()->row(); //pass the code to profile
835
836 if ($profile) {
837
838 if ($this->config->item('forgot_password_expiration', 'ion_auth') > 0) {
839 //Make sure it isn't expired
840 $expiration = $this->config->item('forgot_password_expiration', 'ion_auth');
841 if (time() - $profile->forgotten_password_time > $expiration) {
842 //it has expired
843 $this->set_error('forgot_password_expired');
844 $this->trigger_events(array('post_forgotten_password_complete', 'post_forgotten_password_complete_unsuccessful'));
845 return FALSE;
846 }
847 }
848
849 $password = $this->salt();
850
851 $data = array(
852 'password' => $this->hash_password($password, $salt),
853 'forgotten_password_code' => NULL,
854 'active' => 1,
855 );
856
857 $this->db->update($this->tables['users'], $data, array('forgotten_password_code' => $code));
858
859 $this->trigger_events(array('post_forgotten_password_complete', 'post_forgotten_password_complete_successful'));
860 return $password;
861 }
862
863 $this->trigger_events(array('post_forgotten_password_complete', 'post_forgotten_password_complete_unsuccessful'));
864 return FALSE;
865 }
866
867 /**
868 * register
869 *
870 * @return bool
871 * @author Mathew
872 **/
873 public function register($identity, $password, $email, $additional_data = array(), $groups = array())
874 {
875 $this->trigger_events('pre_register');
876
877 $manual_activation = $this->config->item('manual_activation', 'ion_auth');
878
879 if ($this->identity_check($identity))
880 {
881 $this->set_error('account_creation_duplicate_identity');
882 return FALSE;
883 }
884 elseif ( !$this->config->item('default_group', 'ion_auth') && empty($groups) )
885 {
886 $this->set_error('account_creation_missing_default_group');
887 return FALSE;
888 }
889
890 // check if the default set in config exists in database
891 $query = $this->db->get_where($this->tables['groups'],array('name' => $this->config->item('default_group', 'ion_auth')),1)->row();
892 if( !isset($query->id) && empty($groups) )
893 {
894 $this->set_error('account_creation_invalid_default_group');
895 return FALSE;
896 }
897
898 // capture default group details
899 $default_group = $query;
900
901 // IP Address
902 $ip_address = $this->_prepare_ip($this->input->ip_address());
903 $salt = $this->store_salt ? $this->salt() : FALSE;
904 $password = $this->hash_password($password, $salt);
905
906 // Users table.
907 $data = array(
908 $this->identity_column => $identity,
909 'password' => $password,
910 'email' => $email,
911 'ip_address' => $ip_address,
912 'created_on' => time(),
913 'active' => ($manual_activation === false ? 1 : 0)
914 );
915
916 if ($this->store_salt)
917 {
918 $data['salt'] = $salt;
919 }
920
921 // filter out any data passed that doesnt have a matching column in the users table
922 // and merge the set user data and the additional data
923 $user_data = array_merge($this->_filter_data($this->tables['users'], $additional_data), $data);
924
925 $this->trigger_events('extra_set');
926
927 $this->db->insert($this->tables['users'], $user_data);
928
929 $id = $this->db->insert_id();
930
931 // add in groups array if it doesn't exits and stop adding into default group if default group ids are set
932 if( isset($default_group->id) && empty($groups) )
933 {
934 $groups[] = $default_group->id;
935 }
936
937 if (!empty($groups))
938 {
939 // add to groups
940 foreach ($groups as $group)
941 {
942 $this->add_to_group($group, $id);
943 }
944 }
945
946 $this->trigger_events('post_register');
947
948 return (isset($id)) ? $id : FALSE;
949 }
950
951 /**
952 * login
953 *
954 * @return bool
955 * @author Mathew
956 **/
957 public function login($identity, $password, $remember=FALSE)
958 {
959 $this->trigger_events('pre_login');
960
961 if (empty($identity) || empty($password))
962 {
963 $this->set_error('login_unsuccessful');
964 return FALSE;
965 }
966
967 $this->trigger_events('extra_where');
968
969 $query = $this->db->select($this->identity_column . ', email, id, password, active, last_login')
970 ->where($this->identity_column, $identity)
971 ->limit(1)
972 ->order_by('id', 'desc')
973 ->get($this->tables['users']);
974
975 if($this->is_time_locked_out($identity))
976 {
977 // Hash something anyway, just to take up time
978 $this->hash_password($password);
979
980 $this->trigger_events('post_login_unsuccessful');
981 $this->set_error('login_timeout');
982
983 return FALSE;
984 }
985
986 if ($query->num_rows() === 1)
987 {
988 $user = $query->row();
989
990 $password = $this->hash_password_db($user->id, $password);
991
992 if ($password === TRUE)
993 {
994 if ($user->active == 0)
995 {
996 $this->trigger_events('post_login_unsuccessful');
997 $this->set_error('login_unsuccessful_not_active');
998
999 return FALSE;
1000 }
1001
1002 $this->set_session($user);
1003
1004 $this->update_last_login($user->id);
1005
1006 $this->clear_login_attempts($identity);
1007
1008 if ($remember && $this->config->item('remember_users', 'ion_auth'))
1009 {
1010 $this->remember_user($user->id);
1011 }
1012
1013 $this->trigger_events(array('post_login', 'post_login_successful'));
1014 $this->set_message('login_successful');
1015
1016 return TRUE;
1017 }
1018 }
1019
1020 // Hash something anyway, just to take up time
1021 $this->hash_password($password);
1022
1023 $this->increase_login_attempts($identity);
1024
1025 $this->trigger_events('post_login_unsuccessful');
1026 $this->set_error('login_unsuccessful');
1027
1028 return FALSE;
1029 }
1030
1031 /**
1032 * is_max_login_attempts_exceeded
1033 * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
1034 *
1035 * @param string $identity
1036 * @return boolean
1037 **/
1038 public function is_max_login_attempts_exceeded($identity) {
1039 if ($this->config->item('track_login_attempts', 'ion_auth')) {
1040 $max_attempts = $this->config->item('maximum_login_attempts', 'ion_auth');
1041 if ($max_attempts > 0) {
1042 $attempts = $this->get_attempts_num($identity);
1043 return $attempts >= $max_attempts;
1044 }
1045 }
1046 return FALSE;
1047 }
1048
1049 /**
1050 * Get number of attempts to login occured from given IP-address or identity
1051 * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
1052 *
1053 * @param string $identity
1054 * @return int
1055 */
1056 function get_attempts_num($identity)
1057 {
1058 if ($this->config->item('track_login_attempts', 'ion_auth')) {
1059 $ip_address = $this->_prepare_ip($this->input->ip_address());
1060 $this->db->select('1', FALSE);
1061 if ($this->config->item('track_login_ip_address', 'ion_auth')) {
1062 $this->db->where('ip_address', $ip_address);
1063 $this->db->where('login', $identity);
1064 } else if (strlen($identity) > 0) $this->db->or_where('login', $identity);
1065 $qres = $this->db->get($this->tables['login_attempts']);
1066 return $qres->num_rows();
1067 }
1068 return 0;
1069 }
1070 /**
1071 * Get a boolean to determine if an account should be locked out due to
1072 * exceeded login attempts within a given period
1073 *
1074 * @return boolean
1075 */
1076 public function is_time_locked_out($identity) {
1077
1078 return $this->is_max_login_attempts_exceeded($identity) && $this->get_last_attempt_time($identity) > time() - $this->config->item('lockout_time', 'ion_auth');
1079 }
1080
1081 /**
1082 * Get the time of the last time a login attempt occured from given IP-address or identity
1083 *
1084 * @param string $identity
1085 * @return int
1086 */
1087 public function get_last_attempt_time($identity) {
1088 if ($this->config->item('track_login_attempts', 'ion_auth')) {
1089 $ip_address = $this->_prepare_ip($this->input->ip_address());
1090
1091 $this->db->select_max('time');
1092 if ($this->config->item('track_login_ip_address', 'ion_auth')) $this->db->where('ip_address', $ip_address);
1093 else if (strlen($identity) > 0) $this->db->or_where('login', $identity);
1094 $qres = $this->db->get($this->tables['login_attempts'], 1);
1095
1096 if($qres->num_rows() > 0) {
1097 return $qres->row()->time;
1098 }
1099 }
1100
1101 return 0;
1102 }
1103
1104 /**
1105 * increase_login_attempts
1106 * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
1107 *
1108 * @param string $identity
1109 **/
1110 public function increase_login_attempts($identity) {
1111 if ($this->config->item('track_login_attempts', 'ion_auth')) {
1112 $ip_address = $this->_prepare_ip($this->input->ip_address());
1113 return $this->db->insert($this->tables['login_attempts'], array('ip_address' => $ip_address, 'login' => $identity, 'time' => time()));
1114 }
1115 return FALSE;
1116 }
1117
1118 /**
1119 * clear_login_attempts
1120 * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
1121 *
1122 * @param string $identity
1123 **/
1124 public function clear_login_attempts($identity, $expire_period = 86400) {
1125 if ($this->config->item('track_login_attempts', 'ion_auth')) {
1126 $ip_address = $this->_prepare_ip($this->input->ip_address());
1127
1128 $this->db->where(array('ip_address' => $ip_address, 'login' => $identity));
1129 // Purge obsolete login attempts
1130 $this->db->or_where('time <', time() - $expire_period, FALSE);
1131
1132 return $this->db->delete($this->tables['login_attempts']);
1133 }
1134 return FALSE;
1135 }
1136
1137 public function limit($limit)
1138 {
1139 $this->trigger_events('limit');
1140 $this->_ion_limit = $limit;
1141
1142 return $this;
1143 }
1144
1145 public function offset($offset)
1146 {
1147 $this->trigger_events('offset');
1148 $this->_ion_offset = $offset;
1149
1150 return $this;
1151 }
1152
1153 public function where($where, $value = NULL)
1154 {
1155 $this->trigger_events('where');
1156
1157 if (!is_array($where))
1158 {
1159 $where = array($where => $value);
1160 }
1161
1162 array_push($this->_ion_where, $where);
1163
1164 return $this;
1165 }
1166
1167 public function like($like, $value = NULL, $position = 'both')
1168 {
1169 $this->trigger_events('like');
1170
1171 if (!is_array($like))
1172 {
1173 $like = array($like => array(
1174 'value' => $value,
1175 'position' => $position,
1176 ));
1177 }
1178
1179 array_push($this->_ion_like, $like);
1180
1181 return $this;
1182 }
1183
1184 public function select($select)
1185 {
1186 $this->trigger_events('select');
1187
1188 $this->_ion_select[] = $select;
1189
1190 return $this;
1191 }
1192
1193 public function order_by($by, $order='desc')
1194 {
1195 $this->trigger_events('order_by');
1196
1197 $this->_ion_order_by = $by;
1198 $this->_ion_order = $order;
1199
1200 return $this;
1201 }
1202
1203 public function row()
1204 {
1205 $this->trigger_events('row');
1206
1207 $row = $this->response->row();
1208
1209 return $row;
1210 }
1211
1212 public function row_array()
1213 {
1214 $this->trigger_events(array('row', 'row_array'));
1215
1216 $row = $this->response->row_array();
1217
1218 return $row;
1219 }
1220
1221 public function result()
1222 {
1223 $this->trigger_events('result');
1224
1225 $result = $this->response->result();
1226
1227 return $result;
1228 }
1229
1230 public function result_array()
1231 {
1232 $this->trigger_events(array('result', 'result_array'));
1233
1234 $result = $this->response->result_array();
1235
1236 return $result;
1237 }
1238
1239 public function num_rows()
1240 {
1241 $this->trigger_events(array('num_rows'));
1242
1243 $result = $this->response->num_rows();
1244
1245 return $result;
1246 }
1247
1248 /**
1249 * users
1250 *
1251 * @return object Users
1252 * @author Ben Edmunds
1253 **/
1254 public function users($groups = NULL)
1255 {
1256 $this->trigger_events('users');
1257
1258 if (isset($this->_ion_select) && !empty($this->_ion_select))
1259 {
1260 foreach ($this->_ion_select as $select)
1261 {
1262 $this->db->select($select);
1263 }
1264
1265 $this->_ion_select = array();
1266 }
1267 else
1268 {
1269 //default selects
1270 $this->db->select(array(
1271 $this->tables['users'].'.*',
1272 $this->tables['users'].'.id as id',
1273 $this->tables['users'].'.id as user_id'
1274 ));
1275 }
1276
1277 // filter by group id(s) if passed
1278 if (isset($groups))
1279 {
1280 // build an array if only one group was passed
1281 if (!is_array($groups))
1282 {
1283 $groups = Array($groups);
1284 }
1285
1286 // join and then run a where_in against the group ids
1287 if (isset($groups) && !empty($groups))
1288 {
1289 $this->db->distinct();
1290 $this->db->join(
1291 $this->tables['users_groups'],
1292 $this->tables['users_groups'].'.'.$this->join['users'].'='.$this->tables['users'].'.id',
1293 'inner'
1294 );
1295 }
1296
1297 // verify if group name or group id was used and create and put elements in different arrays
1298 $group_ids = array();
1299 $group_names = array();
1300 foreach($groups as $group)
1301 {
1302 if(is_numeric($group)) $group_ids[] = $group;
1303 else $group_names[] = $group;
1304 }
1305 $or_where_in = (!empty($group_ids) && !empty($group_names)) ? 'or_where_in' : 'where_in';
1306 // if group name was used we do one more join with groups
1307 if(!empty($group_names))
1308 {
1309 $this->db->join($this->tables['groups'], $this->tables['users_groups'] . '.' . $this->join['groups'] . ' = ' . $this->tables['groups'] . '.id', 'inner');
1310 $this->db->where_in($this->tables['groups'] . '.name', $group_names);
1311 }
1312 if(!empty($group_ids))
1313 {
1314 $this->db->{$or_where_in}($this->tables['users_groups'].'.'.$this->join['groups'], $group_ids);
1315 }
1316 }
1317
1318 $this->trigger_events('extra_where');
1319
1320 // run each where that was passed
1321 if (isset($this->_ion_where) && !empty($this->_ion_where))
1322 {
1323 foreach ($this->_ion_where as $where)
1324 {
1325 $this->db->where($where);
1326 }
1327
1328 $this->_ion_where = array();
1329 }
1330
1331 if (isset($this->_ion_like) && !empty($this->_ion_like))
1332 {
1333 foreach ($this->_ion_like as $like)
1334 {
1335 $this->db->or_like($like);
1336 }
1337
1338 $this->_ion_like = array();
1339 }
1340
1341 if (isset($this->_ion_limit) && isset($this->_ion_offset))
1342 {
1343 $this->db->limit($this->_ion_limit, $this->_ion_offset);
1344
1345 $this->_ion_limit = NULL;
1346 $this->_ion_offset = NULL;
1347 }
1348 else if (isset($this->_ion_limit))
1349 {
1350 $this->db->limit($this->_ion_limit);
1351
1352 $this->_ion_limit = NULL;
1353 }
1354
1355 // set the order
1356 if (isset($this->_ion_order_by) && isset($this->_ion_order))
1357 {
1358 $this->db->order_by($this->_ion_order_by, $this->_ion_order);
1359
1360 $this->_ion_order = NULL;
1361 $this->_ion_order_by = NULL;
1362 }
1363
1364 $this->response = $this->db->get($this->tables['users']);
1365
1366 return $this;
1367 }
1368
1369 /**
1370 * user
1371 *
1372 * @return object
1373 * @author Ben Edmunds
1374 **/
1375 public function user($id = NULL)
1376 {
1377 $this->trigger_events('user');
1378
1379 // if no id was passed use the current users id
1380 $id || $id = $this->session->userdata('user_id');
1381
1382 $this->limit(1);
1383 $this->order_by($this->tables['users'].'.id', 'desc');
1384 $this->where($this->tables['users'].'.id', $id);
1385
1386 $this->users();
1387
1388 return $this;
1389 }
1390
1391 /**
1392 * get_users_groups
1393 *
1394 * @return array
1395 * @author Ben Edmunds
1396 **/
1397 public function get_users_groups($id=FALSE)
1398 {
1399 $this->trigger_events('get_users_group');
1400
1401 // if no id was passed use the current users id
1402 $id || $id = $this->session->userdata('user_id');
1403
1404 //return $this->db->select($this->tables['users_groups'].'.'.$this->join['groups'].' as id, '.$this->tables['groups'].'.name, '.$this->tables['groups'].'.description')
1405 /* ADD => , '.$this->tables['groups'].'.bgcolor' */
1406 return $this->db->select($this->tables['users_groups'].'.'.$this->join['groups'].' as id, '.$this->tables['groups'].'.name, '.$this->tables['groups'].'.description, '.$this->tables['groups'].'.bgcolor')
1407 ->where($this->tables['users_groups'].'.'.$this->join['users'], $id)
1408 ->join($this->tables['groups'], $this->tables['users_groups'].'.'.$this->join['groups'].'='.$this->tables['groups'].'.id')
1409 ->get($this->tables['users_groups']);
1410 }
1411
1412 /**
1413 * add_to_group
1414 *
1415 * @return bool
1416 * @author Ben Edmunds
1417 **/
1418 public function add_to_group($group_ids, $user_id=false)
1419 {
1420 $this->trigger_events('add_to_group');
1421
1422 // if no id was passed use the current users id
1423 $user_id || $user_id = $this->session->userdata('user_id');
1424
1425 if(!is_array($group_ids))
1426 {
1427 $group_ids = array($group_ids);
1428 }
1429
1430 $return = 0;
1431
1432 // Then insert each into the database
1433 foreach ($group_ids as $group_id)
1434 {
1435 if ($this->db->insert($this->tables['users_groups'], array( $this->join['groups'] => (float)$group_id, $this->join['users'] => (float)$user_id)))
1436 {
1437 if (isset($this->_cache_groups[$group_id])) {
1438 $group_name = $this->_cache_groups[$group_id];
1439 }
1440 else {
1441 $group = $this->group($group_id)->result();
1442 $group_name = $group[0]->name;
1443 $this->_cache_groups[$group_id] = $group_name;
1444 }
1445 $this->_cache_user_in_group[$user_id][$group_id] = $group_name;
1446
1447 // Return the number of groups added
1448 $return += 1;
1449 }
1450 }
1451
1452 return $return;
1453 }
1454
1455 /**
1456 * remove_from_group
1457 *
1458 * @return bool
1459 * @author Ben Edmunds
1460 **/
1461 public function remove_from_group($group_ids=false, $user_id=false)
1462 {
1463 $this->trigger_events('remove_from_group');
1464
1465 // user id is required
1466 if(empty($user_id))
1467 {
1468 return FALSE;
1469 }
1470
1471 // if group id(s) are passed remove user from the group(s)
1472 if( ! empty($group_ids))
1473 {
1474 if(!is_array($group_ids))
1475 {
1476 $group_ids = array($group_ids);
1477 }
1478
1479 foreach($group_ids as $group_id)
1480 {
1481 $this->db->delete($this->tables['users_groups'], array($this->join['groups'] => (float)$group_id, $this->join['users'] => (float)$user_id));
1482 if (isset($this->_cache_user_in_group[$user_id]) && isset($this->_cache_user_in_group[$user_id][$group_id]))
1483 {
1484 unset($this->_cache_user_in_group[$user_id][$group_id]);
1485 }
1486 }
1487
1488 $return = TRUE;
1489 }
1490 // otherwise remove user from all groups
1491 else
1492 {
1493 if ($return = $this->db->delete($this->tables['users_groups'], array($this->join['users'] => (float)$user_id))) {
1494 $this->_cache_user_in_group[$user_id] = array();
1495 }
1496 }
1497 return $return;
1498 }
1499
1500 /**
1501 * groups
1502 *
1503 * @return object
1504 * @author Ben Edmunds
1505 **/
1506 public function groups()
1507 {
1508 $this->trigger_events('groups');
1509
1510 // run each where that was passed
1511 if (isset($this->_ion_where) && !empty($this->_ion_where))
1512 {
1513 foreach ($this->_ion_where as $where)
1514 {
1515 $this->db->where($where);
1516 }
1517 $this->_ion_where = array();
1518 }
1519
1520 if (isset($this->_ion_limit) && isset($this->_ion_offset))
1521 {
1522 $this->db->limit($this->_ion_limit, $this->_ion_offset);
1523
1524 $this->_ion_limit = NULL;
1525 $this->_ion_offset = NULL;
1526 }
1527 else if (isset($this->_ion_limit))
1528 {
1529 $this->db->limit($this->_ion_limit);
1530
1531 $this->_ion_limit = NULL;
1532 }
1533
1534 // set the order
1535 if (isset($this->_ion_order_by) && isset($this->_ion_order))
1536 {
1537 $this->db->order_by($this->_ion_order_by, $this->_ion_order);
1538 }
1539
1540 $this->response = $this->db->get($this->tables['groups']);
1541
1542 return $this;
1543 }
1544
1545 /**
1546 * group
1547 *
1548 * @return object
1549 * @author Ben Edmunds
1550 **/
1551 public function group($id = NULL)
1552 {
1553 $this->trigger_events('group');
1554
1555 if (isset($id))
1556 {
1557 $this->where($this->tables['groups'].'.id', $id);
1558 }
1559
1560 $this->limit(1);
1561 $this->order_by('id', 'desc');
1562
1563 return $this->groups();
1564 }
1565
1566 /**
1567 * update
1568 *
1569 * @return bool
1570 * @author Phil Sturgeon
1571 **/
1572 public function update($id, array $data)
1573 {
1574 $this->trigger_events('pre_update_user');
1575
1576 $user = $this->user($id)->row();
1577
1578 $this->db->trans_begin();
1579
1580 if (array_key_exists($this->identity_column, $data) && $this->identity_check($data[$this->identity_column]) && $user->{$this->identity_column} !== $data[$this->identity_column])
1581 {
1582 $this->db->trans_rollback();
1583 $this->set_error('account_creation_duplicate_identity');
1584
1585 $this->trigger_events(array('post_update_user', 'post_update_user_unsuccessful'));
1586 $this->set_error('update_unsuccessful');
1587
1588 return FALSE;
1589 }
1590
1591 // Filter the data passed
1592 $data = $this->_filter_data($this->tables['users'], $data);
1593
1594 if (array_key_exists($this->identity_column, $data) || array_key_exists('password', $data) || array_key_exists('email', $data))
1595 {
1596 if (array_key_exists('password', $data))
1597 {
1598 if( ! empty($data['password']))
1599 {
1600 $data['password'] = $this->hash_password($data['password'], $user->salt);
1601 }
1602 else
1603 {
1604 // unset password so it doesn't effect database entry if no password passed
1605 unset($data['password']);
1606 }
1607 }
1608 }
1609
1610 $this->trigger_events('extra_where');
1611 $this->db->update($this->tables['users'], $data, array('id' => $user->id));
1612
1613 if ($this->db->trans_status() === FALSE)
1614 {
1615 $this->db->trans_rollback();
1616
1617 $this->trigger_events(array('post_update_user', 'post_update_user_unsuccessful'));
1618 $this->set_error('update_unsuccessful');
1619 return FALSE;
1620 }
1621
1622 $this->db->trans_commit();
1623
1624 $this->trigger_events(array('post_update_user', 'post_update_user_successful'));
1625 $this->set_message('update_successful');
1626 return TRUE;
1627 }
1628
1629 /**
1630 * delete_user
1631 *
1632 * @return bool
1633 * @author Phil Sturgeon
1634 **/
1635 public function delete_user($id)
1636 {
1637 $this->trigger_events('pre_delete_user');
1638
1639 $this->db->trans_begin();
1640
1641 // remove user from groups
1642 $this->remove_from_group(NULL, $id);
1643
1644 // delete user from users table should be placed after remove from group
1645 $this->db->delete($this->tables['users'], array('id' => $id));
1646
1647 // if user does not exist in database then it returns FALSE else removes the user from groups
1648 if ($this->db->affected_rows() == 0)
1649 {
1650 return FALSE;
1651 }
1652
1653 if ($this->db->trans_status() === FALSE)
1654 {
1655 $this->db->trans_rollback();
1656 $this->trigger_events(array('post_delete_user', 'post_delete_user_unsuccessful'));
1657 $this->set_error('delete_unsuccessful');
1658 return FALSE;
1659 }
1660
1661 $this->db->trans_commit();
1662
1663 $this->trigger_events(array('post_delete_user', 'post_delete_user_successful'));
1664 $this->set_message('delete_successful');
1665 return TRUE;
1666 }
1667
1668 /**
1669 * update_last_login
1670 *
1671 * @return bool
1672 * @author Ben Edmunds
1673 **/
1674 public function update_last_login($id)
1675 {
1676 $this->trigger_events('update_last_login');
1677
1678 $this->load->helper('date');
1679
1680 $this->trigger_events('extra_where');
1681
1682 $this->db->update($this->tables['users'], array('last_login' => time()), array('id' => $id));
1683
1684 return $this->db->affected_rows() == 1;
1685 }
1686
1687 /**
1688 * set_lang
1689 *
1690 * @return bool
1691 * @author Ben Edmunds
1692 **/
1693 public function set_lang($lang = 'en')
1694 {
1695 $this->trigger_events('set_lang');
1696
1697 // if the user_expire is set to zero we'll set the expiration two years from now.
1698 if($this->config->item('user_expire', 'ion_auth') === 0)
1699 {
1700 $expire = (60*60*24*365*2);
1701 }
1702 // otherwise use what is set
1703 else
1704 {
1705 $expire = $this->config->item('user_expire', 'ion_auth');
1706 }
1707
1708 set_cookie(array(
1709 'name' => 'lang_code',
1710 'value' => $lang,
1711 'expire' => $expire
1712 ));
1713
1714 return TRUE;
1715 }
1716
1717 /**
1718 * set_session
1719 *
1720 * @return bool
1721 * @author jrmadsen67
1722 **/
1723 public function set_session($user)
1724 {
1725
1726 $this->trigger_events('pre_set_session');
1727
1728 $session_data = array(
1729 'identity' => $user->{$this->identity_column},
1730 $this->identity_column => $user->{$this->identity_column},
1731 'email' => $user->email,
1732 'user_id' => $user->id, //everyone likes to overwrite id so we'll use user_id
1733 'old_last_login' => $user->last_login
1734 );
1735
1736 $this->session->set_userdata($session_data);
1737
1738 $this->trigger_events('post_set_session');
1739
1740 return TRUE;
1741 }
1742
1743 /**
1744 * remember_user
1745 *
1746 * @return bool
1747 * @author Ben Edmunds
1748 **/
1749 public function remember_user($id)
1750 {
1751 $this->trigger_events('pre_remember_user');
1752
1753 if (!$id)
1754 {
1755 return FALSE;
1756 }
1757
1758 $user = $this->user($id)->row();
1759
1760 $salt = $this->salt();
1761
1762 $this->db->update($this->tables['users'], array('remember_code' => $salt), array('id' => $id));
1763
1764 if ($this->db->affected_rows() > -1)
1765 {
1766 // if the user_expire is set to zero we'll set the expiration two years from now.
1767 if($this->config->item('user_expire', 'ion_auth') === 0)
1768 {
1769 $expire = (60*60*24*365*2);
1770 }
1771 // otherwise use what is set
1772 else
1773 {
1774 $expire = $this->config->item('user_expire', 'ion_auth');
1775 }
1776
1777 set_cookie(array(
1778 'name' => $this->config->item('identity_cookie_name', 'ion_auth'),
1779 'value' => $user->{$this->identity_column},
1780 'expire' => $expire
1781 ));
1782
1783 set_cookie(array(
1784 'name' => $this->config->item('remember_cookie_name', 'ion_auth'),
1785 'value' => $salt,
1786 'expire' => $expire
1787 ));
1788
1789 $this->trigger_events(array('post_remember_user', 'remember_user_successful'));
1790 return TRUE;
1791 }
1792
1793 $this->trigger_events(array('post_remember_user', 'remember_user_unsuccessful'));
1794 return FALSE;
1795 }
1796
1797 /**
1798 * login_remembed_user
1799 *
1800 * @return bool
1801 * @author Ben Edmunds
1802 **/
1803 public function login_remembered_user()
1804 {
1805 $this->trigger_events('pre_login_remembered_user');
1806
1807 // check for valid data
1808 if (!get_cookie($this->config->item('identity_cookie_name', 'ion_auth'))
1809 || !get_cookie($this->config->item('remember_cookie_name', 'ion_auth'))
1810 || !$this->identity_check(get_cookie($this->config->item('identity_cookie_name', 'ion_auth'))))
1811 {
1812 $this->trigger_events(array('post_login_remembered_user', 'post_login_remembered_user_unsuccessful'));
1813 return FALSE;
1814 }
1815
1816 // get the user
1817 $this->trigger_events('extra_where');
1818 $query = $this->db->select($this->identity_column.', id, email, last_login')
1819 ->where($this->identity_column, get_cookie($this->config->item('identity_cookie_name', 'ion_auth')))
1820 ->where('remember_code', get_cookie($this->config->item('remember_cookie_name', 'ion_auth')))
1821 ->limit(1)
1822 ->order_by('id', 'desc')
1823 ->get($this->tables['users']);
1824
1825 // if the user was found, sign them in
1826 if ($query->num_rows() == 1)
1827 {
1828 $user = $query->row();
1829
1830 $this->update_last_login($user->id);
1831
1832 $this->set_session($user);
1833
1834 // extend the users cookies if the option is enabled
1835 if ($this->config->item('user_extend_on_login', 'ion_auth'))
1836 {
1837 $this->remember_user($user->id);
1838 }
1839
1840 $this->trigger_events(array('post_login_remembered_user', 'post_login_remembered_user_successful'));
1841 return TRUE;
1842 }
1843
1844 $this->trigger_events(array('post_login_remembered_user', 'post_login_remembered_user_unsuccessful'));
1845 return FALSE;
1846 }
1847
1848
1849 /**
1850 * create_group
1851 *
1852 * @author aditya menon
1853 */
1854 public function create_group($group_name = FALSE, $group_description = '', $additional_data = array())
1855 {
1856 // bail if the group name was not passed
1857 if(!$group_name)
1858 {
1859 $this->set_error('group_name_required');
1860 return FALSE;
1861 }
1862
1863 // bail if the group name already exists
1864 $existing_group = $this->db->get_where($this->tables['groups'], array('name' => $group_name))->num_rows();
1865 if($existing_group !== 0)
1866 {
1867 $this->set_error('group_already_exists');
1868 return FALSE;
1869 }
1870
1871 $data = array('name'=>$group_name,'description'=>$group_description);
1872
1873 // filter out any data passed that doesnt have a matching column in the groups table
1874 // and merge the set group data and the additional data
1875 if (!empty($additional_data)) $data = array_merge($this->_filter_data($this->tables['groups'], $additional_data), $data);
1876
1877 $this->trigger_events('extra_group_set');
1878
1879 // insert the new group
1880 $this->db->insert($this->tables['groups'], $data);
1881 $group_id = $this->db->insert_id();
1882
1883 // report success
1884 $this->set_message('group_creation_successful');
1885 // return the brand new group id
1886 return $group_id;
1887 }
1888
1889 /**
1890 * update_group
1891 *
1892 * @return bool
1893 * @author aditya menon
1894 **/
1895 public function update_group($group_id = FALSE, $group_name = FALSE, $additional_data = array())
1896 {
1897 if (empty($group_id)) return FALSE;
1898
1899 $data = array();
1900
1901 if (!empty($group_name))
1902 {
1903 // we are changing the name, so do some checks
1904
1905 // bail if the group name already exists
1906 $existing_group = $this->db->get_where($this->tables['groups'], array('name' => $group_name))->row();
1907 if(isset($existing_group->id) && $existing_group->id != $group_id)
1908 {
1909 $this->set_error('group_already_exists');
1910 return FALSE;
1911 }
1912
1913 $data['name'] = $group_name;
1914 }
1915
1916 // restrict change of name of the admin group
1917 $group = $this->db->get_where($this->tables['groups'], array('id' => $group_id))->row();
1918 if($this->config->item('admin_group', 'ion_auth') === $group->name && $group_name !== $group->name)
1919 {
1920 $this->set_error('group_name_admin_not_alter');
1921 return FALSE;
1922 }
1923
1924
1925 // IMPORTANT!! Third parameter was string type $description; this following code is to maintain backward compatibility
1926 // New projects should work with 3rd param as array
1927 if (is_string($additional_data)) $additional_data = array('description' => $additional_data);
1928
1929
1930 // filter out any data passed that doesnt have a matching column in the groups table
1931 // and merge the set group data and the additional data
1932 if (!empty($additional_data)) $data = array_merge($this->_filter_data($this->tables['groups'], $additional_data), $data);
1933
1934
1935 $this->db->update($this->tables['groups'], $data, array('id' => $group_id));
1936
1937 $this->set_message('group_update_successful');
1938
1939 return TRUE;
1940 }
1941
1942 /**
1943 * delete_group
1944 *
1945 * @return bool
1946 * @author aditya menon
1947 **/
1948 public function delete_group($group_id = FALSE)
1949 {
1950 // bail if mandatory param not set
1951 if(!$group_id || empty($group_id))
1952 {
1953 return FALSE;
1954 }
1955 $group = $this->group($group_id)->row();
1956 if($group->name == $this->config->item('admin_group', 'ion_auth'))
1957 {
1958 $this->trigger_events(array('post_delete_group', 'post_delete_group_notallowed'));
1959 $this->set_error('group_delete_notallowed');
1960 return FALSE;
1961 }
1962
1963 $this->trigger_events('pre_delete_group');
1964
1965 $this->db->trans_begin();
1966
1967 // remove all users from this group
1968 $this->db->delete($this->tables['users_groups'], array($this->join['groups'] => $group_id));
1969 // remove the group itself
1970 $this->db->delete($this->tables['groups'], array('id' => $group_id));
1971
1972 if ($this->db->trans_status() === FALSE)
1973 {
1974 $this->db->trans_rollback();
1975 $this->trigger_events(array('post_delete_group', 'post_delete_group_unsuccessful'));
1976 $this->set_error('group_delete_unsuccessful');
1977 return FALSE;
1978 }
1979
1980 $this->db->trans_commit();
1981
1982 $this->trigger_events(array('post_delete_group', 'post_delete_group_successful'));
1983 $this->set_message('group_delete_successful');
1984 return TRUE;
1985 }
1986
1987 public function set_hook($event, $name, $class, $method, $arguments)
1988 {
1989 $this->_ion_hooks->{$event}[$name] = new stdClass;
1990 $this->_ion_hooks->{$event}[$name]->class = $class;
1991 $this->_ion_hooks->{$event}[$name]->method = $method;
1992 $this->_ion_hooks->{$event}[$name]->arguments = $arguments;
1993 }
1994
1995 public function remove_hook($event, $name)
1996 {
1997 if (isset($this->_ion_hooks->{$event}[$name]))
1998 {
1999 unset($this->_ion_hooks->{$event}[$name]);
2000 }
2001 }
2002
2003 public function remove_hooks($event)
2004 {
2005 if (isset($this->_ion_hooks->$event))
2006 {
2007 unset($this->_ion_hooks->$event);
2008 }
2009 }
2010
2011 protected function _call_hook($event, $name)
2012 {
2013 if (isset($this->_ion_hooks->{$event}[$name]) && method_exists($this->_ion_hooks->{$event}[$name]->class, $this->_ion_hooks->{$event}[$name]->method))
2014 {
2015 $hook = $this->_ion_hooks->{$event}[$name];
2016
2017 return call_user_func_array(array($hook->class, $hook->method), $hook->arguments);
2018 }
2019
2020 return FALSE;
2021 }
2022
2023 public function trigger_events($events)
2024 {
2025 if (is_array($events) && !empty($events))
2026 {
2027 foreach ($events as $event)
2028 {
2029 $this->trigger_events($event);
2030 }
2031 }
2032 else
2033 {
2034 if (isset($this->_ion_hooks->$events) && !empty($this->_ion_hooks->$events))
2035 {
2036 foreach ($this->_ion_hooks->$events as $name => $hook)
2037 {
2038 $this->_call_hook($events, $name);
2039 }
2040 }
2041 }
2042 }
2043
2044 /**
2045 * set_message_delimiters
2046 *
2047 * Set the message delimiters
2048 *
2049 * @return void
2050 * @author Ben Edmunds
2051 **/
2052 public function set_message_delimiters($start_delimiter, $end_delimiter)
2053 {
2054 $this->message_start_delimiter = $start_delimiter;
2055 $this->message_end_delimiter = $end_delimiter;
2056
2057 return TRUE;
2058 }
2059
2060 /**
2061 * set_error_delimiters
2062 *
2063 * Set the error delimiters
2064 *
2065 * @return void
2066 * @author Ben Edmunds
2067 **/
2068 public function set_error_delimiters($start_delimiter, $end_delimiter)
2069 {
2070 $this->error_start_delimiter = $start_delimiter;
2071 $this->error_end_delimiter = $end_delimiter;
2072
2073 return TRUE;
2074 }
2075
2076 /**
2077 * set_message
2078 *
2079 * Set a message
2080 *
2081 * @return void
2082 * @author Ben Edmunds
2083 **/
2084 public function set_message($message)
2085 {
2086 $this->messages[] = $message;
2087
2088 return $message;
2089 }
2090
2091
2092
2093 /**
2094 * messages
2095 *
2096 * Get the messages
2097 *
2098 * @return void
2099 * @author Ben Edmunds
2100 **/
2101 public function messages()
2102 {
2103 $_output = '';
2104 foreach ($this->messages as $message)
2105 {
2106 $messageLang = $this->lang->line($message) ? $this->lang->line($message) : '##' . $message . '##';
2107 $_output .= $this->message_start_delimiter . $messageLang . $this->message_end_delimiter;
2108 }
2109
2110 return $_output;
2111 }
2112
2113 /**
2114 * messages as array
2115 *
2116 * Get the messages as an array
2117 *
2118 * @return array
2119 * @author Raul Baldner Junior
2120 **/
2121 public function messages_array($langify = TRUE)
2122 {
2123 if ($langify)
2124 {
2125 $_output = array();
2126 foreach ($this->messages as $message)
2127 {
2128 $messageLang = $this->lang->line($message) ? $this->lang->line($message) : '##' . $message . '##';
2129 $_output[] = $this->message_start_delimiter . $messageLang . $this->message_end_delimiter;
2130 }
2131 return $_output;
2132 }
2133 else
2134 {
2135 return $this->messages;
2136 }
2137 }
2138
2139
2140 /**
2141 * clear_messages
2142 *
2143 * Clear messages
2144 *
2145 * @return void
2146 * @author Ben Edmunds
2147 **/
2148 public function clear_messages()
2149 {
2150 $this->messages = array();
2151
2152 return TRUE;
2153 }
2154
2155
2156 /**
2157 * set_error
2158 *
2159 * Set an error message
2160 *
2161 * @return void
2162 * @author Ben Edmunds
2163 **/
2164 public function set_error($error)
2165 {
2166 $this->errors[] = $error;
2167
2168 return $error;
2169 }
2170
2171 /**
2172 * errors
2173 *
2174 * Get the error message
2175 *
2176 * @return void
2177 * @author Ben Edmunds
2178 **/
2179 public function errors()
2180 {
2181 $_output = '';
2182 foreach ($this->errors as $error)
2183 {
2184 $errorLang = $this->lang->line($error) ? $this->lang->line($error) : '##' . $error . '##';
2185 $_output .= $this->error_start_delimiter . $errorLang . $this->error_end_delimiter;
2186 }
2187
2188 return $_output;
2189 }
2190
2191 /**
2192 * errors as array
2193 *
2194 * Get the error messages as an array
2195 *
2196 * @return array
2197 * @author Raul Baldner Junior
2198 **/
2199 public function errors_array($langify = TRUE)
2200 {
2201 if ($langify)
2202 {
2203 $_output = array();
2204 foreach ($this->errors as $error)
2205 {
2206 $errorLang = $this->lang->line($error) ? $this->lang->line($error) : '##' . $error . '##';
2207 $_output[] = $this->error_start_delimiter . $errorLang . $this->error_end_delimiter;
2208 }
2209 return $_output;
2210 }
2211 else
2212 {
2213 return $this->errors;
2214 }
2215 }
2216
2217
2218 /**
2219 * clear_errors
2220 *
2221 * Clear Errors
2222 *
2223 * @return void
2224 * @author Ben Edmunds
2225 **/
2226 public function clear_errors()
2227 {
2228 $this->errors = array();
2229
2230 return TRUE;
2231 }
2232
2233
2234
2235 protected function _filter_data($table, $data)
2236 {
2237 $filtered_data = array();
2238 $columns = $this->db->list_fields($table);
2239
2240 if (is_array($data))
2241 {
2242 foreach ($columns as $column)
2243 {
2244 if (array_key_exists($column, $data))
2245 $filtered_data[$column] = $data[$column];
2246 }
2247 }
2248
2249 return $filtered_data;
2250 }
2251
2252 protected function _prepare_ip($ip_address) {
2253 // just return the string IP address now for better compatibility
2254 return $ip_address;
2255 }
2256}