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