1: <?php defined('BASEPATH') or exit('No direct script access allowed');
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16: abstract class REST_Controller extends CI_Controller
17: {
18: 19: 20: 21: 22: 23: 24:
25: protected $rest_format = NULL;
26:
27: 28: 29: 30: 31:
32: protected $methods = array();
33:
34: 35: 36: 37: 38:
39: protected $allowed_http_methods = array('get', 'delete', 'post', 'put', 'options', 'patch', 'head');
40:
41: 42: 43: 44: 45: 46:
47: protected $request = NULL;
48:
49: 50: 51: 52: 53:
54: protected $response = NULL;
55:
56: 57: 58: 59: 60:
61: protected $rest = NULL;
62:
63: 64: 65: 66: 67:
68: protected $_get_args = array();
69:
70: 71: 72: 73: 74:
75: protected $_post_args = array();
76:
77: 78: 79: 80: 81:
82: protected $_insert_id = '';
83:
84: 85: 86: 87: 88:
89: protected $_put_args = array();
90:
91: 92: 93: 94: 95:
96: protected $_delete_args = array();
97:
98: 99: 100: 101: 102:
103: protected $_patch_args = array();
104:
105: 106: 107: 108: 109:
110: protected $_head_args = array();
111:
112: 113: 114: 115: 116:
117: protected $_options_args = array();
118:
119: 120: 121: 122: 123:
124: protected $_args = array();
125:
126: 127: 128: 129: 130:
131: protected $_allow = TRUE;
132:
133: 134: 135: 136: 137:
138: protected $_zlib_oc = FALSE;
139:
140: 141: 142: 143: 144:
145: protected $_user_ldap_dn = '';
146:
147: 148: 149: 150: 151:
152: protected $_start_rtime = '';
153:
154: 155: 156: 157: 158:
159: protected $_end_rtime = '';
160:
161: 162: 163: 164: 165:
166: protected $_supported_formats = array(
167: 'xml' => 'application/xml',
168: 'json' => 'application/json',
169: 'jsonp' => 'application/javascript',
170: 'serialized' => 'application/vnd.php.serialized',
171: 'php' => 'text/plain',
172: 'html' => 'text/html',
173: 'csv' => 'application/csv'
174: );
175:
176: 177: 178: 179: 180:
181: protected $_apiuser;
182:
183: 184: 185:
186: protected function early_checks()
187: {
188:
189: }
190:
191: 192: 193: 194:
195: public function __construct($config = 'rest')
196: {
197: parent::__construct();
198:
199:
200: $this->_start_rtime = microtime(TRUE);
201:
202:
203: $this->load->config($config);
204:
205:
206: $this->load->library('format');
207:
208:
209: $this->response = new stdClass();
210: $this->rest = new stdClass();
211:
212: $this->_zlib_oc = @ini_get('zlib.output_compression');
213:
214:
215: $this->request = new stdClass();
216:
217:
218: if ($this->config->item('rest_ip_blacklist_enabled')) {
219: $this->_check_blacklist_auth();
220: }
221:
222:
223: $this->request->ssl = $this->_detect_ssl();
224:
225:
226: $this->request->method = $this->_detect_method();
227:
228:
229: if (!isset($this->{'_'.$this->request->method.'_args'})) {
230: $this->{'_'.$this->request->method.'_args'} = array();
231: }
232:
233:
234: $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc());
235:
236:
237: $this->request->format = $this->_detect_input_format();
238:
239:
240: $this->request->body = NULL;
241:
242: $this->{'_parse_' . $this->request->method}();
243:
244:
245: if ($this->request->format and $this->request->body) {
246: $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array();
247:
248: $this->{'_'.$this->request->method.'_args'} = $this->request->body;
249: }
250:
251:
252: $this->_args = array_merge($this->_get_args,
253: $this->_options_args,
254: $this->_patch_args,
255: $this->_head_args ,
256: $this->_put_args,
257: $this->_post_args,
258: $this->_delete_args,
259: $this->{'_'.$this->request->method.'_args'}
260: );
261:
262:
263: $this->response = new stdClass();
264: $this->response->format = $this->_detect_output_format();
265:
266:
267: $this->response->lang = $this->_detect_lang();
268:
269:
270: $this->early_checks();
271:
272: $this->rest = new StdClass();
273:
274:
275: if (config_item('rest_database_group') and (config_item('rest_enable_keys') or config_item('rest_enable_logging'))) {
276: $this->rest->db = $this->load->database(config_item('rest_database_group'), TRUE);
277: }
278:
279:
280: elseif (property_exists($this, "db")) {
281: $this->rest->db = $this->db;
282: }
283:
284:
285:
286: $this->auth_override = $this->_auth_override_check();
287:
288:
289:
290: if (config_item('rest_enable_keys') and $this->auth_override !== TRUE) {
291: $this->_allow = $this->_detect_api_key();
292: }
293:
294:
295: if (!$this->input->is_ajax_request() and config_item('rest_ajax_only')) {
296: $response = array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Only AJAX requests are accepted.');
297: $this->response($response, 406);
298: }
299:
300:
301: if ($this->auth_override !== TRUE && !(config_item('rest_enable_keys') && $this->_allow === TRUE)) {
302: $rest_auth = strtolower($this->config->item('rest_auth'));
303: switch ($rest_auth) {
304: case 'basic':
305: $this->_prepare_basic_auth();
306: break;
307: case 'digest':
308: $this->_prepare_digest_auth();
309: break;
310: case 'session':
311: $this->_check_php_session();
312: break;
313: }
314: if ($this->config->item('rest_ip_whitelist_enabled')) {
315: $this->_check_whitelist_auth();
316: }
317: }
318: }
319:
320: 321: 322: 323:
324: public function __destruct()
325: {
326:
327: $this->_end_rtime = microtime(TRUE);
328:
329: if (config_item('rest_enable_logging')) {
330: $this->_log_access_time();
331: }
332:
333: }
334:
335: 336: 337: 338: 339: 340: 341: 342: 343: 344:
345: public function _remap($object_called, $arguments)
346: {
347:
348: if (config_item('force_https') and !$this->_detect_ssl()) {
349: $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unsupported protocol'), 403);
350: }
351:
352: $pattern = '/^(.*)\.('.implode('|', array_keys($this->_supported_formats)).')$/';
353: $matches = array();
354: if (preg_match($pattern, $object_called, $matches)) {
355: $object_called = $matches[1];
356: }
357:
358: $controller_method = $object_called.'_'.$this->request->method;
359:
360:
361: $log_method = !(isset($this->methods[$controller_method]['log']) and $this->methods[$controller_method]['log'] == FALSE);
362:
363:
364: $use_key = !(isset($this->methods[$controller_method]['key']) and $this->methods[$controller_method]['key'] == FALSE);
365:
366:
367: if (config_item('rest_enable_keys') and $use_key and $this->_allow === FALSE) {
368: if (config_item('rest_enable_logging') and $log_method) {
369: $this->_log_request();
370: }
371:
372: $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Invalid API Key '.$this->rest->key), 403);
373: }
374:
375:
376: if (config_item('rest_enable_keys') and $use_key and !empty($this->rest->key) and !$this->_check_access()) {
377: if (config_item('rest_enable_logging') and $log_method) {
378: $this->_log_request();
379: }
380:
381: $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'This API key does not have access to the requested controller.'), 401);
382: }
383:
384:
385: if ( ! method_exists($this, $controller_method)) {
386: $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Unknown method.'), 404);
387: }
388:
389:
390: if (config_item('rest_enable_keys') and !empty($this->rest->key)) {
391:
392: if (config_item('rest_enable_limits') and !$this->_check_limit($controller_method)) {
393: $response = array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'This API key has reached the hourly limit for this method.');
394: $this->response($response, 401);
395: }
396:
397:
398: $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0;
399:
400:
401: $authorized = $level <= $this->rest->level;
402:
403:
404: if (config_item('rest_enable_logging') and $log_method) {
405: $this->_log_request($authorized);
406: }
407:
408:
409: $response = array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'This API key does not have enough permissions.');
410: $authorized or $this->response($response, 401);
411: }
412:
413:
414: else if (config_item('rest_enable_logging') and $log_method) {
415: $this->_log_request($authorized = TRUE);
416: }
417:
418:
419: $this->_fire_method(array($this, $controller_method), $arguments);
420: }
421:
422: 423: 424: 425: 426: 427: 428: 429:
430: protected function _fire_method($method, $args)
431: {
432: call_user_func_array($method, $args);
433: }
434:
435: 436: 437: 438: 439: 440: 441: 442: 443: 444:
445: public function response($data = NULL, $http_code = NULL, $continue = FALSE)
446: {
447:
448: if ($data === NULL && $http_code === NULL) {
449: $http_code = 404;
450:
451:
452: $output = NULL;
453: }
454:
455:
456: else if ($data === NULL && is_numeric($http_code)) {
457: $output = NULL;
458: }
459:
460:
461: else {
462:
463: if ($this->config->item('compress_output') === TRUE && $this->_zlib_oc == FALSE) {
464: if (extension_loaded('zlib')) {
465: if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) and strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) {
466: ob_start('ob_gzhandler');
467: }
468: }
469: }
470:
471: is_numeric($http_code) or $http_code = 200;
472:
473:
474:
475: if (method_exists($this, '_format_'.$this->response->format)) {
476:
477: header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset')));
478:
479: $output = $this->{'_format_'.$this->response->format}($data);
480: }
481:
482:
483: elseif (method_exists($this->format, 'to_'.$this->response->format)) {
484:
485: header('Content-Type: '.$this->_supported_formats[$this->response->format] . '; charset=' . strtolower($this->config->item('charset')));
486:
487: $output = $this->format->factory($data)->{'to_'.$this->response->format}();
488: }
489:
490:
491: else {
492: $output = $data;
493: }
494: }
495:
496: set_status_header($http_code);
497:
498:
499:
500:
501:
502: if ( ! $this->_zlib_oc && ! $this->config->item('compress_output')) {
503: header('Content-Length: ' . strlen($output));
504: }
505:
506: if($continue){
507: echo($output);
508: ob_end_flush();
509: ob_flush();
510: flush();
511: }else{
512: exit($output);
513: }
514: }
515:
516: 517: 518: 519: 520:
521: protected function _detect_ssl()
522: {
523:
524:
525: return (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']));
526: }
527:
528:
529: 530: 531: 532: 533:
534: protected function _detect_input_format()
535: {
536: if ($this->input->server('CONTENT_TYPE')) {
537:
538: foreach ($this->_supported_formats as $format => $mime) {
539: if (strpos($match = $this->input->server('CONTENT_TYPE'), ';')) {
540: $match = current(explode(';', $match));
541: }
542:
543: if ($match == $mime) {
544: return $format;
545: }
546: }
547: }
548:
549: return NULL;
550: }
551:
552: 553: 554: 555: 556: 557: 558:
559: protected function _detect_output_format()
560: {
561: $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')$/';
562:
563:
564: $matches = array();
565: if (!$this->_get_args and preg_match($pattern, $this->uri->uri_string(), $matches)) {
566: return $matches[1];
567: }
568:
569:
570: elseif ($this->_get_args and !is_array(end($this->_get_args)) and preg_match($pattern, end($this->_get_args), $matches)) {
571:
572:
573: $last_key = end(array_keys($this->_get_args));
574:
575:
576: $this->_get_args[$last_key] = preg_replace($pattern, '', $this->_get_args[$last_key]);
577: $this->_args[$last_key] = preg_replace($pattern, '', $this->_args[$last_key]);
578:
579: return $matches[1];
580: }
581:
582:
583: if (isset($this->_get_args['format']) and array_key_exists($this->_get_args['format'], $this->_supported_formats)) {
584: return $this->_get_args['format'];
585: }
586:
587:
588: if ($this->config->item('rest_ignore_http_accept') === FALSE and $this->input->server('HTTP_ACCEPT')) {
589:
590: foreach (array_keys($this->_supported_formats) as $format) {
591:
592: if (strpos($this->input->server('HTTP_ACCEPT'), $format) !== FALSE) {
593:
594: if ($format != 'html' and $format != 'xml') {
595: return $format;
596: }
597:
598:
599: else {
600:
601: if ($format == 'html' and strpos($this->input->server('HTTP_ACCEPT'), 'xml') === FALSE) {
602: return $format;
603: }
604:
605:
606: elseif ($format == 'xml' and strpos($this->input->server('HTTP_ACCEPT'), 'html') === FALSE) {
607: return $format;
608: }
609: }
610: }
611: }
612: }
613:
614:
615: if ( ! empty($this->rest_format)) {
616: return $this->rest_format;
617: }
618:
619:
620: return config_item('rest_default_format');
621: }
622:
623: 624: 625: 626: 627: 628: 629:
630: protected function _detect_method()
631: {
632: $method = strtolower($this->input->server('REQUEST_METHOD'));
633:
634: if ($this->config->item('enable_emulate_request')) {
635: if ($this->input->post('_method')) {
636: $method = strtolower($this->input->post('_method'));
637: } elseif ($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE')) {
638: $method = strtolower($this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE'));
639: }
640: }
641:
642: if (in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method)) {
643: return $method;
644: }
645:
646: return 'get';
647: }
648:
649: 650: 651: 652: 653: 654: 655:
656: protected function _detect_api_key()
657: {
658:
659: $api_key_variable = config_item('rest_key_name');
660:
661:
662: $key_name = 'HTTP_'.strtoupper(str_replace('-', '_', $api_key_variable));
663:
664: $this->rest->key = NULL;
665: $this->rest->level = NULL;
666: $this->rest->user_id = NULL;
667: $this->rest->ignore_limits = FALSE;
668:
669:
670: if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) {
671: if ( ! ($row = $this->rest->db->where(config_item('rest_key_column'), $key)->get(config_item('rest_keys_table'))->row())) {
672: return FALSE;
673: }
674:
675: $this->rest->key = $row->{config_item('rest_key_column')};
676:
677: isset($row->user_id) and $this->rest->user_id = $row->user_id;
678: isset($row->level) and $this->rest->level = $row->level;
679: isset($row->ignore_limits) and $this->rest->ignore_limits = $row->ignore_limits;
680:
681: $this->_apiuser = $row;
682:
683: 684: 685: 686:
687: if (!empty($row->is_private_key)) {
688:
689: if (isset($row->ip_addresses)) {
690:
691: $list_ip_addresses = explode(",", $row->ip_addresses);
692: $found_address = FALSE;
693:
694: foreach ($list_ip_addresses as $ip_address) {
695: if ($this->input->ip_address() == trim($ip_address)) {
696:
697: $found_address = TRUE;
698: break;
699: }
700: }
701:
702: return $found_address;
703: } else {
704:
705: return FALSE;
706: }
707: }
708:
709: return $row;
710: }
711:
712:
713: return FALSE;
714: }
715:
716: 717: 718: 719: 720: 721: 722:
723: protected function _detect_lang()
724: {
725: if ( ! $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE')) {
726: return NULL;
727: }
728:
729:
730: if (strpos($lang, ',') !== FALSE) {
731: $langs = explode(',', $lang);
732:
733: $return_langs = array();
734: foreach ($langs as $lang) {
735:
736: list($lang) = explode(';', $lang);
737: $return_langs[] = trim($lang);
738: }
739:
740: return $return_langs;
741: }
742:
743:
744: return $lang;
745: }
746:
747: 748: 749: 750: 751: 752: 753: 754:
755: protected function _log_request($authorized = FALSE)
756: {
757: $status = $this->rest->db->insert(config_item('rest_logs_table'), array(
758: 'uri' => $this->uri->uri_string(),
759: 'method' => $this->request->method,
760: 'params' => $this->_args ? (config_item('rest_logs_json_params') ? json_encode($this->_args) : serialize($this->_args)) : NULL,
761: 'api_key' => isset($this->rest->key) ? $this->rest->key : '',
762: 'ip_address' => $this->input->ip_address(),
763: 'time' => function_exists('now') ? now() : time(),
764: 'authorized' => $authorized
765: ));
766:
767: $this->_insert_id = $this->rest->db->insert_id();
768:
769: return $status;
770: }
771:
772: 773: 774: 775: 776: 777: 778: 779:
780: protected function _check_limit($controller_method)
781: {
782:
783: if ( ! empty($this->rest->ignore_limits) or !isset($this->methods[$controller_method]['limit'])) {
784:
785: return TRUE;
786: }
787:
788:
789: $limit = $this->methods[$controller_method]['limit'];
790:
791:
792: $result = $this->rest->db
793: ->where('uri', $this->uri->uri_string())
794: ->where('api_key', $this->rest->key)
795: ->get(config_item('rest_limits_table'))
796: ->row();
797:
798:
799: if ( ! $result ) {
800:
801: $this->rest->db->insert(config_item('rest_limits_table'), array(
802: 'uri' => $this->uri->uri_string(),
803: 'api_key' => isset($this->rest->key) ? $this->rest->key : '',
804: 'count' => 1,
805: 'hour_started' => time()
806: ));
807: }
808:
809:
810: else if ($result->hour_started < time() - (60 * 60)) {
811:
812: $this->rest->db
813: ->where('uri', $this->uri->uri_string())
814: ->where('api_key', isset($this->rest->key) ? $this->rest->key : '')
815: ->set('hour_started', time())
816: ->set('count', 1)
817: ->update(config_item('rest_limits_table'));
818: }
819:
820:
821: else {
822:
823: if ($result->count >= $limit) {
824: return FALSE;
825: }
826:
827: $this->rest->db
828: ->where('uri', $this->uri->uri_string())
829: ->where('api_key', $this->rest->key)
830: ->set('count', 'count + 1', FALSE)
831: ->update(config_item('rest_limits_table'));
832: }
833:
834: return TRUE;
835: }
836:
837: 838: 839: 840: 841: 842: 843: 844:
845: protected function _auth_override_check()
846: {
847:
848:
849: $this->overrides_array = $this->config->item('auth_override_class_method');
850:
851:
852: if (empty($this->overrides_array)) {
853: return FALSE;
854: }
855:
856:
857: if(!empty($this->overrides_array[$this->router->class]['*'])){
858:
859: if ($this->overrides_array[$this->router->class]['*'] == 'none')
860: {
861: return TRUE;
862: }
863:
864:
865: if ($this->overrides_array[$this->router->class]['*'] == 'basic')
866: {
867: $this->_prepare_basic_auth();
868: return TRUE;
869: }
870:
871:
872: if ($this->overrides_array[$this->router->class]['*'] == 'digest')
873: {
874: $this->_prepare_digest_auth();
875: return TRUE;
876: }
877:
878:
879: if ($this->overrides_array[$this->router->class]['*'] == 'whitelist')
880: {
881: $this->_check_whitelist_auth();
882: return TRUE;
883: }
884: }
885:
886:
887: if (empty($this->overrides_array[$this->router->class][$this->router->method])) {
888: return FALSE;
889: }
890:
891:
892: if ($this->overrides_array[$this->router->class][$this->router->method] == 'none') {
893: return TRUE;
894: }
895:
896:
897: if ($this->overrides_array[$this->router->class][$this->router->method] == 'basic') {
898: $this->_prepare_basic_auth();
899:
900: return TRUE;
901: }
902:
903:
904: if ($this->overrides_array[$this->router->class][$this->router->method] == 'digest') {
905: $this->_prepare_digest_auth();
906:
907: return TRUE;
908: }
909:
910:
911: if ($this->overrides_array[$this->router->class][$this->router->method] == 'whitelist') {
912: $this->_check_whitelist_auth();
913:
914: return TRUE;
915: }
916:
917:
918:
919: return FALSE;
920: }
921:
922: 923: 924:
925: protected function _parse_get()
926: {
927:
928: if ($this->input->is_cli_request()) {
929: $args = $_SERVER['argv'];
930: unset($args[0]);
931: $_SERVER['QUERY_STRING'] = $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'] = '/' . implode('/', $args) . '/';
932: }
933:
934:
935: parse_str(parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY), $get);
936:
937:
938: $this->_get_args = array_merge($this->_get_args, $get);
939: }
940:
941: 942: 943:
944: protected function _parse_post()
945: {
946: $this->_post_args = $_POST;
947:
948: $this->request->format and $this->request->body = file_get_contents('php://input');
949: }
950:
951: 952: 953:
954: protected function _parse_put()
955: {
956:
957: if ($this->request->format) {
958: $this->request->body = file_get_contents('php://input');
959: }
960:
961:
962: else {
963: parse_str(file_get_contents('php://input'), $this->_put_args);
964: }
965:
966: }
967:
968: 969: 970:
971: protected function _parse_head()
972: {
973:
974: parse_str(parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY), $head);
975:
976:
977: $this->_head_args = array_merge($this->_head_args, $head);
978: }
979:
980: 981: 982:
983: protected function _parse_options()
984: {
985:
986: parse_str(parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY), $options);
987:
988:
989: $this->_options_args = array_merge($this->_options_args, $options);
990: }
991:
992: 993: 994:
995: protected function _parse_patch()
996: {
997:
998: if ($this->request->format) {
999: $this->request->body = file_get_contents('php://input');
1000: }
1001:
1002:
1003: else {
1004: parse_str(file_get_contents('php://input'), $this->_patch_args);
1005: }
1006: }
1007:
1008: 1009: 1010:
1011: protected function _parse_delete()
1012: {
1013:
1014: parse_str(file_get_contents('php://input'), $this->_delete_args);
1015: }
1016:
1017:
1018:
1019: 1020: 1021: 1022: 1023: 1024: 1025:
1026: public function get($key = NULL, $xss_clean = TRUE)
1027: {
1028: if ($key === NULL) {
1029: return $this->_get_args;
1030: }
1031:
1032: return array_key_exists($key, $this->_get_args) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : FALSE;
1033: }
1034:
1035: 1036: 1037: 1038: 1039: 1040: 1041:
1042: public function options($key = NULL, $xss_clean = TRUE)
1043: {
1044: if ($key === NULL) {
1045: return $this->_options_args;
1046: }
1047:
1048: return array_key_exists($key, $this->_options_args) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : FALSE;
1049: }
1050:
1051: 1052: 1053: 1054: 1055: 1056: 1057:
1058: public function head($key = NULL, $xss_clean = TRUE)
1059: {
1060: if ($key === NULL) {
1061: return $this->head_args;
1062: }
1063:
1064: return array_key_exists($key, $this->head_args) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : FALSE;
1065: }
1066:
1067: 1068: 1069: 1070: 1071: 1072: 1073:
1074: public function post($key = NULL, $xss_clean = TRUE)
1075: {
1076: if ($key === NULL) {
1077: return $this->_post_args;
1078: }
1079:
1080: return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : FALSE;
1081: }
1082:
1083: 1084: 1085: 1086: 1087: 1088: 1089:
1090: public function put($key = NULL, $xss_clean = TRUE)
1091: {
1092: if ($key === NULL) {
1093: return $this->_put_args;
1094: }
1095:
1096: return array_key_exists($key, $this->_put_args) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : FALSE;
1097: }
1098:
1099: 1100: 1101: 1102: 1103: 1104: 1105:
1106: public function delete($key = NULL, $xss_clean = TRUE)
1107: {
1108: if ($key === NULL) {
1109: return $this->_delete_args;
1110: }
1111:
1112: return array_key_exists($key, $this->_delete_args) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : FALSE;
1113: }
1114:
1115: 1116: 1117: 1118: 1119: 1120: 1121:
1122: public function patch($key = NULL, $xss_clean = TRUE)
1123: {
1124: if ($key === NULL) {
1125: return $this->_patch_args;
1126: }
1127:
1128: return array_key_exists($key, $this->_patch_args) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : FALSE;
1129: }
1130:
1131: 1132: 1133: 1134: 1135: 1136: 1137:
1138: protected function _xss_clean($val, $process)
1139: {
1140: if (CI_VERSION < 2) {
1141: return $process ? $this->input->xss_clean($val) : $val;
1142: }
1143:
1144: return $process ? $this->security->xss_clean($val) : $val;
1145: }
1146:
1147: 1148: 1149: 1150: 1151:
1152: public function validation_errors()
1153: {
1154: $string = strip_tags($this->form_validation->error_string());
1155:
1156: return explode("\n", trim($string, "\n"));
1157: }
1158:
1159:
1160:
1161: 1162: 1163: 1164: 1165: 1166: 1167:
1168: protected function _perform_ldap_auth($username = '', $password = NULL)
1169: {
1170: if (empty($username)) {
1171: log_message('debug', 'LDAP Auth: failure, empty username');
1172:
1173: return FALSE;
1174: }
1175:
1176: log_message('debug', 'LDAP Auth: Loading Config');
1177:
1178: $this->config->load('ldap.php', TRUE);
1179:
1180: $ldap = array(
1181: 'timeout' => $this->config->item('timeout', 'ldap');
1182: 'host' => $this->config->item('server', 'ldap');
1183: 'port' => $this->config->item('port', 'ldap');
1184: 'rdn' => $this->config->item('binduser', 'ldap');
1185: 'pass' => $this->config->item('bindpw', 'ldap');
1186: 'basedn' => $this->config->item('basedn', 'ldap');
1187: );
1188:
1189: log_message('debug', 'LDAP Auth: Connect to ' . $ldaphost);
1190:
1191: $ldapconfig['authrealm'] = $this->config->item('domain', 'ldap');
1192:
1193:
1194: $ldapconn = ldap_connect($ldap['host'], $ldap['port']);
1195:
1196: if ($ldapconn) {
1197:
1198: log_message('debug', 'Setting timeout to ' . $ldap['timeout'] . ' seconds');
1199:
1200: ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldap['timeout']);
1201:
1202: log_message('debug', 'LDAP Auth: Binding to ' . $ldap['host'] . ' with dn ' . $ldap['rdn']);
1203:
1204:
1205: $ldapbind = ldap_bind($ldapconn, $ldap['rdn'], $ldap['pass']);
1206:
1207:
1208: if ($ldapbind) {
1209: log_message('debug', 'LDAP Auth: bind successful');
1210: } else {
1211: log_message('error', 'LDAP Auth: bind unsuccessful');
1212:
1213: return FALSE;
1214: }
1215:
1216: }
1217:
1218:
1219: if (($res_id = ldap_search( $ldapconn, $ldap['basedn'], "uid=$username")) == FALSE) {
1220: log_message('error', 'LDAP Auth: User ' . $username . ' not found in search');
1221:
1222: return FALSE;
1223: }
1224:
1225: if (ldap_count_entries($ldapconn, $res_id) != 1) {
1226: log_message('error', 'LDAP Auth: failure, username ' . $username . 'found more than once');
1227:
1228: return FALSE;
1229: }
1230:
1231: if (( $entry_id = ldap_first_entry($ldapconn, $res_id))== FALSE) {
1232: log_message('error', 'LDAP Auth: failure, entry of searchresult could not be fetched');
1233:
1234: return FALSE;
1235: }
1236:
1237: if (( $user_dn = ldap_get_dn($ldapconn, $entry_id)) == FALSE) {
1238: log_message('error', 'LDAP Auth: failure, user-dn could not be fetched');
1239:
1240: return FALSE;
1241: }
1242:
1243:
1244: if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) == FALSE) {
1245: log_message('error', 'LDAP Auth: failure, username/password did not match: ' . $user_dn);
1246:
1247: return FALSE;
1248: }
1249:
1250: log_message('debug', 'LDAP Auth: Success ' . $user_dn . ' authenticated successfully');
1251:
1252: $this->_user_ldap_dn = $user_dn;
1253: ldap_close($ldapconn);
1254:
1255: return TRUE;
1256: }
1257:
1258: 1259: 1260: 1261: 1262: 1263: 1264:
1265: protected function _perform_library_auth($username = '', $password = NULL)
1266: {
1267: if (empty($username)) {
1268: log_message('error', 'Library Auth: failure, empty username');
1269: return FALSE;
1270: }
1271:
1272: $auth_library_class = strtolower($this->config->item('auth_library_class'));
1273: $auth_library_function = strtolower($this->config->item('auth_library_function'));
1274:
1275: if (empty($auth_library_class)) {
1276: log_message('debug', 'Library Auth: failure, empty auth_library_class');
1277: return FALSE;
1278: }
1279:
1280: if (empty($auth_library_function)) {
1281: log_message('debug', 'Library Auth: failure, empty auth_library_function');
1282: return FALSE;
1283: }
1284:
1285: if (!is_callable(array($auth_library_class, $auth_library_function))) {
1286: $this->load->library($auth_library_class);
1287: }
1288:
1289: return $this->{$auth_library_class}->$auth_library_function($username, $password);
1290: }
1291:
1292: 1293: 1294: 1295: 1296: 1297: 1298:
1299: protected function _check_login($username = '', $password = FALSE)
1300: {
1301: if (empty($username)) {
1302: return FALSE;
1303: }
1304:
1305: $auth_source = strtolower($this->config->item('auth_source'));
1306: $rest_auth = strtolower($this->config->item('rest_auth'));
1307: $valid_logins = $this->config->item('rest_valid_logins');
1308:
1309: if (!$this->config->item('auth_source') && $rest_auth == 'digest') {
1310: return md5($username.':'.$this->config->item('rest_realm').':'.(isset($valid_logins[$username])?$valid_logins[$username]:''));
1311: }
1312:
1313: if ($password === FALSE) {
1314: return FALSE;
1315: }
1316:
1317: if ($auth_source == 'ldap') {
1318: log_message('debug', 'performing LDAP authentication for $username');
1319:
1320: return $this->_perform_ldap_auth($username, $password);
1321: }
1322:
1323: if ($auth_source == 'library') {
1324: log_message('debug', 'performing Library authentication for '.$username);
1325:
1326: return $this->_perform_library_auth($username, $password);
1327: }
1328:
1329: if (!array_key_exists($username, $valid_logins)) {
1330: return FALSE;
1331: }
1332:
1333: if ($valid_logins[$username] != $password) {
1334: return FALSE;
1335: }
1336:
1337: return TRUE;
1338: }
1339:
1340: 1341: 1342:
1343: protected function _check_php_session()
1344: {
1345: $key = $this->config->item('auth_source');
1346: if (!$this->session->userdata($key)) {
1347: $this->response(array('status' => FALSE, 'error' => 'Not Authorized'), 401);
1348: }
1349: }
1350:
1351: 1352: 1353:
1354: protected function _prepare_basic_auth()
1355: {
1356:
1357: if (config_item('rest_ip_whitelist_enabled')) {
1358: $this->_check_whitelist_auth();
1359: }
1360:
1361: $username = NULL;
1362: $password = NULL;
1363:
1364:
1365: if ($this->input->server('PHP_AUTH_USER')) {
1366: $username = $this->input->server('PHP_AUTH_USER');
1367: $password = $this->input->server('PHP_AUTH_PW');
1368: }
1369:
1370:
1371: elseif ($this->input->server('HTTP_AUTHENTICATION')) {
1372: if (strpos(strtolower($this->input->server('HTTP_AUTHENTICATION')), 'basic') === 0) {
1373: list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6)));
1374: }
1375: }
1376:
1377: if ( ! $this->_check_login($username, $password)) {
1378: $this->_force_login();
1379: }
1380: }
1381:
1382: 1383: 1384:
1385: protected function _prepare_digest_auth()
1386: {
1387:
1388: if (config_item('rest_ip_whitelist_enabled')) {
1389: $this->_check_whitelist_auth();
1390: }
1391:
1392: $uniqid = uniqid("");
1393:
1394:
1395: if ($this->input->server('PHP_AUTH_DIGEST')) {
1396: $digest_string = $this->input->server('PHP_AUTH_DIGEST');
1397: } elseif ($this->input->server('HTTP_AUTHORIZATION')) {
1398: $digest_string = $this->input->server('HTTP_AUTHORIZATION');
1399: } else {
1400: $digest_string = "";
1401: }
1402:
1403:
1404:
1405: if (empty($digest_string)) {
1406: $this->_force_login($uniqid);
1407: }
1408:
1409:
1410: $matches = array();
1411: preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches);
1412: $digest = (empty($matches[1]) || empty($matches[2])) ? array() : array_combine($matches[1], $matches[2]);
1413:
1414:
1415: $A1 = $this->_check_login($digest['username'], TRUE);
1416: if ( ! array_key_exists('username', $digest) or ! $A1 ) {
1417: $this->_force_login($uniqid);
1418: }
1419:
1420: $A2 = md5(strtoupper($this->request->method).':'.$digest['uri']);
1421: $valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2);
1422:
1423: if ($digest['response'] != $valid_response) {
1424: $this->response(array(config_item('rest_status_field_name') => 0, config_item('rest_message_field_name') => 'Invalid credentials'), 401);
1425: exit;
1426: }
1427: }
1428:
1429: 1430: 1431:
1432: protected function _check_blacklist_auth()
1433: {
1434: $blacklist = explode(',', config_item('rest_ip_blacklist'));
1435:
1436: foreach ($blacklist AS &$ip) {
1437: $ip = trim($ip);
1438: }
1439:
1440: if (in_array($this->input->ip_address(), $blacklist)) {
1441: $this->response(array('status' => FALSE, 'error' => 'IP Denied'), 401);
1442: }
1443: }
1444:
1445: 1446: 1447:
1448: protected function _check_whitelist_auth()
1449: {
1450: $whitelist = explode(',', config_item('rest_ip_whitelist'));
1451:
1452: array_push($whitelist, '127.0.0.1', '0.0.0.0');
1453:
1454: foreach ($whitelist AS &$ip) {
1455: $ip = trim($ip);
1456: }
1457:
1458: if ( ! in_array($this->input->ip_address(), $whitelist)) {
1459: $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'IP not authorized'), 401);
1460: }
1461: }
1462:
1463: 1464: 1465: 1466: 1467:
1468: protected function _force_login($nonce = '')
1469: {
1470: if (strtolower( $this->config->item('rest_auth') ) == 'basic') {
1471: header('WWW-Authenticate: Basic realm="'.$this->config->item('rest_realm').'"');
1472: } elseif (strtolower( $this->config->item('rest_auth') ) == 'digest') {
1473: header('WWW-Authenticate: Digest realm="'.$this->config->item('rest_realm').'", qop="auth", nonce="'.$nonce.'", opaque="'.md5($this->config->item('rest_realm')).'"');
1474: }
1475:
1476: $this->response(array(config_item('rest_status_field_name') => FALSE, config_item('rest_message_field_name') => 'Not authorized'), 401);
1477: }
1478:
1479: 1480: 1481: 1482: 1483: 1484:
1485: protected function _force_loopable($data)
1486: {
1487:
1488: if ( ! is_array($data) and !is_object($data)) {
1489: $data = (array) $data;
1490: }
1491:
1492: return $data;
1493: }
1494:
1495: 1496: 1497: 1498: 1499: 1500:
1501:
1502: protected function _log_access_time()
1503: {
1504: $payload['rtime'] = $this->_end_rtime - $this->_start_rtime;
1505:
1506: return $this->rest->db->update(config_item('rest_logs_table'), $payload, array('id' => $this->_insert_id));
1507: }
1508:
1509: 1510: 1511: 1512: 1513:
1514: protected function _check_access()
1515: {
1516:
1517: if (config_item('rest_enable_access') === FALSE) {
1518: return TRUE;
1519: }
1520:
1521:
1522: $controller = implode( '/', array($this->router->fetch_directory(), $this->router->fetch_class()) );
1523:
1524:
1525: $controller = str_replace('//', '/', $controller);
1526:
1527:
1528: $this->rest->db->select();
1529: $this->rest->db->where('key', $this->rest->key);
1530: $this->rest->db->where('controller', $controller);
1531:
1532: $query = $this->rest->db->get(config_item('rest_access_table'));
1533:
1534: if ($query->num_rows > 0) {
1535: return TRUE;
1536: }
1537:
1538: return FALSE;
1539: }
1540:
1541: }
1542: