1: <?php
2:
3: defined('BASEPATH') OR exit('No direct script access allowed');
4:
5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
17: abstract class REST_Controller extends CI_Controller {
18:
19:
20:
21:
22:
23: const HTTP_CONTINUE = 100;
24: const HTTP_SWITCHING_PROTOCOLS = 101;
25: const HTTP_PROCESSING = 102;
26:
27:
28:
29: 30: 31:
32: const HTTP_OK = 200;
33:
34: 35: 36:
37: const HTTP_CREATED = 201;
38: const HTTP_ACCEPTED = 202;
39: const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
40:
41: 42: 43:
44: const HTTP_NO_CONTENT = 204;
45: const HTTP_RESET_CONTENT = 205;
46: const HTTP_PARTIAL_CONTENT = 206;
47: const HTTP_MULTI_STATUS = 207;
48: const HTTP_ALREADY_REPORTED = 208;
49: const HTTP_IM_USED = 226;
50:
51:
52:
53: const HTTP_MULTIPLE_CHOICES = 300;
54: const HTTP_MOVED_PERMANENTLY = 301;
55: const HTTP_FOUND = 302;
56: const HTTP_SEE_OTHER = 303;
57:
58: 59: 60:
61: const HTTP_NOT_MODIFIED = 304;
62: const HTTP_USE_PROXY = 305;
63: const HTTP_RESERVED = 306;
64: const HTTP_TEMPORARY_REDIRECT = 307;
65: const HTTP_PERMANENTLY_REDIRECT = 308;
66:
67:
68:
69: 70: 71:
72: const HTTP_BAD_REQUEST = 400;
73:
74: 75: 76:
77: const HTTP_UNAUTHORIZED = 401;
78: const HTTP_PAYMENT_REQUIRED = 402;
79:
80: 81: 82:
83: const HTTP_FORBIDDEN = 403;
84:
85: 86: 87: 88: 89: 90:
91: const HTTP_NOT_FOUND = 404;
92:
93: 94: 95:
96: const HTTP_METHOD_NOT_ALLOWED = 405;
97:
98: 99: 100:
101: const HTTP_NOT_ACCEPTABLE = 406;
102: const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
103: const HTTP_REQUEST_TIMEOUT = 408;
104:
105: 106: 107: 108:
109: const HTTP_CONFLICT = 409;
110: const HTTP_GONE = 410;
111: const HTTP_LENGTH_REQUIRED = 411;
112: const HTTP_PRECONDITION_FAILED = 412;
113: const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
114: const HTTP_REQUEST_URI_TOO_LONG = 414;
115: const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
116: const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
117: const HTTP_EXPECTATION_FAILED = 417;
118: const HTTP_I_AM_A_TEAPOT = 418;
119: const HTTP_UNPROCESSABLE_ENTITY = 422;
120: const HTTP_LOCKED = 423;
121: const HTTP_FAILED_DEPENDENCY = 424;
122: const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425;
123: const HTTP_UPGRADE_REQUIRED = 426;
124: const HTTP_PRECONDITION_REQUIRED = 428;
125: const HTTP_TOO_MANY_REQUESTS = 429;
126: const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
127:
128:
129:
130: 131: 132: 133: 134: 135:
136: const HTTP_INTERNAL_SERVER_ERROR = 500;
137:
138: 139: 140:
141: const HTTP_NOT_IMPLEMENTED = 501;
142: const HTTP_BAD_GATEWAY = 502;
143: const HTTP_SERVICE_UNAVAILABLE = 503;
144: const HTTP_GATEWAY_TIMEOUT = 504;
145: const HTTP_VERSION_NOT_SUPPORTED = 505;
146: const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506;
147: const HTTP_INSUFFICIENT_STORAGE = 507;
148: const HTTP_LOOP_DETECTED = 508;
149: const HTTP_NOT_EXTENDED = 510;
150: const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;
151:
152: 153: 154: 155: 156: 157:
158: protected $rest_format = NULL;
159:
160: 161: 162: 163: 164:
165: protected $methods = [];
166:
167: 168: 169: 170: 171:
172: protected $allowed_http_methods = ['get', 'delete', 'post', 'put', 'options', 'patch', 'head'];
173:
174: 175: 176: 177: 178: 179: 180:
181: protected $request = NULL;
182:
183: 184: 185: 186: 187: 188: 189:
190: protected $response = NULL;
191:
192: 193: 194: 195: 196: 197: 198:
199: protected $rest = NULL;
200:
201: 202: 203: 204: 205:
206: protected $_get_args = [];
207:
208: 209: 210: 211: 212:
213: protected $_post_args = [];
214:
215: 216: 217: 218: 219:
220: protected $_put_args = [];
221:
222: 223: 224: 225: 226:
227: protected $_delete_args = [];
228:
229: 230: 231: 232: 233:
234: protected $_patch_args = [];
235:
236: 237: 238: 239: 240:
241: protected $_head_args = [];
242:
243: 244: 245: 246: 247:
248: protected $_options_args = [];
249:
250: 251: 252: 253: 254:
255: protected $_query_args = [];
256:
257: 258: 259: 260: 261:
262: protected $_args = [];
263:
264: 265: 266: 267: 268:
269: protected $_insert_id = '';
270:
271: 272: 273: 274: 275:
276: protected $_allow = TRUE;
277:
278: 279: 280: 281: 282:
283: protected $_user_ldap_dn = '';
284:
285: 286: 287: 288: 289:
290: protected $_start_rtime = '';
291:
292: 293: 294: 295: 296:
297: protected $_end_rtime = '';
298:
299: 300: 301: 302: 303:
304: protected $_supported_formats = [
305: 'json' => 'application/json',
306: 'array' => 'application/json',
307: 'csv' => 'application/csv',
308: 'html' => 'text/html',
309: 'jsonp' => 'application/javascript',
310: 'php' => 'text/plain',
311: 'serialized' => 'application/vnd.php.serialized',
312: 'xml' => 'application/xml'
313: ];
314:
315: 316: 317: 318: 319:
320: protected $_apiuser;
321:
322: 323: 324: 325: 326:
327: protected $check_cors = NULL;
328:
329: 330: 331: 332: 333: 334: 335: 336:
337: protected $_enable_xss = FALSE;
338:
339: 340: 341: 342: 343: 344: 345:
346: protected $http_status_codes = [
347: self::HTTP_OK => 'OK',
348: self::HTTP_CREATED => 'CREATED',
349: self::HTTP_NO_CONTENT => 'NO CONTENT',
350: self::HTTP_NOT_MODIFIED => 'NOT MODIFIED',
351: self::HTTP_BAD_REQUEST => 'BAD REQUEST',
352: self::HTTP_UNAUTHORIZED => 'UNAUTHORIZED',
353: self::HTTP_FORBIDDEN => 'FORBIDDEN',
354: self::HTTP_NOT_FOUND => 'NOT FOUND',
355: self::HTTP_METHOD_NOT_ALLOWED => 'METHOD NOT ALLOWED',
356: self::HTTP_NOT_ACCEPTABLE => 'NOT ACCEPTABLE',
357: self::HTTP_CONFLICT => 'CONFLICT',
358: self::HTTP_INTERNAL_SERVER_ERROR => 'INTERNAL SERVER ERROR',
359: self::HTTP_NOT_IMPLEMENTED => 'NOT IMPLEMENTED'
360: ];
361:
362: 363: 364: 365: 366: 367:
368: protected function early_checks()
369: {
370: }
371:
372: 373: 374: 375: 376: 377: 378: 379:
380: public function __construct($config = 'rest')
381: {
382: parent::__construct();
383:
384:
385: libxml_disable_entity_loader(TRUE);
386:
387:
388: if (is_php('5.4') === FALSE)
389: {
390:
391: throw new Exception('Using PHP v'.PHP_VERSION.', though PHP v5.4 or greater is required');
392: }
393:
394:
395: if (explode('.', CI_VERSION, 2)[0] < 3)
396: {
397: throw new Exception('REST Server requires CodeIgniter 3.x');
398: }
399:
400:
401: $this->_enable_xss = ($this->config->item('global_xss_filtering') === TRUE);
402:
403:
404:
405: $this->output->parse_exec_vars = FALSE;
406:
407:
408: $this->_start_rtime = microtime(TRUE);
409:
410:
411: $this->load->config($config);
412:
413:
414: $this->load->library('format');
415:
416:
417: $supported_formats = $this->config->item('rest_supported_formats');
418:
419:
420: if (empty($supported_formats))
421: {
422: $supported_formats = [];
423: }
424:
425: if ( ! is_array($supported_formats))
426: {
427: $supported_formats = [$supported_formats];
428: }
429:
430:
431: $default_format = $this->_get_default_output_format();
432: if (!in_array($default_format, $supported_formats))
433: {
434: $supported_formats[] = $default_format;
435: }
436:
437:
438: $this->_supported_formats = array_intersect_key($this->_supported_formats, array_flip($supported_formats));
439:
440:
441: $language = $this->config->item('rest_language');
442: if ($language === NULL)
443: {
444: $language = 'english';
445: }
446:
447:
448: $this->lang->load('rest_controller', $language);
449:
450:
451: $this->request = new stdClass();
452: $this->response = new stdClass();
453: $this->rest = new stdClass();
454:
455:
456: if ($this->config->item('rest_ip_blacklist_enabled') === TRUE)
457: {
458: $this->_check_blacklist_auth();
459: }
460:
461:
462: $this->request->ssl = is_https();
463:
464:
465: $this->request->method = $this->_detect_method();
466:
467:
468: $check_cors = $this->config->item('check_cors');
469: if ($check_cors === TRUE)
470: {
471: $this->_check_cors();
472: }
473:
474:
475: if (isset($this->{'_'.$this->request->method.'_args'}) === FALSE)
476: {
477: $this->{'_'.$this->request->method.'_args'} = [];
478: }
479:
480:
481: $this->_parse_query();
482:
483:
484: $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc());
485:
486:
487: $this->request->format = $this->_detect_input_format();
488:
489:
490: $this->request->body = NULL;
491:
492: $this->{'_parse_' . $this->request->method}();
493:
494:
495: if ($this->request->format && $this->request->body)
496: {
497: $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array();
498:
499: $this->{'_'.$this->request->method.'_args'} = $this->request->body;
500: }
501:
502:
503: $this->_args = array_merge(
504: $this->_get_args,
505: $this->_options_args,
506: $this->_patch_args,
507: $this->_head_args,
508: $this->_put_args,
509: $this->_post_args,
510: $this->_delete_args,
511: $this->{'_'.$this->request->method.'_args'}
512: );
513:
514:
515: $this->response->format = $this->_detect_output_format();
516:
517:
518: $this->response->lang = $this->_detect_lang();
519:
520:
521: $this->early_checks();
522:
523:
524: if ($this->config->item('rest_database_group') && ($this->config->item('rest_enable_keys') || $this->config->item('rest_enable_logging')))
525: {
526: $this->rest->db = $this->load->database($this->config->item('rest_database_group'), TRUE);
527: }
528:
529:
530: elseif (property_exists($this, 'db'))
531: {
532: $this->rest->db = $this->db;
533: }
534:
535:
536:
537: $this->auth_override = $this->_auth_override_check();
538:
539:
540:
541: if ($this->config->item('rest_enable_keys') && $this->auth_override !== TRUE)
542: {
543: $this->_allow = $this->_detect_api_key();
544: }
545:
546:
547: if ($this->input->is_ajax_request() === FALSE && $this->config->item('rest_ajax_only'))
548: {
549:
550: $this->response([
551: $this->config->item('rest_status_field_name') => FALSE,
552: $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ajax_only')
553: ], self::HTTP_NOT_ACCEPTABLE);
554: }
555:
556:
557: if ($this->auth_override === FALSE && ! ($this->config->item('rest_enable_keys') && $this->_allow === TRUE) || ($this->config->item('allow_auth_and_keys') === TRUE && $this->_allow === TRUE))
558: {
559: $rest_auth = strtolower($this->config->item('rest_auth'));
560: switch ($rest_auth)
561: {
562: case 'basic':
563: $this->_prepare_basic_auth();
564: break;
565: case 'digest':
566: $this->_prepare_digest_auth();
567: break;
568: case 'session':
569: $this->_check_php_session();
570: break;
571: }
572: if ($this->config->item('rest_ip_whitelist_enabled') === TRUE)
573: {
574: $this->_check_whitelist_auth();
575: }
576: }
577: }
578:
579: 580: 581: 582: 583: 584: 585:
586: public function __destruct()
587: {
588:
589: $this->_end_rtime = microtime(TRUE);
590:
591:
592: if ($this->config->item('rest_enable_logging') === TRUE)
593: {
594: $this->_log_access_time();
595: }
596: }
597:
598: 599: 600: 601: 602: 603: 604: 605: 606:
607: public function _remap($object_called, $arguments = [])
608: {
609:
610: if ($this->config->item('force_https') && $this->request->ssl === FALSE)
611: {
612: $this->response([
613: $this->config->item('rest_status_field_name') => FALSE,
614: $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unsupported')
615: ], self::HTTP_FORBIDDEN);
616: }
617:
618:
619: $object_called = preg_replace('/^(.*)\.(?:'.implode('|', array_keys($this->_supported_formats)).')$/', '$1', $object_called);
620:
621: $controller_method = $object_called.'_'.$this->request->method;
622:
623:
624: $log_method = ! (isset($this->methods[$controller_method]['log']) && $this->methods[$controller_method]['log'] === FALSE);
625:
626:
627: $use_key = ! (isset($this->methods[$controller_method]['key']) && $this->methods[$controller_method]['key'] === FALSE);
628:
629:
630: if ($this->config->item('rest_enable_keys') && $use_key && $this->_allow === FALSE)
631: {
632: if ($this->config->item('rest_enable_logging') && $log_method)
633: {
634: $this->_log_request();
635: }
636:
637: $this->response([
638: $this->config->item('rest_status_field_name') => FALSE,
639: $this->config->item('rest_message_field_name') => sprintf($this->lang->line('text_rest_invalid_api_key'), $this->rest->key)
640: ], self::HTTP_FORBIDDEN);
641: }
642:
643:
644: if ($this->config->item('rest_enable_keys') && $use_key && empty($this->rest->key) === FALSE && $this->_check_access() === FALSE)
645: {
646: if ($this->config->item('rest_enable_logging') && $log_method)
647: {
648: $this->_log_request();
649: }
650:
651: $this->response([
652: $this->config->item('rest_status_field_name') => FALSE,
653: $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_unauthorized')
654: ], self::HTTP_UNAUTHORIZED);
655: }
656:
657:
658: if (method_exists($this, $controller_method) === FALSE)
659: {
660: $this->response([
661: $this->config->item('rest_status_field_name') => FALSE,
662: $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unknown_method')
663: ], self::HTTP_NOT_FOUND);
664: }
665:
666:
667: if ($this->config->item('rest_enable_keys') && empty($this->rest->key) === FALSE)
668: {
669:
670: if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE)
671: {
672: $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_time_limit')];
673: $this->response($response, self::HTTP_UNAUTHORIZED);
674: }
675:
676:
677: $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0;
678:
679:
680: $authorized = $level <= $this->rest->level;
681:
682: if ($this->config->item('rest_enable_logging') && $log_method)
683: {
684: $this->_log_request($authorized);
685: }
686: if($authorized === FALSE)
687: {
688:
689: $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_permissions')];
690: $this->response($response, self::HTTP_UNAUTHORIZED);
691: }
692: }
693:
694:
695: elseif ($this->config->item('rest_enable_logging') && $log_method)
696: {
697: $this->_log_request($authorized = TRUE);
698: }
699:
700:
701: try
702: {
703: call_user_func_array([$this, $controller_method], $arguments);
704: }
705: catch (Exception $ex)
706: {
707:
708: $this->response([
709: $this->config->item('rest_status_field_name') => FALSE,
710: $this->config->item('rest_message_field_name') => [
711: 'classname' => get_class($ex),
712: 'message' => $ex->getMessage()
713: ]
714: ], self::HTTP_INTERNAL_SERVER_ERROR);
715: }
716: }
717:
718: 719: 720: 721: 722: 723: 724: 725: 726:
727: public function response($data = NULL, $http_code = NULL, $continue = FALSE)
728: {
729:
730: if ($http_code !== NULL)
731: {
732:
733: $http_code = (int) $http_code;
734: }
735:
736:
737: $output = NULL;
738:
739:
740: if ($data === NULL && $http_code === NULL)
741: {
742: $http_code = self::HTTP_NOT_FOUND;
743: }
744:
745:
746: elseif ($data !== NULL)
747: {
748:
749: if (method_exists($this->format, 'to_' . $this->response->format))
750: {
751:
752: $this->output->set_content_type($this->_supported_formats[$this->response->format], strtolower($this->config->item('charset')));
753: $output = $this->format->factory($data)->{'to_' . $this->response->format}();
754:
755:
756:
757: if ($this->response->format === 'array')
758: {
759: $output = $this->format->factory($output)->{'to_json'}();
760: }
761: }
762: else
763: {
764:
765: if (is_array($data) || is_object($data))
766: {
767: $data = $this->format->factory($data)->{'to_json'}();
768: }
769:
770:
771: $output = $data;
772: }
773: }
774:
775:
776:
777:
778: $http_code > 0 || $http_code = self::HTTP_OK;
779:
780: $this->output->set_status_header($http_code);
781:
782:
783: if ($this->config->item('rest_enable_logging') === TRUE)
784: {
785: $this->_log_response_code($http_code);
786: }
787:
788:
789: $this->output->set_output($output);
790:
791: if ($continue === FALSE)
792: {
793:
794: $this->output->_display();
795: exit;
796: }
797:
798:
799: }
800:
801: 802: 803: 804: 805: 806: 807: 808: 809: 810:
811: public function set_response($data = NULL, $http_code = NULL)
812: {
813: $this->response($data, $http_code, TRUE);
814: }
815:
816: 817: 818: 819: 820: 821:
822: protected function _detect_input_format()
823: {
824:
825: $content_type = $this->input->server('CONTENT_TYPE');
826:
827: if (empty($content_type) === FALSE)
828: {
829:
830:
831: $content_type = (strpos($content_type, ';') !== FALSE ? current(explode(';', $content_type)) : $content_type);
832:
833:
834: foreach ($this->_supported_formats as $type => $mime)
835: {
836:
837:
838:
839:
840: if ($content_type === $mime)
841: {
842: return $type;
843: }
844: }
845: }
846:
847: return NULL;
848: }
849:
850: 851: 852: 853: 854: 855: 856: 857:
858: protected function _get_default_output_format()
859: {
860: $default_format = (string) $this->config->item('rest_default_format');
861: return $default_format === '' ? 'json' : $default_format;
862: }
863:
864: 865: 866: 867: 868: 869:
870: protected function _detect_output_format()
871: {
872:
873: $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')($|\/)/';
874: $matches = [];
875:
876:
877: if (preg_match($pattern, $this->uri->uri_string(), $matches))
878: {
879: return $matches[1];
880: }
881:
882:
883: if (isset($this->_get_args['format']))
884: {
885: $format = strtolower($this->_get_args['format']);
886:
887: if (isset($this->_supported_formats[$format]) === TRUE)
888: {
889: return $format;
890: }
891: }
892:
893:
894: $http_accept = $this->input->server('HTTP_ACCEPT');
895:
896:
897: if ($this->config->item('rest_ignore_http_accept') === FALSE && $http_accept !== NULL)
898: {
899:
900: foreach (array_keys($this->_supported_formats) as $format)
901: {
902:
903: if (strpos($http_accept, $format) !== FALSE)
904: {
905: if ($format !== 'html' && $format !== 'xml')
906: {
907:
908: return $format;
909: }
910: elseif ($format === 'html' && strpos($http_accept, 'xml') === FALSE)
911: {
912:
913:
914: return $format;
915: }
916: else if ($format === 'xml' && strpos($http_accept, 'html') === FALSE)
917: {
918:
919: return $format;
920: }
921: }
922: }
923: }
924:
925:
926: if (empty($this->rest_format) === FALSE)
927: {
928: return $this->rest_format;
929: }
930:
931:
932: return $this->_get_default_output_format();
933: }
934:
935: 936: 937: 938: 939: 940:
941: protected function _detect_method()
942: {
943:
944: $method = NULL;
945:
946:
947: if ($this->config->item('enable_emulate_request') === TRUE)
948: {
949: $method = $this->input->post('_method');
950: if ($method === NULL)
951: {
952: $method = $this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE');
953: }
954:
955: $method = strtolower($method);
956: }
957:
958: if (empty($method))
959: {
960:
961: $method = $this->input->method();
962: }
963:
964: return in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method) ? $method : 'get';
965: }
966:
967: 968: 969: 970: 971: 972:
973: protected function _detect_api_key()
974: {
975:
976: $api_key_variable = $this->config->item('rest_key_name');
977:
978:
979: $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', $api_key_variable));
980:
981: $this->rest->key = NULL;
982: $this->rest->level = NULL;
983: $this->rest->user_id = NULL;
984: $this->rest->ignore_limits = FALSE;
985:
986:
987: if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name)))
988: {
989: if ( ! ($row = $this->rest->db->where($this->config->item('rest_key_column'), $key)->get($this->config->item('rest_keys_table'))->row()))
990: {
991: return FALSE;
992: }
993:
994: $this->rest->key = $row->{$this->config->item('rest_key_column')};
995:
996: isset($row->user_id) && $this->rest->user_id = $row->user_id;
997: isset($row->level) && $this->rest->level = $row->level;
998: isset($row->ignore_limits) && $this->rest->ignore_limits = $row->ignore_limits;
999:
1000: $this->_apiuser = $row;
1001:
1002: 1003: 1004: 1005:
1006: if (empty($row->is_private_key) === FALSE)
1007: {
1008:
1009: if (isset($row->ip_addresses))
1010: {
1011:
1012: $list_ip_addresses = explode(',', $row->ip_addresses);
1013: $found_address = FALSE;
1014:
1015: foreach ($list_ip_addresses as $ip_address)
1016: {
1017: if ($this->input->ip_address() === trim($ip_address))
1018: {
1019:
1020: $found_address = TRUE;
1021: break;
1022: }
1023: }
1024:
1025: return $found_address;
1026: }
1027: else
1028: {
1029:
1030: return FALSE;
1031: }
1032: }
1033:
1034: return TRUE;
1035: }
1036:
1037:
1038: return FALSE;
1039: }
1040:
1041: 1042: 1043: 1044: 1045: 1046:
1047: protected function _detect_lang()
1048: {
1049: $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE');
1050: if ($lang === NULL)
1051: {
1052: return NULL;
1053: }
1054:
1055:
1056: if (strpos($lang, ',') !== FALSE)
1057: {
1058: $langs = explode(',', $lang);
1059:
1060: $return_langs = [];
1061: foreach ($langs as $lang)
1062: {
1063:
1064: list($lang) = explode(';', $lang);
1065: $return_langs[] = trim($lang);
1066: }
1067:
1068: return $return_langs;
1069: }
1070:
1071:
1072: return $lang;
1073: }
1074:
1075: 1076: 1077: 1078: 1079: 1080: 1081:
1082: protected function _log_request($authorized = FALSE)
1083: {
1084:
1085: $is_inserted = $this->rest->db
1086: ->insert(
1087: $this->config->item('rest_logs_table'), [
1088: 'uri' => $this->uri->uri_string(),
1089: 'method' => $this->request->method,
1090: 'params' => $this->_args ? ($this->config->item('rest_logs_json_params') === TRUE ? json_encode($this->_args) : serialize($this->_args)) : NULL,
1091: 'api_key' => isset($this->rest->key) ? $this->rest->key : '',
1092: 'ip_address' => $this->input->ip_address(),
1093: 'time' => time(),
1094: 'authorized' => $authorized
1095: ]);
1096:
1097:
1098: $this->_insert_id = $this->rest->db->insert_id();
1099:
1100: return $is_inserted;
1101: }
1102:
1103: 1104: 1105: 1106: 1107: 1108: 1109:
1110: protected function _check_limit($controller_method)
1111: {
1112:
1113: if (empty($this->rest->ignore_limits) === FALSE)
1114: {
1115:
1116: return TRUE;
1117: }
1118:
1119: switch ($this->config->item('rest_limits_method'))
1120: {
1121: case 'API_KEY':
1122: $limited_uri = 'api-key:' . (isset($this->rest->key) ? $this->rest->key : '');
1123: $limited_method_name = isset($this->rest->key) ? $this->rest->key : '';
1124: break;
1125:
1126: case 'METHOD_NAME':
1127: $limited_uri = 'method-name:' . $controller_method;
1128: $limited_method_name = $controller_method;
1129: break;
1130:
1131: case 'ROUTED_URL':
1132: default:
1133: $limited_uri = $this->uri->ruri_string();
1134: if (strpos(strrev($limited_uri), strrev($this->response->format)) === 0)
1135: {
1136: $limited_uri = substr($limited_uri,0, -strlen($this->response->format) - 1);
1137: }
1138: $limited_uri = 'uri:'.$limited_uri.':'.$this->request->method;
1139: $limited_method_name = $controller_method;
1140: break;
1141: }
1142:
1143: if (isset($this->methods[$limited_method_name]['limit']) === FALSE )
1144: {
1145:
1146: return TRUE;
1147: }
1148:
1149:
1150: $limit = $this->methods[$limited_method_name]['limit'];
1151:
1152: $time_limit = (isset($this->methods[$limited_method_name]['time']) ? $this->methods[$limited_method_name]['time'] : 3600);
1153:
1154:
1155: $result = $this->rest->db
1156: ->where('uri', $limited_uri)
1157: ->where('api_key', $this->rest->key)
1158: ->get($this->config->item('rest_limits_table'))
1159: ->row();
1160:
1161:
1162: if ($result === NULL)
1163: {
1164:
1165: $this->rest->db->insert($this->config->item('rest_limits_table'), [
1166: 'uri' => $limited_uri,
1167: 'api_key' => isset($this->rest->key) ? $this->rest->key : '',
1168: 'count' => 1,
1169: 'hour_started' => time()
1170: ]);
1171: }
1172:
1173:
1174: elseif ($result->hour_started < (time() - $time_limit))
1175: {
1176:
1177: $this->rest->db
1178: ->where('uri', $limited_uri)
1179: ->where('api_key', isset($this->rest->key) ? $this->rest->key : '')
1180: ->set('hour_started', time())
1181: ->set('count', 1)
1182: ->update($this->config->item('rest_limits_table'));
1183: }
1184:
1185:
1186: else
1187: {
1188:
1189: if ($result->count >= $limit)
1190: {
1191: return FALSE;
1192: }
1193:
1194:
1195: $this->rest->db
1196: ->where('uri', $limited_uri)
1197: ->where('api_key', $this->rest->key)
1198: ->set('count', 'count + 1', FALSE)
1199: ->update($this->config->item('rest_limits_table'));
1200: }
1201:
1202: return TRUE;
1203: }
1204:
1205: 1206: 1207: 1208: 1209: 1210:
1211: protected function _auth_override_check()
1212: {
1213:
1214: $auth_override_class_method = $this->config->item('auth_override_class_method');
1215:
1216:
1217: if ( ! empty($auth_override_class_method))
1218: {
1219:
1220: if ( ! empty($auth_override_class_method[$this->router->class]['*']))
1221: {
1222:
1223: if ($auth_override_class_method[$this->router->class]['*'] === 'none')
1224: {
1225: return TRUE;
1226: }
1227:
1228:
1229: if ($auth_override_class_method[$this->router->class]['*'] === 'basic')
1230: {
1231: $this->_prepare_basic_auth();
1232:
1233: return TRUE;
1234: }
1235:
1236:
1237: if ($auth_override_class_method[$this->router->class]['*'] === 'digest')
1238: {
1239: $this->_prepare_digest_auth();
1240:
1241: return TRUE;
1242: }
1243:
1244:
1245: if ($auth_override_class_method[$this->router->class]['*'] === 'session')
1246: {
1247: $this->_check_php_session();
1248:
1249: return TRUE;
1250: }
1251:
1252:
1253: if ($auth_override_class_method[$this->router->class]['*'] === 'whitelist')
1254: {
1255: $this->_check_whitelist_auth();
1256:
1257: return TRUE;
1258: }
1259: }
1260:
1261:
1262: if ( ! empty($auth_override_class_method[$this->router->class][$this->router->method]))
1263: {
1264:
1265: if ($auth_override_class_method[$this->router->class][$this->router->method] === 'none')
1266: {
1267: return TRUE;
1268: }
1269:
1270:
1271: if ($auth_override_class_method[$this->router->class][$this->router->method] === 'basic')
1272: {
1273: $this->_prepare_basic_auth();
1274:
1275: return TRUE;
1276: }
1277:
1278:
1279: if ($auth_override_class_method[$this->router->class][$this->router->method] === 'digest')
1280: {
1281: $this->_prepare_digest_auth();
1282:
1283: return TRUE;
1284: }
1285:
1286:
1287: if ($auth_override_class_method[$this->router->class][$this->router->method] === 'session')
1288: {
1289: $this->_check_php_session();
1290:
1291: return TRUE;
1292: }
1293:
1294:
1295: if ($auth_override_class_method[$this->router->class][$this->router->method] === 'whitelist')
1296: {
1297: $this->_check_whitelist_auth();
1298:
1299: return TRUE;
1300: }
1301: }
1302: }
1303:
1304:
1305: $auth_override_class_method_http = $this->config->item('auth_override_class_method_http');
1306:
1307:
1308: if ( ! empty($auth_override_class_method_http))
1309: {
1310:
1311: if ( ! empty($auth_override_class_method_http[$this->router->class]['*'][$this->request->method]))
1312: {
1313:
1314: if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'none')
1315: {
1316: return TRUE;
1317: }
1318:
1319:
1320: if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'basic')
1321: {
1322: $this->_prepare_basic_auth();
1323:
1324: return TRUE;
1325: }
1326:
1327:
1328: if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'digest')
1329: {
1330: $this->_prepare_digest_auth();
1331:
1332: return TRUE;
1333: }
1334:
1335:
1336: if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'session')
1337: {
1338: $this->_check_php_session();
1339:
1340: return TRUE;
1341: }
1342:
1343:
1344: if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'whitelist')
1345: {
1346: $this->_check_whitelist_auth();
1347:
1348: return TRUE;
1349: }
1350: }
1351:
1352:
1353: if ( ! empty($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method]))
1354: {
1355:
1356: if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'none')
1357: {
1358: return TRUE;
1359: }
1360:
1361:
1362: if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'basic')
1363: {
1364: $this->_prepare_basic_auth();
1365:
1366: return TRUE;
1367: }
1368:
1369:
1370: if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'digest')
1371: {
1372: $this->_prepare_digest_auth();
1373:
1374: return TRUE;
1375: }
1376:
1377:
1378: if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'session')
1379: {
1380: $this->_check_php_session();
1381:
1382: return TRUE;
1383: }
1384:
1385:
1386: if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'whitelist')
1387: {
1388: $this->_check_whitelist_auth();
1389:
1390: return TRUE;
1391: }
1392: }
1393: }
1394: return FALSE;
1395: }
1396:
1397: 1398: 1399: 1400: 1401: 1402:
1403: protected function _parse_get()
1404: {
1405:
1406: $this->_get_args = array_merge($this->_get_args, $this->_query_args);
1407: }
1408:
1409: 1410: 1411: 1412: 1413: 1414:
1415: protected function _parse_post()
1416: {
1417: $this->_post_args = $_POST;
1418:
1419: if ($this->request->format)
1420: {
1421: $this->request->body = $this->input->raw_input_stream;
1422: }
1423: }
1424:
1425: 1426: 1427: 1428: 1429: 1430:
1431: protected function _parse_put()
1432: {
1433: if ($this->request->format)
1434: {
1435: $this->request->body = $this->input->raw_input_stream;
1436: }
1437: else if ($this->input->method() === 'put')
1438: {
1439:
1440: $this->_put_args = $this->input->input_stream();
1441: }
1442: }
1443:
1444: 1445: 1446: 1447: 1448: 1449:
1450: protected function _parse_head()
1451: {
1452:
1453: parse_str(parse_url($this->input->server('REQUEST_URI'), PHP_URL_QUERY), $head);
1454:
1455:
1456: $this->_head_args = array_merge($this->_head_args, $head);
1457: }
1458:
1459: 1460: 1461: 1462: 1463: 1464:
1465: protected function _parse_options()
1466: {
1467:
1468: parse_str(parse_url($this->input->server('REQUEST_URI'), PHP_URL_QUERY), $options);
1469:
1470:
1471: $this->_options_args = array_merge($this->_options_args, $options);
1472: }
1473:
1474: 1475: 1476: 1477: 1478: 1479:
1480: protected function _parse_patch()
1481: {
1482:
1483: if ($this->request->format)
1484: {
1485: $this->request->body = $this->input->raw_input_stream;
1486: }
1487: else if ($this->input->method() === 'patch')
1488: {
1489:
1490: $this->_patch_args = $this->input->input_stream();
1491: }
1492: }
1493:
1494: 1495: 1496: 1497: 1498: 1499:
1500: protected function _parse_delete()
1501: {
1502:
1503: if ($this->input->method() === 'delete')
1504: {
1505: $this->_delete_args = $this->input->input_stream();
1506: }
1507: }
1508:
1509: 1510: 1511: 1512: 1513: 1514:
1515: protected function _parse_query()
1516: {
1517: $this->_query_args = $this->input->get();
1518: }
1519:
1520:
1521:
1522: 1523: 1524: 1525: 1526: 1527: 1528: 1529: 1530:
1531: public function get($key = NULL, $xss_clean = NULL)
1532: {
1533: if ($key === NULL)
1534: {
1535: return $this->_get_args;
1536: }
1537:
1538: return isset($this->_get_args[$key]) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : NULL;
1539: }
1540:
1541: 1542: 1543: 1544: 1545: 1546: 1547: 1548: 1549:
1550: public function options($key = NULL, $xss_clean = NULL)
1551: {
1552: if ($key === NULL)
1553: {
1554: return $this->_options_args;
1555: }
1556:
1557: return isset($this->_options_args[$key]) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : NULL;
1558: }
1559:
1560: 1561: 1562: 1563: 1564: 1565: 1566: 1567: 1568:
1569: public function head($key = NULL, $xss_clean = NULL)
1570: {
1571: if ($key === NULL)
1572: {
1573: return $this->_head_args;
1574: }
1575:
1576: return isset($this->_head_args[$key]) ? $this->_xss_clean($this->_head_args[$key], $xss_clean) : NULL;
1577: }
1578:
1579: 1580: 1581: 1582: 1583: 1584: 1585: 1586: 1587:
1588: public function post($key = NULL, $xss_clean = NULL)
1589: {
1590: if ($key === NULL)
1591: {
1592: return $this->_post_args;
1593: }
1594:
1595: return isset($this->_post_args[$key]) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : NULL;
1596: }
1597:
1598: 1599: 1600: 1601: 1602: 1603: 1604: 1605: 1606:
1607: public function put($key = NULL, $xss_clean = NULL)
1608: {
1609: if ($key === NULL)
1610: {
1611: return $this->_put_args;
1612: }
1613:
1614: return isset($this->_put_args[$key]) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : NULL;
1615: }
1616:
1617: 1618: 1619: 1620: 1621: 1622: 1623: 1624: 1625:
1626: public function delete($key = NULL, $xss_clean = NULL)
1627: {
1628: if ($key === NULL)
1629: {
1630: return $this->_delete_args;
1631: }
1632:
1633: return isset($this->_delete_args[$key]) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : NULL;
1634: }
1635:
1636: 1637: 1638: 1639: 1640: 1641: 1642: 1643: 1644:
1645: public function patch($key = NULL, $xss_clean = NULL)
1646: {
1647: if ($key === NULL)
1648: {
1649: return $this->_patch_args;
1650: }
1651:
1652: return isset($this->_patch_args[$key]) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : NULL;
1653: }
1654:
1655: 1656: 1657: 1658: 1659: 1660: 1661: 1662: 1663:
1664: public function query($key = NULL, $xss_clean = NULL)
1665: {
1666: if ($key === NULL)
1667: {
1668: return $this->_query_args;
1669: }
1670:
1671: return isset($this->_query_args[$key]) ? $this->_xss_clean($this->_query_args[$key], $xss_clean) : NULL;
1672: }
1673:
1674: 1675: 1676: 1677: 1678: 1679: 1680: 1681: 1682:
1683: protected function _xss_clean($value, $xss_clean)
1684: {
1685: is_bool($xss_clean) || $xss_clean = $this->_enable_xss;
1686:
1687: return $xss_clean === TRUE ? $this->security->xss_clean($value) : $value;
1688: }
1689:
1690: 1691: 1692: 1693: 1694: 1695:
1696: public function validation_errors()
1697: {
1698: $string = strip_tags($this->form_validation->error_string());
1699:
1700: return explode(PHP_EOL, trim($string, PHP_EOL));
1701: }
1702:
1703:
1704:
1705: 1706: 1707: 1708: 1709: 1710: 1711: 1712:
1713: protected function _perform_ldap_auth($username = '', $password = NULL)
1714: {
1715: if (empty($username))
1716: {
1717: log_message('debug', 'LDAP Auth: failure, empty username');
1718: return FALSE;
1719: }
1720:
1721: log_message('debug', 'LDAP Auth: Loading configuration');
1722:
1723: $this->config->load('ldap.php', TRUE);
1724:
1725: $ldap = [
1726: 'timeout' => $this->config->item('timeout', 'ldap'),
1727: 'host' => $this->config->item('server', 'ldap'),
1728: 'port' => $this->config->item('port', 'ldap'),
1729: 'rdn' => $this->config->item('binduser', 'ldap'),
1730: 'pass' => $this->config->item('bindpw', 'ldap'),
1731: 'basedn' => $this->config->item('basedn', 'ldap'),
1732: ];
1733:
1734: log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]'));
1735:
1736:
1737: $ldapconn = ldap_connect($ldap['host'], $ldap['port']);
1738: if ($ldapconn)
1739: {
1740: log_message('debug', 'Setting timeout to '.$ldap['timeout'].' seconds');
1741:
1742: ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldap['timeout']);
1743:
1744: log_message('debug', 'LDAP Auth: Binding to '.$ldap['host'].' with dn '.$ldap['rdn']);
1745:
1746:
1747: $ldapbind = ldap_bind($ldapconn, $ldap['rdn'], $ldap['pass']);
1748:
1749:
1750: if ($ldapbind === FALSE)
1751: {
1752: log_message('error', 'LDAP Auth: bind was unsuccessful');
1753: return FALSE;
1754: }
1755:
1756: log_message('debug', 'LDAP Auth: bind successful');
1757: }
1758:
1759:
1760: if (($res_id = ldap_search($ldapconn, $ldap['basedn'], "uid=$username")) === FALSE)
1761: {
1762: log_message('error', 'LDAP Auth: User '.$username.' not found in search');
1763: return FALSE;
1764: }
1765:
1766: if (ldap_count_entries($ldapconn, $res_id) !== 1)
1767: {
1768: log_message('error', 'LDAP Auth: Failure, username '.$username.'found more than once');
1769: return FALSE;
1770: }
1771:
1772: if (($entry_id = ldap_first_entry($ldapconn, $res_id)) === FALSE)
1773: {
1774: log_message('error', 'LDAP Auth: Failure, entry of search result could not be fetched');
1775: return FALSE;
1776: }
1777:
1778: if (($user_dn = ldap_get_dn($ldapconn, $entry_id)) === FALSE)
1779: {
1780: log_message('error', 'LDAP Auth: Failure, user-dn could not be fetched');
1781: return FALSE;
1782: }
1783:
1784:
1785: if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) === FALSE)
1786: {
1787: log_message('error', 'LDAP Auth: Failure, username/password did not match: ' . $user_dn);
1788: return FALSE;
1789: }
1790:
1791: log_message('debug', 'LDAP Auth: Success '.$user_dn.' authenticated successfully');
1792:
1793: $this->_user_ldap_dn = $user_dn;
1794:
1795: ldap_close($ldapconn);
1796:
1797: return TRUE;
1798: }
1799:
1800: 1801: 1802: 1803: 1804: 1805: 1806: 1807:
1808: protected function _perform_library_auth($username = '', $password = NULL)
1809: {
1810: if (empty($username))
1811: {
1812: log_message('error', 'Library Auth: Failure, empty username');
1813: return FALSE;
1814: }
1815:
1816: $auth_library_class = strtolower($this->config->item('auth_library_class'));
1817: $auth_library_function = strtolower($this->config->item('auth_library_function'));
1818:
1819: if (empty($auth_library_class))
1820: {
1821: log_message('debug', 'Library Auth: Failure, empty auth_library_class');
1822: return FALSE;
1823: }
1824:
1825: if (empty($auth_library_function))
1826: {
1827: log_message('debug', 'Library Auth: Failure, empty auth_library_function');
1828: return FALSE;
1829: }
1830:
1831: if (is_callable([$auth_library_class, $auth_library_function]) === FALSE)
1832: {
1833: $this->load->library($auth_library_class);
1834: }
1835:
1836: return $this->{$auth_library_class}->$auth_library_function($username, $password);
1837: }
1838:
1839: 1840: 1841: 1842: 1843: 1844: 1845: 1846:
1847: protected function _check_login($username = NULL, $password = FALSE)
1848: {
1849: if (empty($username))
1850: {
1851: return FALSE;
1852: }
1853:
1854: $auth_source = strtolower($this->config->item('auth_source'));
1855: $rest_auth = strtolower($this->config->item('rest_auth'));
1856: $valid_logins = $this->config->item('rest_valid_logins');
1857:
1858: if ( ! $this->config->item('auth_source') && $rest_auth === 'digest')
1859: {
1860:
1861: return md5($username.':'.$this->config->item('rest_realm').':'.(isset($valid_logins[$username]) ? $valid_logins[$username] : ''));
1862: }
1863:
1864: if ($password === FALSE)
1865: {
1866: return FALSE;
1867: }
1868:
1869: if ($auth_source === 'ldap')
1870: {
1871: log_message('debug', "Performing LDAP authentication for $username");
1872:
1873: return $this->_perform_ldap_auth($username, $password);
1874: }
1875:
1876: if ($auth_source === 'library')
1877: {
1878: log_message('debug', "Performing Library authentication for $username");
1879:
1880: return $this->_perform_library_auth($username, $password);
1881: }
1882:
1883: if (array_key_exists($username, $valid_logins) === FALSE)
1884: {
1885: return FALSE;
1886: }
1887:
1888: if ($valid_logins[$username] !== $password)
1889: {
1890: return FALSE;
1891: }
1892:
1893: return TRUE;
1894: }
1895:
1896: 1897: 1898: 1899: 1900: 1901:
1902: protected function _check_php_session()
1903: {
1904:
1905: $key = $this->config->item('auth_source');
1906:
1907:
1908: if ( ! $this->session->userdata($key))
1909: {
1910:
1911: $this->response([
1912: $this->config->item('rest_status_field_name') => FALSE,
1913: $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unauthorized')
1914: ], self::HTTP_UNAUTHORIZED);
1915: }
1916: }
1917:
1918: 1919: 1920: 1921: 1922: 1923:
1924: protected function _prepare_basic_auth()
1925: {
1926:
1927: if ($this->config->item('rest_ip_whitelist_enabled'))
1928: {
1929: $this->_check_whitelist_auth();
1930: }
1931:
1932:
1933: $username = $this->input->server('PHP_AUTH_USER');
1934: $http_auth = $this->input->server('HTTP_AUTHENTICATION');
1935:
1936: $password = NULL;
1937: if ($username !== NULL)
1938: {
1939: $password = $this->input->server('PHP_AUTH_PW');
1940: }
1941: elseif ($http_auth !== NULL)
1942: {
1943:
1944:
1945: if (strpos(strtolower($http_auth), 'basic') === 0)
1946: {
1947:
1948: list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6)));
1949: }
1950: }
1951:
1952:
1953: if ($this->_check_login($username, $password) === FALSE)
1954: {
1955: $this->_force_login();
1956: }
1957: }
1958:
1959: 1960: 1961: 1962: 1963: 1964:
1965: protected function _prepare_digest_auth()
1966: {
1967:
1968: if ($this->config->item('rest_ip_whitelist_enabled'))
1969: {
1970: $this->_check_whitelist_auth();
1971: }
1972:
1973:
1974:
1975: $digest_string = $this->input->server('PHP_AUTH_DIGEST');
1976: if ($digest_string === NULL)
1977: {
1978: $digest_string = $this->input->server('HTTP_AUTHORIZATION');
1979: }
1980:
1981: $unique_id = uniqid();
1982:
1983:
1984:
1985: if (empty($digest_string))
1986: {
1987: $this->_force_login($unique_id);
1988: }
1989:
1990:
1991: $matches = [];
1992: preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches);
1993: $digest = (empty($matches[1]) || empty($matches[2])) ? [] : array_combine($matches[1], $matches[2]);
1994:
1995:
1996: $username = $this->_check_login($digest['username'], TRUE);
1997: if (array_key_exists('username', $digest) === FALSE || $username === FALSE)
1998: {
1999: $this->_force_login($unique_id);
2000: }
2001:
2002: $md5 = md5(strtoupper($this->request->method).':'.$digest['uri']);
2003: $valid_response = md5($username.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$md5);
2004:
2005:
2006: if (strcasecmp($digest['response'], $valid_response) !== 0)
2007: {
2008:
2009: $this->response([
2010: $this->config->item('rest_status_field_name') => FALSE,
2011: $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_invalid_credentials')
2012: ], self::HTTP_UNAUTHORIZED);
2013: }
2014: }
2015:
2016: 2017: 2018: 2019: 2020: 2021:
2022: protected function _check_blacklist_auth()
2023: {
2024:
2025: $pattern = sprintf('/(?:,\s*|^)\Q%s\E(?=,\s*|$)/m', $this->input->ip_address());
2026:
2027:
2028: if (preg_match($pattern, $this->config->item('rest_ip_blacklist')))
2029: {
2030:
2031: $this->response([
2032: $this->config->item('rest_status_field_name') => FALSE,
2033: $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_denied')
2034: ], self::HTTP_UNAUTHORIZED);
2035: }
2036: }
2037:
2038: 2039: 2040: 2041: 2042: 2043:
2044: protected function _check_whitelist_auth()
2045: {
2046: $whitelist = explode(',', $this->config->item('rest_ip_whitelist'));
2047:
2048: array_push($whitelist, '127.0.0.1', '0.0.0.0');
2049:
2050: foreach ($whitelist as &$ip)
2051: {
2052:
2053:
2054: $ip = trim($ip);
2055: }
2056:
2057: if (in_array($this->input->ip_address(), $whitelist) === FALSE)
2058: {
2059: $this->response([
2060: $this->config->item('rest_status_field_name') => FALSE,
2061: $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_unauthorized')
2062: ], self::HTTP_UNAUTHORIZED);
2063: }
2064: }
2065:
2066: 2067: 2068: 2069: 2070: 2071: 2072: 2073:
2074: protected function _force_login($nonce = '')
2075: {
2076: $rest_auth = $this->config->item('rest_auth');
2077: $rest_realm = $this->config->item('rest_realm');
2078: if (strtolower($rest_auth) === 'basic')
2079: {
2080:
2081: header('WWW-Authenticate: Basic realm="'.$rest_realm.'"');
2082: }
2083: elseif (strtolower($rest_auth) === 'digest')
2084: {
2085:
2086: header(
2087: 'WWW-Authenticate: Digest realm="'.$rest_realm
2088: .'", qop="auth", nonce="'.$nonce
2089: .'", opaque="' . md5($rest_realm).'"');
2090: }
2091:
2092:
2093: $this->response([
2094: $this->config->item('rest_status_field_name') => FALSE,
2095: $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unauthorized')
2096: ], self::HTTP_UNAUTHORIZED);
2097: }
2098:
2099: 2100: 2101: 2102: 2103: 2104: 2105:
2106: protected function _log_access_time()
2107: {
2108: $payload['rtime'] = $this->_end_rtime - $this->_start_rtime;
2109:
2110: return $this->rest->db->update(
2111: $this->config->item('rest_logs_table'), $payload, [
2112: 'id' => $this->_insert_id
2113: ]);
2114: }
2115:
2116: 2117: 2118: 2119: 2120: 2121: 2122: 2123:
2124: protected function _log_response_code($http_code)
2125: {
2126: $payload['response_code'] = $http_code;
2127:
2128: return $this->rest->db->update(
2129: $this->config->item('rest_logs_table'), $payload, [
2130: 'id' => $this->_insert_id
2131: ]);
2132: }
2133:
2134: 2135: 2136: 2137: 2138: 2139:
2140: protected function _check_access()
2141: {
2142:
2143: if ($this->config->item('rest_enable_access') === FALSE)
2144: {
2145: return TRUE;
2146: }
2147:
2148:
2149: $controller = implode(
2150: '/', [
2151: $this->router->directory,
2152: $this->router->class
2153: ]);
2154:
2155:
2156: $controller = str_replace('//', '/', $controller);
2157:
2158:
2159: return $this->rest->db
2160: ->where('key', $this->rest->key)
2161: ->where('controller', $controller)
2162: ->get($this->config->item('rest_access_table'))
2163: ->num_rows() > 0;
2164: }
2165:
2166: 2167: 2168: 2169: 2170: 2171:
2172: protected function _check_cors()
2173: {
2174:
2175: $allowed_headers = implode(' ,', $this->config->item('allowed_cors_headers'));
2176: $allowed_methods = implode(' ,', $this->config->item('allowed_cors_methods'));
2177:
2178:
2179: if ($this->config->item('allow_any_cors_domain') === TRUE)
2180: {
2181: header('Access-Control-Allow-Origin: *');
2182: header('Access-Control-Allow-Headers: '.$allowed_headers);
2183: header('Access-Control-Allow-Methods: '.$allowed_methods);
2184: }
2185: else
2186: {
2187:
2188:
2189: $origin = $this->input->server('HTTP_ORIGIN');
2190: if ($origin === NULL)
2191: {
2192: $origin = '';
2193: }
2194:
2195:
2196: if (in_array($origin, $this->config->item('allowed_cors_origins')))
2197: {
2198: header('Access-Control-Allow-Origin: '.$origin);
2199: header('Access-Control-Allow-Headers: '.$allowed_headers);
2200: header('Access-Control-Allow-Methods: '.$allowed_methods);
2201: }
2202: }
2203:
2204:
2205: if ($this->input->method() === 'options')
2206: {
2207: exit;
2208: }
2209: }
2210:
2211: }
2212: