Guest User

Untitled

a guest
Aug 24th, 2019
360
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 79.81 KB | None | 0 0
  1. <?php
  2.  
  3. defined('BASEPATH') OR exit('No direct script access allowed');
  4.  
  5. /**
  6. * CodeIgniter Rest Controller
  7. * A fully RESTful server implementation for CodeIgniter using one library, one config file and one controller.
  8. *
  9. * @package CodeIgniter
  10. * @subpackage Libraries
  11. * @category Libraries
  12. * @author Phil Sturgeon, Chris Kacerguis
  13. * @license MIT
  14. * @link https://github.com/chriskacerguis/codeigniter-restserver
  15. * @version 3.0.0
  16. */
  17. abstract class REST_Controller extends CI_Controller {
  18.  
  19. // Note: Only the widely used HTTP status codes are documented
  20.  
  21. // Informational
  22.  
  23. const HTTP_CONTINUE = 100;
  24. const HTTP_SWITCHING_PROTOCOLS = 101;
  25. const HTTP_PROCESSING = 102; // RFC2518
  26.  
  27. // Success
  28.  
  29. /**
  30. * The request has succeeded
  31. */
  32. const HTTP_OK = 200;
  33.  
  34. /**
  35. * The server successfully created a new resource
  36. */
  37. const HTTP_CREATED = 201;
  38. const HTTP_ACCEPTED = 202;
  39. const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
  40.  
  41. /**
  42. * The server successfully processed the request, though no content is returned
  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; // RFC4918
  48. const HTTP_ALREADY_REPORTED = 208; // RFC5842
  49. const HTTP_IM_USED = 226; // RFC3229
  50.  
  51. // Redirection
  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. * The resource has not been modified since the last request
  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; // RFC7238
  66.  
  67. // Client Error
  68.  
  69. /**
  70. * The request cannot be fulfilled due to multiple errors
  71. */
  72. const HTTP_BAD_REQUEST = 400;
  73.  
  74. /**
  75. * The user is unauthorized to access the requested resource
  76. */
  77. const HTTP_UNAUTHORIZED = 401;
  78. const HTTP_PAYMENT_REQUIRED = 402;
  79.  
  80. /**
  81. * The requested resource is unavailable at this present time
  82. */
  83. const HTTP_FORBIDDEN = 403;
  84.  
  85. /**
  86. * The requested resource could not be found
  87. *
  88. * Note: This is sometimes used to mask if there was an UNAUTHORIZED (401) or
  89. * FORBIDDEN (403) error, for security reasons
  90. */
  91. const HTTP_NOT_FOUND = 404;
  92.  
  93. /**
  94. * The request method is not supported by the following resource
  95. */
  96. const HTTP_METHOD_NOT_ALLOWED = 405;
  97.  
  98. /**
  99. * The request was not acceptable
  100. */
  101. const HTTP_NOT_ACCEPTABLE = 406;
  102. const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
  103. const HTTP_REQUEST_TIMEOUT = 408;
  104.  
  105. /**
  106. * The request could not be completed due to a conflict with the current state
  107. * of the resource
  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; // RFC2324
  119. const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
  120. const HTTP_LOCKED = 423; // RFC4918
  121. const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
  122. const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817
  123. const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
  124. const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
  125. const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
  126. const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
  127.  
  128. // Server Error
  129.  
  130. /**
  131. * The server encountered an unexpected error
  132. *
  133. * Note: This is a generic error message when no specific message
  134. * is suitable
  135. */
  136. const HTTP_INTERNAL_SERVER_ERROR = 500;
  137.  
  138. /**
  139. * The server does not recognise the request method
  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; // RFC2295
  147. const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
  148. const HTTP_LOOP_DETECTED = 508; // RFC5842
  149. const HTTP_NOT_EXTENDED = 510; // RFC2774
  150. const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;
  151.  
  152. /**
  153. * This defines the rest format
  154. * Must be overridden it in a controller so that it is set
  155. *
  156. * @var string|NULL
  157. */
  158. protected $rest_format = NULL;
  159.  
  160. /**
  161. * Defines the list of method properties such as limit, log and level
  162. *
  163. * @var array
  164. */
  165. protected $methods = [];
  166.  
  167. /**
  168. * List of allowed HTTP methods
  169. *
  170. * @var array
  171. */
  172. protected $allowed_http_methods = ['get', 'delete', 'post', 'put', 'options', 'patch', 'head'];
  173.  
  174. /**
  175. * Contains details about the request
  176. * Fields: body, format, method, ssl
  177. * Note: This is a dynamic object (stdClass)
  178. *
  179. * @var object
  180. */
  181. protected $request = NULL;
  182.  
  183. /**
  184. * Contains details about the response
  185. * Fields: format, lang
  186. * Note: This is a dynamic object (stdClass)
  187. *
  188. * @var object
  189. */
  190. protected $response = NULL;
  191.  
  192. /**
  193. * Contains details about the REST API
  194. * Fields: db, ignore_limits, key, level, user_id
  195. * Note: This is a dynamic object (stdClass)
  196. *
  197. * @var object
  198. */
  199. protected $rest = NULL;
  200.  
  201. /**
  202. * The arguments for the GET request method
  203. *
  204. * @var array
  205. */
  206. protected $_get_args = [];
  207.  
  208. /**
  209. * The arguments for the POST request method
  210. *
  211. * @var array
  212. */
  213. protected $_post_args = [];
  214.  
  215. /**
  216. * The arguments for the PUT request method
  217. *
  218. * @var array
  219. */
  220. protected $_put_args = [];
  221.  
  222. /**
  223. * The arguments for the DELETE request method
  224. *
  225. * @var array
  226. */
  227. protected $_delete_args = [];
  228.  
  229. /**
  230. * The arguments for the PATCH request method
  231. *
  232. * @var array
  233. */
  234. protected $_patch_args = [];
  235.  
  236. /**
  237. * The arguments for the HEAD request method
  238. *
  239. * @var array
  240. */
  241. protected $_head_args = [];
  242.  
  243. /**
  244. * The arguments for the OPTIONS request method
  245. *
  246. * @var array
  247. */
  248. protected $_options_args = [];
  249.  
  250. /**
  251. * The arguments for the query parameters
  252. *
  253. * @var array
  254. */
  255. protected $_query_args = [];
  256.  
  257. /**
  258. * The arguments from GET, POST, PUT, DELETE, PATCH, HEAD and OPTIONS request methods combined
  259. *
  260. * @var array
  261. */
  262. protected $_args = [];
  263.  
  264. /**
  265. * The insert_id of the log entry (if we have one)
  266. *
  267. * @var string
  268. */
  269. protected $_insert_id = '';
  270.  
  271. /**
  272. * If the request is allowed based on the API key provided
  273. *
  274. * @var bool
  275. */
  276. protected $_allow = TRUE;
  277.  
  278. /**
  279. * The LDAP Distinguished Name of the User post authentication
  280. *
  281. * @var string
  282. */
  283. protected $_user_ldap_dn = '';
  284.  
  285. /**
  286. * The start of the response time from the server
  287. *
  288. * @var number
  289. */
  290. protected $_start_rtime;
  291.  
  292. /**
  293. * The end of the response time from the server
  294. *
  295. * @var number
  296. */
  297. protected $_end_rtime;
  298.  
  299. /**
  300. * List all supported methods, the first will be the default format
  301. *
  302. * @var array
  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. * Information about the current API user
  317. *
  318. * @var object
  319. */
  320. protected $_apiuser;
  321.  
  322. /**
  323. * Whether or not to perform a CORS check and apply CORS headers to the request
  324. *
  325. * @var bool
  326. */
  327. protected $check_cors = NULL;
  328.  
  329. /**
  330. * Enable XSS flag
  331. * Determines whether the XSS filter is always active when
  332. * GET, OPTIONS, HEAD, POST, PUT, DELETE and PATCH data is encountered
  333. * Set automatically based on config setting
  334. *
  335. * @var bool
  336. */
  337. protected $_enable_xss = FALSE;
  338.  
  339. private $is_valid_request = TRUE;
  340.  
  341. /**
  342. * HTTP status codes and their respective description
  343. * Note: Only the widely used HTTP status codes are used
  344. *
  345. * @var array
  346. * @link http://www.restapitutorial.com/httpstatuscodes.html
  347. */
  348. protected $http_status_codes = [
  349. self::HTTP_OK => 'OK',
  350. self::HTTP_CREATED => 'CREATED',
  351. self::HTTP_NO_CONTENT => 'NO CONTENT',
  352. self::HTTP_NOT_MODIFIED => 'NOT MODIFIED',
  353. self::HTTP_BAD_REQUEST => 'BAD REQUEST',
  354. self::HTTP_UNAUTHORIZED => 'UNAUTHORIZED',
  355. self::HTTP_FORBIDDEN => 'FORBIDDEN',
  356. self::HTTP_NOT_FOUND => 'NOT FOUND',
  357. self::HTTP_METHOD_NOT_ALLOWED => 'METHOD NOT ALLOWED',
  358. self::HTTP_NOT_ACCEPTABLE => 'NOT ACCEPTABLE',
  359. self::HTTP_CONFLICT => 'CONFLICT',
  360. self::HTTP_INTERNAL_SERVER_ERROR => 'INTERNAL SERVER ERROR',
  361. self::HTTP_NOT_IMPLEMENTED => 'NOT IMPLEMENTED'
  362. ];
  363.  
  364. /**
  365. * @var Format
  366. */
  367. private $format;
  368. /**
  369. * @var bool
  370. */
  371. private $auth_override;
  372.  
  373. /**
  374. * Extend this function to apply additional checking early on in the process
  375. *
  376. * @access protected
  377. * @return void
  378. */
  379. protected function early_checks()
  380. {
  381. }
  382.  
  383. /**
  384. * Constructor for the REST API
  385. *
  386. * @access public
  387. * @param string $config Configuration filename minus the file extension
  388. * e.g: my_rest.php is passed as 'my_rest'
  389. */
  390. public function __construct($config = 'rest')
  391. {
  392. parent::__construct();
  393.  
  394. $this->lang_check();
  395. $this->preflight_checks();
  396.  
  397. // Set the default value of global xss filtering. Same approach as CodeIgniter 3
  398. $this->_enable_xss = ($this->config->item('global_xss_filtering') === TRUE);
  399.  
  400. // Don't try to parse template variables like {elapsed_time} and {memory_usage}
  401. // when output is displayed for not damaging data accidentally
  402. $this->output->parse_exec_vars = FALSE;
  403.  
  404. // Start the timer for how long the request takes
  405. $this->_start_rtime = microtime(TRUE);
  406.  
  407. // Load the rest.php configuration file
  408. $this->get_local_config($config);
  409.  
  410. // At present the library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter (no citation)
  411. if(class_exists('Format'))
  412. {
  413. $this->format = new Format();
  414. }
  415. else
  416. {
  417. $this->load->library('Format', NULL, 'libraryFormat');
  418. $this->format = $this->libraryFormat;
  419. }
  420.  
  421.  
  422. // Determine supported output formats from configuration
  423. $supported_formats = $this->config->item('rest_supported_formats');
  424.  
  425. // Validate the configuration setting output formats
  426. if (empty($supported_formats))
  427. {
  428. $supported_formats = [];
  429. }
  430.  
  431. if ( ! is_array($supported_formats))
  432. {
  433. $supported_formats = [$supported_formats];
  434. }
  435.  
  436. // Add silently the default output format if it is missing
  437. $default_format = $this->_get_default_output_format();
  438. if (!in_array($default_format, $supported_formats))
  439. {
  440. $supported_formats[] = $default_format;
  441. }
  442.  
  443. // Now update $this->_supported_formats
  444. $this->_supported_formats = array_intersect_key($this->_supported_formats, array_flip($supported_formats));
  445.  
  446. // Get the language
  447. $language = $this->config->item('rest_language');
  448. if ($language === NULL)
  449. {
  450. $language = 'english';
  451. }
  452.  
  453. // Load the language file
  454. $this->lang->load('rest_controller', $language, FALSE, TRUE, __DIR__.'/../');
  455.  
  456. // Initialise the response, request and rest objects
  457. $this->request = new stdClass();
  458. $this->response = new stdClass();
  459. $this->rest = new stdClass();
  460.  
  461. // Check to see if the current IP address is blacklisted
  462. if ($this->config->item('rest_ip_blacklist_enabled') === TRUE)
  463. {
  464. $this->_check_blacklist_auth();
  465. }
  466.  
  467. // Determine whether the connection is HTTPS
  468. $this->request->ssl = is_https();
  469.  
  470. // How is this request being made? GET, POST, PATCH, DELETE, INSERT, PUT, HEAD or OPTIONS
  471. $this->request->method = $this->_detect_method();
  472.  
  473. // Check for CORS access request
  474. $check_cors = $this->config->item('check_cors');
  475. if ($check_cors === TRUE)
  476. {
  477. $this->_check_cors();
  478. }
  479.  
  480. // Create an argument container if it doesn't exist e.g. _get_args
  481. if (isset($this->{'_'.$this->request->method.'_args'}) === FALSE)
  482. {
  483. $this->{'_'.$this->request->method.'_args'} = [];
  484. }
  485.  
  486. // Set up the query parameters
  487. $this->_parse_query();
  488.  
  489. // Set up the GET variables
  490. $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc());
  491.  
  492. // Try to find a format for the request (means we have a request body)
  493. $this->request->format = $this->_detect_input_format();
  494.  
  495. // Not all methods have a body attached with them
  496. $this->request->body = NULL;
  497.  
  498. $this->{'_parse_' . $this->request->method}();
  499.  
  500. // Fix parse method return arguments null
  501. if($this->{'_'.$this->request->method.'_args'} === null)
  502. {
  503. $this->{'_'.$this->request->method.'_args'} = [];
  504. }
  505.  
  506. // Now we know all about our request, let's try and parse the body if it exists
  507. if ($this->request->format && $this->request->body)
  508. {
  509. $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array();
  510. // Assign payload arguments to proper method container
  511. $this->{'_'.$this->request->method.'_args'} = $this->request->body;
  512. }
  513.  
  514. //get header vars
  515. $this->_head_args = $this->input->request_headers();
  516.  
  517. // Merge both for one mega-args variable
  518. $this->_args = array_merge(
  519. $this->_get_args,
  520. $this->_options_args,
  521. $this->_patch_args,
  522. $this->_head_args,
  523. $this->_put_args,
  524. $this->_post_args,
  525. $this->_delete_args,
  526. $this->{'_'.$this->request->method.'_args'}
  527. );
  528.  
  529. // Which format should the data be returned in?
  530. $this->response->format = $this->_detect_output_format();
  531.  
  532. // Which language should the data be returned in?
  533. $this->response->lang = $this->_detect_lang();
  534.  
  535. // Extend this function to apply additional checking early on in the process
  536. $this->early_checks();
  537.  
  538. // Load DB if its enabled
  539. if ($this->config->item('rest_database_group') && ($this->config->item('rest_enable_keys') || $this->config->item('rest_enable_logging')))
  540. {
  541. $this->rest->db = $this->load->database($this->config->item('rest_database_group'), TRUE);
  542. }
  543.  
  544. // Use whatever database is in use (isset returns FALSE)
  545. elseif (property_exists($this, 'db'))
  546. {
  547. $this->rest->db = $this->db;
  548. }
  549.  
  550. // Check if there is a specific auth type for the current class/method
  551. // _auth_override_check could exit so we need $this->rest->db initialized before
  552. $this->auth_override = $this->_auth_override_check();
  553.  
  554. // Checking for keys? GET TO WorK!
  555. // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none'
  556. if ($this->config->item('rest_enable_keys') && $this->auth_override !== TRUE)
  557. {
  558. $this->_allow = $this->_detect_api_key();
  559. }
  560.  
  561. // Only allow ajax requests
  562. if ($this->input->is_ajax_request() === FALSE && $this->config->item('rest_ajax_only'))
  563. {
  564. // Display an error response
  565. $this->response([
  566. $this->config->item('rest_status_field_name') => FALSE,
  567. $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ajax_only')
  568. ], self::HTTP_NOT_ACCEPTABLE);
  569. }
  570.  
  571. // When there is no specific override for the current class/method, use the default auth value set in the config
  572. if ($this->auth_override === FALSE &&
  573. (! ($this->config->item('rest_enable_keys') && $this->_allow === TRUE) ||
  574. ($this->config->item('allow_auth_and_keys') === TRUE && $this->_allow === TRUE)))
  575. {
  576. $rest_auth = strtolower($this->config->item('rest_auth'));
  577. switch ($rest_auth)
  578. {
  579. case 'basic':
  580. $this->_prepare_basic_auth();
  581. break;
  582. case 'digest':
  583. $this->_prepare_digest_auth();
  584. break;
  585. case 'session':
  586. $this->_check_php_session();
  587. break;
  588. }
  589. if ($this->config->item('rest_ip_whitelist_enabled') === TRUE)
  590. {
  591. $this->_check_whitelist_auth();
  592. }
  593. }
  594. }
  595.  
  596. /**
  597. * @param $config_file
  598. */
  599. private function lang_check()
  600. {
  601. if(!isset($_SESSION['lang'])){
  602. $_SESSION['lang']="ID";
  603. }
  604. }
  605. /**
  606. * @param $config_file
  607. */
  608. private function get_local_config($config_file)
  609. {
  610. if(file_exists(__DIR__."/../config/".$config_file.".php"))
  611. {
  612. $config = array();
  613. include(__DIR__ . "/../config/" . $config_file . ".php");
  614.  
  615. foreach($config AS $key => $value)
  616. {
  617. $this->config->set_item($key, $value);
  618. }
  619. }
  620.  
  621. $this->load->config($config_file, FALSE, TRUE);
  622. }
  623.  
  624. /**
  625. * De-constructor
  626. *
  627. * @author Chris Kacerguis
  628. * @access public
  629. * @return void
  630. */
  631. public function __destruct()
  632. {
  633. // Get the current timestamp
  634. $this->_end_rtime = microtime(TRUE);
  635.  
  636. // Log the loading time to the log table
  637. if ($this->config->item('rest_enable_logging') === TRUE)
  638. {
  639. $this->_log_access_time();
  640. }
  641. }
  642.  
  643. /**
  644. * Checks to see if we have everything we need to run this library.
  645. *
  646. * @access protected
  647. * @throws Exception
  648. */
  649. protected function preflight_checks()
  650. {
  651. // Check to see if PHP is equal to or greater than 5.4.x
  652. if (is_php('5.4') === FALSE)
  653. {
  654. // CodeIgniter 3 is recommended for v5.4 or above
  655. throw new Exception('Using PHP v'.PHP_VERSION.', though PHP v5.4 or greater is required');
  656. }
  657.  
  658. // Check to see if this is CI 3.x
  659. if (explode('.', CI_VERSION, 2)[0] < 3)
  660. {
  661. throw new Exception('REST Server requires CodeIgniter 3.x');
  662. }
  663. }
  664.  
  665. /**
  666. * Requests are not made to methods directly, the request will be for
  667. * an "object". This simply maps the object and method to the correct
  668. * Controller method
  669. *
  670. * @access public
  671. * @param string $object_called
  672. * @param array $arguments The arguments passed to the controller method
  673. * @throws Exception
  674. */
  675. public function _remap($object_called, $arguments = [])
  676. {
  677. // Should we answer if not over SSL?
  678. if ($this->config->item('force_https') && $this->request->ssl === FALSE)
  679. {
  680. $this->response([
  681. $this->config->item('rest_status_field_name') => FALSE,
  682. $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unsupported')
  683. ], self::HTTP_FORBIDDEN);
  684.  
  685. $this->is_valid_request = false;
  686. }
  687.  
  688. // Remove the supported format from the function name e.g. index.json => index
  689. $object_called = preg_replace('/^(.*)\.(?:'.implode('|', array_keys($this->_supported_formats)).')$/', '$1', $object_called);
  690.  
  691. $controller_method = $object_called.'_'.$this->request->method;
  692. // Does this method exist? If not, try executing an index method
  693. if (!method_exists($this, $controller_method)) {
  694. $controller_method = "index_" . $this->request->method;
  695. array_unshift($arguments, $object_called);
  696. }
  697.  
  698. // Do we want to log this method (if allowed by config)?
  699. $log_method = ! (isset($this->methods[$controller_method]['log']) && $this->methods[$controller_method]['log'] === FALSE);
  700.  
  701. // Use keys for this method?
  702. $use_key = ! (isset($this->methods[$controller_method]['key']) && $this->methods[$controller_method]['key'] === FALSE);
  703.  
  704. // They provided a key, but it wasn't valid, so get them out of here
  705. if ($this->config->item('rest_enable_keys') && $use_key && $this->_allow === FALSE)
  706. {
  707. if ($this->config->item('rest_enable_logging') && $log_method)
  708. {
  709. $this->_log_request();
  710. }
  711.  
  712. // fix cross site to option request error
  713. if($this->request->method == 'options') {
  714. exit;
  715. }
  716.  
  717. $this->response([
  718. $this->config->item('rest_status_field_name') => FALSE,
  719. $this->config->item('rest_message_field_name') => sprintf($this->lang->line('text_rest_invalid_api_key'), $this->rest->key)
  720. ], self::HTTP_FORBIDDEN);
  721.  
  722. $this->is_valid_request = false;
  723. }
  724.  
  725. // Check to see if this key has access to the requested controller
  726. if ($this->config->item('rest_enable_keys') && $use_key && empty($this->rest->key) === FALSE && $this->_check_access() === FALSE)
  727. {
  728. if ($this->config->item('rest_enable_logging') && $log_method)
  729. {
  730. $this->_log_request();
  731. }
  732.  
  733. $this->response([
  734. $this->config->item('rest_status_field_name') => FALSE,
  735. $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_unauthorized')
  736. ], self::HTTP_UNAUTHORIZED);
  737.  
  738. $this->is_valid_request = false;
  739. }
  740.  
  741. // Sure it exists, but can they do anything with it?
  742. if (! method_exists($this, $controller_method))
  743. {
  744. $this->response([
  745. $this->config->item('rest_status_field_name') => FALSE,
  746. $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unknown_method')
  747. ], self::HTTP_METHOD_NOT_ALLOWED);
  748.  
  749. $this->is_valid_request = false;
  750. }
  751.  
  752. // Doing key related stuff? Can only do it if they have a key right?
  753. if ($this->config->item('rest_enable_keys') && empty($this->rest->key) === FALSE)
  754. {
  755. // Check the limit
  756. if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE)
  757. {
  758. $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')];
  759. $this->response($response, self::HTTP_UNAUTHORIZED);
  760.  
  761. $this->is_valid_request = false;
  762. }
  763.  
  764. // If no level is set use 0, they probably aren't using permissions
  765. $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0;
  766.  
  767. // If no level is set, or it is lower than/equal to the key's level
  768. $authorized = $level <= $this->rest->level;
  769. // IM TELLIN!
  770. if ($this->config->item('rest_enable_logging') && $log_method)
  771. {
  772. $this->_log_request($authorized);
  773. }
  774. if($authorized === FALSE)
  775. {
  776. // They don't have good enough perms
  777. $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_permissions')];
  778. $this->response($response, self::HTTP_UNAUTHORIZED);
  779.  
  780. $this->is_valid_request = false;
  781. }
  782. }
  783.  
  784. //check request limit by ip without login
  785. elseif ($this->config->item('rest_limits_method') == "IP_ADDRESS" && $this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE)
  786. {
  787. $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_address_time_limit')];
  788. $this->response($response, self::HTTP_UNAUTHORIZED);
  789.  
  790. $this->is_valid_request = false;
  791. }
  792.  
  793. // No key stuff, but record that stuff is happening
  794. elseif ($this->config->item('rest_enable_logging') && $log_method)
  795. {
  796. $this->_log_request($authorized = TRUE);
  797. }
  798.  
  799. // Call the controller method and passed arguments
  800. try
  801. {
  802. if ($this->is_valid_request) {
  803. call_user_func_array([$this, $controller_method], $arguments);
  804. }
  805. }
  806. catch (Exception $ex)
  807. {
  808. if ($this->config->item('rest_handle_exceptions') === FALSE) {
  809. throw $ex;
  810. }
  811.  
  812. // If the method doesn't exist, then the error will be caught and an error response shown
  813. $_error = &load_class('Exceptions', 'core');
  814. $_error->show_exception($ex);
  815. }
  816. }
  817.  
  818. /**
  819. * Takes mixed data and optionally a status code, then creates the response
  820. *
  821. * @access public
  822. * @param array|NULL $data Data to output to the user
  823. * @param int|NULL $http_code HTTP status code
  824. * @param bool $continue TRUE to flush the response to the client and continue
  825. * running the script; otherwise, exit
  826. */
  827. public function response($data = NULL, $http_code = NULL, $continue = FALSE)
  828. {
  829. ob_start();
  830. // If the HTTP status is not NULL, then cast as an integer
  831. if ($http_code !== NULL)
  832. {
  833. // So as to be safe later on in the process
  834. $http_code = (int) $http_code;
  835. }
  836.  
  837. // Set the output as NULL by default
  838. $output = NULL;
  839.  
  840. // If data is NULL and no HTTP status code provided, then display, error and exit
  841. if ($data === NULL && $http_code === NULL)
  842. {
  843. $http_code = self::HTTP_NOT_FOUND;
  844. }
  845.  
  846. // If data is not NULL and a HTTP status code provided, then continue
  847. elseif ($data !== NULL)
  848. {
  849. // If the format method exists, call and return the output in that format
  850. if (method_exists($this->format, 'to_' . $this->response->format))
  851. {
  852. // Set the format header
  853. $this->output->set_content_type($this->_supported_formats[$this->response->format], strtolower($this->config->item('charset')));
  854. $output = $this->format->factory($data)->{'to_' . $this->response->format}();
  855.  
  856. // An array must be parsed as a string, so as not to cause an array to string error
  857. // Json is the most appropriate form for such a data type
  858. if ($this->response->format === 'array')
  859. {
  860. $output = $this->format->factory($output)->{'to_json'}();
  861. }
  862. }
  863. else
  864. {
  865. // If an array or object, then parse as a json, so as to be a 'string'
  866. if (is_array($data) || is_object($data))
  867. {
  868. $data = $this->format->factory($data)->{'to_json'}();
  869. }
  870.  
  871. // Format is not supported, so output the raw data as a string
  872. $output = $data;
  873. }
  874. }
  875.  
  876. // If not greater than zero, then set the HTTP status code as 200 by default
  877. // Though perhaps 500 should be set instead, for the developer not passing a
  878. // correct HTTP status code
  879. $http_code > 0 || $http_code = self::HTTP_OK;
  880.  
  881. $this->output->set_status_header($http_code);
  882.  
  883. // JC: Log response code only if rest logging enabled
  884. if ($this->config->item('rest_enable_logging') === TRUE)
  885. {
  886. $this->_log_response_code($http_code);
  887. }
  888.  
  889. // Output the data
  890. $this->output->set_output($output);
  891.  
  892. if ($continue === FALSE)
  893. {
  894. // Display the data and exit execution
  895. $this->output->_display();
  896. exit;
  897. }
  898. else
  899. {
  900. ob_end_flush();
  901. }
  902.  
  903. // Otherwise dump the output automatically
  904. }
  905.  
  906. /**
  907. * Takes mixed data and optionally a status code, then creates the response
  908. * within the buffers of the Output class. The response is sent to the client
  909. * lately by the framework, after the current controller's method termination.
  910. * All the hooks after the controller's method termination are executable
  911. *
  912. * @access public
  913. * @param array|NULL $data Data to output to the user
  914. * @param int|NULL $http_code HTTP status code
  915. */
  916. public function set_response($data = NULL, $http_code = NULL)
  917. {
  918. $this->response($data, $http_code, TRUE);
  919. }
  920.  
  921. /**
  922. * Get the input format e.g. json or xml
  923. *
  924. * @access protected
  925. * @return string|NULL Supported input format; otherwise, NULL
  926. */
  927. protected function _detect_input_format()
  928. {
  929. // Get the CONTENT-TYPE value from the SERVER variable
  930. $content_type = $this->input->server('CONTENT_TYPE');
  931.  
  932. if (empty($content_type) === FALSE)
  933. {
  934. // If a semi-colon exists in the string, then explode by ; and get the value of where
  935. // the current array pointer resides. This will generally be the first element of the array
  936. $content_type = (strpos($content_type, ';') !== FALSE ? current(explode(';', $content_type)) : $content_type);
  937.  
  938. // Check all formats against the CONTENT-TYPE header
  939. foreach ($this->_supported_formats as $type => $mime)
  940. {
  941. // $type = format e.g. csv
  942. // $mime = mime type e.g. application/csv
  943.  
  944. // If both the mime types match, then return the format
  945. if ($content_type === $mime)
  946. {
  947. return $type;
  948. }
  949. }
  950. }
  951.  
  952. return NULL;
  953. }
  954.  
  955. /**
  956. * Gets the default format from the configuration. Fallbacks to 'json'
  957. * if the corresponding configuration option $config['rest_default_format']
  958. * is missing or is empty
  959. *
  960. * @access protected
  961. * @return string The default supported input format
  962. */
  963. protected function _get_default_output_format()
  964. {
  965. $default_format = (string) $this->config->item('rest_default_format');
  966. return $default_format === '' ? 'json' : $default_format;
  967. }
  968.  
  969. /**
  970. * Detect which format should be used to output the data
  971. *
  972. * @access protected
  973. * @return mixed|NULL|string Output format
  974. */
  975. protected function _detect_output_format()
  976. {
  977. // Concatenate formats to a regex pattern e.g. \.(csv|json|xml)
  978. $pattern = '/\.('.implode('|', array_keys($this->_supported_formats)).')($|\/)/';
  979. $matches = [];
  980.  
  981. // Check if a file extension is used e.g. http://example.com/api/index.json?param1=param2
  982. if (preg_match($pattern, $this->uri->uri_string(), $matches))
  983. {
  984. return $matches[1];
  985. }
  986.  
  987. // Get the format parameter named as 'format'
  988. if (isset($this->_get_args['format']))
  989. {
  990. $format = strtolower($this->_get_args['format']);
  991.  
  992. if (isset($this->_supported_formats[$format]) === TRUE)
  993. {
  994. return $format;
  995. }
  996. }
  997.  
  998. // Get the HTTP_ACCEPT server variable
  999. $http_accept = $this->input->server('HTTP_ACCEPT');
  1000.  
  1001. // Otherwise, check the HTTP_ACCEPT server variable
  1002. if ($this->config->item('rest_ignore_http_accept') === FALSE && $http_accept !== NULL)
  1003. {
  1004. // Check all formats against the HTTP_ACCEPT header
  1005. foreach (array_keys($this->_supported_formats) as $format)
  1006. {
  1007. // Has this format been requested?
  1008. if (strpos($http_accept, $format) !== FALSE)
  1009. {
  1010. if ($format !== 'html' && $format !== 'xml')
  1011. {
  1012. // If not HTML or XML assume it's correct
  1013. return $format;
  1014. }
  1015. elseif ($format === 'html' && strpos($http_accept, 'xml') === FALSE)
  1016. {
  1017. // HTML or XML have shown up as a match
  1018. // If it is truly HTML, it wont want any XML
  1019. return $format;
  1020. }
  1021. else if ($format === 'xml' && strpos($http_accept, 'html') === FALSE)
  1022. {
  1023. // If it is truly XML, it wont want any HTML
  1024. return $format;
  1025. }
  1026. }
  1027. }
  1028. }
  1029.  
  1030. // Check if the controller has a default format
  1031. if (empty($this->rest_format) === FALSE)
  1032. {
  1033. return $this->rest_format;
  1034. }
  1035.  
  1036. // Obtain the default format from the configuration
  1037. return $this->_get_default_output_format();
  1038. }
  1039.  
  1040. /**
  1041. * Get the HTTP request string e.g. get or post
  1042. *
  1043. * @access protected
  1044. * @return string|NULL Supported request method as a lowercase string; otherwise, NULL if not supported
  1045. */
  1046. protected function _detect_method()
  1047. {
  1048. // Declare a variable to store the method
  1049. $method = NULL;
  1050.  
  1051. // Determine whether the 'enable_emulate_request' setting is enabled
  1052. if ($this->config->item('enable_emulate_request') === TRUE)
  1053. {
  1054. $method = $this->input->post('_method');
  1055. if ($method === NULL)
  1056. {
  1057. $method = $this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE');
  1058. }
  1059.  
  1060. $method = strtolower($method);
  1061. }
  1062.  
  1063. if (empty($method))
  1064. {
  1065. // Get the request method as a lowercase string
  1066. $method = $this->input->method();
  1067. }
  1068.  
  1069. return in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method) ? $method : 'get';
  1070. }
  1071.  
  1072. /**
  1073. * See if the user has provided an API key
  1074. *
  1075. * @access protected
  1076. * @return bool
  1077. */
  1078. protected function _detect_api_key()
  1079. {
  1080. // Get the api key name variable set in the rest config file
  1081. $api_key_variable = $this->config->item('rest_key_name');
  1082.  
  1083. // Work out the name of the SERVER entry based on config
  1084. $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', $api_key_variable));
  1085.  
  1086. $this->rest->key = NULL;
  1087. $this->rest->level = NULL;
  1088. $this->rest->user_id = NULL;
  1089. $this->rest->ignore_limits = FALSE;
  1090.  
  1091. // Find the key from server or arguments
  1092. if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name)))
  1093. {
  1094. if ( ! ($row = $this->rest->db->where($this->config->item('rest_key_column'), $key)->get($this->config->item('rest_keys_table'))->row()))
  1095. {
  1096. return FALSE;
  1097. }
  1098.  
  1099. $this->rest->key = $row->{$this->config->item('rest_key_column')};
  1100.  
  1101. isset($row->user_id) && $this->rest->user_id = $row->user_id;
  1102. isset($row->level) && $this->rest->level = $row->level;
  1103. isset($row->ignore_limits) && $this->rest->ignore_limits = $row->ignore_limits;
  1104.  
  1105. $this->_apiuser = $row;
  1106.  
  1107. /*
  1108. * If "is private key" is enabled, compare the ip address with the list
  1109. * of valid ip addresses stored in the database
  1110. */
  1111. if (empty($row->is_private_key) === FALSE)
  1112. {
  1113. // Check for a list of valid ip addresses
  1114. if (isset($row->ip_addresses))
  1115. {
  1116. // multiple ip addresses must be separated using a comma, explode and loop
  1117. $list_ip_addresses = explode(',', $row->ip_addresses);
  1118. $found_address = FALSE;
  1119.  
  1120. foreach ($list_ip_addresses as $ip_address)
  1121. {
  1122. if ($this->input->ip_address() === trim($ip_address))
  1123. {
  1124. // there is a match, set the the value to TRUE and break out of the loop
  1125. $found_address = TRUE;
  1126. break;
  1127. }
  1128. }
  1129.  
  1130. return $found_address;
  1131. }
  1132. else
  1133. {
  1134. // There should be at least one IP address for this private key
  1135. return FALSE;
  1136. }
  1137. }
  1138.  
  1139. return TRUE;
  1140. }
  1141.  
  1142. // No key has been sent
  1143. return FALSE;
  1144. }
  1145.  
  1146. /**
  1147. * Preferred return language
  1148. *
  1149. * @access protected
  1150. * @return string|NULL|array The language code
  1151. */
  1152. protected function _detect_lang()
  1153. {
  1154. $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE');
  1155. if ($lang === NULL)
  1156. {
  1157. return NULL;
  1158. }
  1159.  
  1160. // It appears more than one language has been sent using a comma delimiter
  1161. if (strpos($lang, ',') !== FALSE)
  1162. {
  1163. $langs = explode(',', $lang);
  1164.  
  1165. $return_langs = [];
  1166. foreach ($langs as $lang)
  1167. {
  1168. // Remove weight and trim leading and trailing whitespace
  1169. list($lang) = explode(';', $lang);
  1170. $return_langs[] = trim($lang);
  1171. }
  1172.  
  1173. return $return_langs;
  1174. }
  1175.  
  1176. // Otherwise simply return as a string
  1177. return $lang;
  1178. }
  1179.  
  1180. /**
  1181. * Add the request to the log table
  1182. *
  1183. * @access protected
  1184. * @param bool $authorized TRUE the user is authorized; otherwise, FALSE
  1185. * @return bool TRUE the data was inserted; otherwise, FALSE
  1186. */
  1187. protected function _log_request($authorized = FALSE)
  1188. {
  1189. // Insert the request into the log table
  1190. $is_inserted = $this->rest->db
  1191. ->insert(
  1192. $this->config->item('rest_logs_table'), [
  1193. 'uri' => $this->uri->uri_string(),
  1194. 'method' => $this->request->method,
  1195. 'params' => $this->_args ? ($this->config->item('rest_logs_json_params') === TRUE ? json_encode($this->_args) : serialize($this->_args)) : NULL,
  1196. 'api_key' => isset($this->rest->key) ? $this->rest->key : '',
  1197. 'ip_address' => $this->input->ip_address(),
  1198. 'time' => time(),
  1199. 'authorized' => $authorized
  1200. ]);
  1201.  
  1202. // Get the last insert id to update at a later stage of the request
  1203. $this->_insert_id = $this->rest->db->insert_id();
  1204.  
  1205. return $is_inserted;
  1206. }
  1207.  
  1208. /**
  1209. * Check if the requests to a controller method exceed a limit
  1210. *
  1211. * @access protected
  1212. * @param string $controller_method The method being called
  1213. * @return bool TRUE the call limit is below the threshold; otherwise, FALSE
  1214. */
  1215. protected function _check_limit($controller_method)
  1216. {
  1217. // They are special, or it might not even have a limit
  1218. if (empty($this->rest->ignore_limits) === FALSE)
  1219. {
  1220. // Everything is fine
  1221. return TRUE;
  1222. }
  1223.  
  1224. $api_key = isset($this->rest->key) ? $this->rest->key : '';
  1225.  
  1226. switch ($this->config->item('rest_limits_method'))
  1227. {
  1228. case 'IP_ADDRESS':
  1229. $limited_uri = 'ip-address:' .$this->input->ip_address();
  1230. $api_key = $this->input->ip_address();
  1231. break;
  1232.  
  1233. case 'API_KEY':
  1234. $limited_uri = 'api-key:' . $api_key;
  1235. break;
  1236.  
  1237. case 'METHOD_NAME':
  1238. $limited_uri = 'method-name:' . $controller_method;
  1239. break;
  1240.  
  1241. case 'ROUTED_URL':
  1242. default:
  1243. $limited_uri = $this->uri->ruri_string();
  1244. if (strpos(strrev($limited_uri), strrev($this->response->format)) === 0)
  1245. {
  1246. $limited_uri = substr($limited_uri,0, -strlen($this->response->format) - 1);
  1247. }
  1248. $limited_uri = 'uri:'.$limited_uri.':'.$this->request->method; // It's good to differentiate GET from PUT
  1249. break;
  1250. }
  1251.  
  1252. if (isset($this->methods[$controller_method]['limit']) === FALSE )
  1253. {
  1254. // Everything is fine
  1255. return TRUE;
  1256. }
  1257.  
  1258. // How many times can you get to this method in a defined time_limit (default: 1 hour)?
  1259. $limit = $this->methods[$controller_method]['limit'];
  1260.  
  1261. $time_limit = (isset($this->methods[$controller_method]['time']) ? $this->methods[$controller_method]['time'] : 3600); // 3600 = 60 * 60
  1262.  
  1263. // Get data about a keys' usage and limit to one row
  1264. $result = $this->rest->db
  1265. ->where('uri', $limited_uri)
  1266. ->where('api_key', $api_key)
  1267. ->get($this->config->item('rest_limits_table'))
  1268. ->row();
  1269.  
  1270. // No calls have been made for this key
  1271. if ($result === NULL)
  1272. {
  1273. // Create a new row for the following key
  1274. $this->rest->db->insert($this->config->item('rest_limits_table'), [
  1275. 'uri' => $limited_uri,
  1276. 'api_key' =>$api_key,
  1277. 'count' => 1,
  1278. 'hour_started' => time()
  1279. ]);
  1280. }
  1281.  
  1282. // Been a time limit (or by default an hour) since they called
  1283. elseif ($result->hour_started < (time() - $time_limit))
  1284. {
  1285. // Reset the started period and count
  1286. $this->rest->db
  1287. ->where('uri', $limited_uri)
  1288. ->where('api_key', $api_key)
  1289. ->set('hour_started', time())
  1290. ->set('count', 1)
  1291. ->update($this->config->item('rest_limits_table'));
  1292. }
  1293.  
  1294. // They have called within the hour, so lets update
  1295. else
  1296. {
  1297. // The limit has been exceeded
  1298. if ($result->count >= $limit)
  1299. {
  1300. return FALSE;
  1301. }
  1302.  
  1303. // Increase the count by one
  1304. $this->rest->db
  1305. ->where('uri', $limited_uri)
  1306. ->where('api_key', $api_key)
  1307. ->set('count', 'count + 1', FALSE)
  1308. ->update($this->config->item('rest_limits_table'));
  1309. }
  1310.  
  1311. return TRUE;
  1312. }
  1313.  
  1314. /**
  1315. * Check if there is a specific auth type set for the current class/method/HTTP-method being called
  1316. *
  1317. * @access protected
  1318. * @return bool
  1319. */
  1320. protected function _auth_override_check()
  1321. {
  1322. // Assign the class/method auth type override array from the config
  1323. $auth_override_class_method = $this->config->item('auth_override_class_method');
  1324.  
  1325. // Check to see if the override array is even populated
  1326. if ( ! empty($auth_override_class_method))
  1327. {
  1328. // Check for wildcard flag for rules for classes
  1329. if ( ! empty($auth_override_class_method[$this->router->class]['*'])) // Check for class overrides
  1330. {
  1331. // No auth override found, prepare nothing but send back a TRUE override flag
  1332. if ($auth_override_class_method[$this->router->class]['*'] === 'none')
  1333. {
  1334. return TRUE;
  1335. }
  1336.  
  1337. // Basic auth override found, prepare basic
  1338. if ($auth_override_class_method[$this->router->class]['*'] === 'basic')
  1339. {
  1340. $this->_prepare_basic_auth();
  1341.  
  1342. return TRUE;
  1343. }
  1344.  
  1345. // Digest auth override found, prepare digest
  1346. if ($auth_override_class_method[$this->router->class]['*'] === 'digest')
  1347. {
  1348. $this->_prepare_digest_auth();
  1349.  
  1350. return TRUE;
  1351. }
  1352.  
  1353. // Session auth override found, check session
  1354. if ($auth_override_class_method[$this->router->class]['*'] === 'session')
  1355. {
  1356. $this->_check_php_session();
  1357.  
  1358. return TRUE;
  1359. }
  1360.  
  1361. // Whitelist auth override found, check client's ip against config whitelist
  1362. if ($auth_override_class_method[$this->router->class]['*'] === 'whitelist')
  1363. {
  1364. $this->_check_whitelist_auth();
  1365.  
  1366. return TRUE;
  1367. }
  1368. }
  1369.  
  1370. // Check to see if there's an override value set for the current class/method being called
  1371. if ( ! empty($auth_override_class_method[$this->router->class][$this->router->method]))
  1372. {
  1373. // None auth override found, prepare nothing but send back a TRUE override flag
  1374. if ($auth_override_class_method[$this->router->class][$this->router->method] === 'none')
  1375. {
  1376. return TRUE;
  1377. }
  1378.  
  1379. // Basic auth override found, prepare basic
  1380. if ($auth_override_class_method[$this->router->class][$this->router->method] === 'basic')
  1381. {
  1382. $this->_prepare_basic_auth();
  1383.  
  1384. return TRUE;
  1385. }
  1386.  
  1387. // Digest auth override found, prepare digest
  1388. if ($auth_override_class_method[$this->router->class][$this->router->method] === 'digest')
  1389. {
  1390. $this->_prepare_digest_auth();
  1391.  
  1392. return TRUE;
  1393. }
  1394.  
  1395. // Session auth override found, check session
  1396. if ($auth_override_class_method[$this->router->class][$this->router->method] === 'session')
  1397. {
  1398. $this->_check_php_session();
  1399.  
  1400. return TRUE;
  1401. }
  1402.  
  1403. // Whitelist auth override found, check client's ip against config whitelist
  1404. if ($auth_override_class_method[$this->router->class][$this->router->method] === 'whitelist')
  1405. {
  1406. $this->_check_whitelist_auth();
  1407.  
  1408. return TRUE;
  1409. }
  1410. }
  1411. }
  1412.  
  1413. // Assign the class/method/HTTP-method auth type override array from the config
  1414. $auth_override_class_method_http = $this->config->item('auth_override_class_method_http');
  1415.  
  1416. // Check to see if the override array is even populated
  1417. if ( ! empty($auth_override_class_method_http))
  1418. {
  1419. // check for wildcard flag for rules for classes
  1420. if ( ! empty($auth_override_class_method_http[$this->router->class]['*'][$this->request->method]))
  1421. {
  1422. // None auth override found, prepare nothing but send back a TRUE override flag
  1423. if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'none')
  1424. {
  1425. return TRUE;
  1426. }
  1427.  
  1428. // Basic auth override found, prepare basic
  1429. if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'basic')
  1430. {
  1431. $this->_prepare_basic_auth();
  1432.  
  1433. return TRUE;
  1434. }
  1435.  
  1436. // Digest auth override found, prepare digest
  1437. if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'digest')
  1438. {
  1439. $this->_prepare_digest_auth();
  1440.  
  1441. return TRUE;
  1442. }
  1443.  
  1444. // Session auth override found, check session
  1445. if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'session')
  1446. {
  1447. $this->_check_php_session();
  1448.  
  1449. return TRUE;
  1450. }
  1451.  
  1452. // Whitelist auth override found, check client's ip against config whitelist
  1453. if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'whitelist')
  1454. {
  1455. $this->_check_whitelist_auth();
  1456.  
  1457. return TRUE;
  1458. }
  1459. }
  1460.  
  1461. // Check to see if there's an override value set for the current class/method/HTTP-method being called
  1462. if ( ! empty($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method]))
  1463. {
  1464. // None auth override found, prepare nothing but send back a TRUE override flag
  1465. if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'none')
  1466. {
  1467. return TRUE;
  1468. }
  1469.  
  1470. // Basic auth override found, prepare basic
  1471. if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'basic')
  1472. {
  1473. $this->_prepare_basic_auth();
  1474.  
  1475. return TRUE;
  1476. }
  1477.  
  1478. // Digest auth override found, prepare digest
  1479. if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'digest')
  1480. {
  1481. $this->_prepare_digest_auth();
  1482.  
  1483. return TRUE;
  1484. }
  1485.  
  1486. // Session auth override found, check session
  1487. if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'session')
  1488. {
  1489. $this->_check_php_session();
  1490.  
  1491. return TRUE;
  1492. }
  1493.  
  1494. // Whitelist auth override found, check client's ip against config whitelist
  1495. if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'whitelist')
  1496. {
  1497. $this->_check_whitelist_auth();
  1498.  
  1499. return TRUE;
  1500. }
  1501. }
  1502. }
  1503. return FALSE;
  1504. }
  1505.  
  1506. /**
  1507. * Parse the GET request arguments
  1508. *
  1509. * @access protected
  1510. * @return void
  1511. */
  1512. protected function _parse_get()
  1513. {
  1514. // Merge both the URI segments and query parameters
  1515. $this->_get_args = array_merge($this->_get_args, $this->_query_args);
  1516. }
  1517.  
  1518. /**
  1519. * Parse the POST request arguments
  1520. *
  1521. * @access protected
  1522. * @return void
  1523. */
  1524. protected function _parse_post()
  1525. {
  1526. $this->_post_args = $_POST;
  1527.  
  1528. if ($this->request->format)
  1529. {
  1530. $this->request->body = $this->input->raw_input_stream;
  1531. }
  1532. }
  1533.  
  1534. /**
  1535. * Parse the PUT request arguments
  1536. *
  1537. * @access protected
  1538. * @return void
  1539. */
  1540. protected function _parse_put()
  1541. {
  1542. if ($this->request->format)
  1543. {
  1544. $this->request->body = $this->input->raw_input_stream;
  1545. if ($this->request->format === 'json')
  1546. {
  1547. $this->_put_args = json_decode($this->input->raw_input_stream);
  1548. }
  1549. }
  1550. else if ($this->input->method() === 'put')
  1551. {
  1552. // If no file type is provided, then there are probably just arguments
  1553. $this->_put_args = $this->input->input_stream();
  1554. }
  1555. }
  1556.  
  1557. /**
  1558. * Parse the HEAD request arguments
  1559. *
  1560. * @access protected
  1561. * @return void
  1562. */
  1563. protected function _parse_head()
  1564. {
  1565. // Parse the HEAD variables
  1566. parse_str(parse_url($this->input->server('REQUEST_URI'), PHP_URL_QUERY), $head);
  1567.  
  1568. // Merge both the URI segments and HEAD params
  1569. $this->_head_args = array_merge($this->_head_args, $head);
  1570. }
  1571.  
  1572. /**
  1573. * Parse the OPTIONS request arguments
  1574. *
  1575. * @access protected
  1576. * @return void
  1577. */
  1578. protected function _parse_options()
  1579. {
  1580. // Parse the OPTIONS variables
  1581. parse_str(parse_url($this->input->server('REQUEST_URI'), PHP_URL_QUERY), $options);
  1582.  
  1583. // Merge both the URI segments and OPTIONS params
  1584. $this->_options_args = array_merge($this->_options_args, $options);
  1585. }
  1586.  
  1587. /**
  1588. * Parse the PATCH request arguments
  1589. *
  1590. * @access protected
  1591. * @return void
  1592. */
  1593. protected function _parse_patch()
  1594. {
  1595. // It might be a HTTP body
  1596. if ($this->request->format)
  1597. {
  1598. $this->request->body = $this->input->raw_input_stream;
  1599. }
  1600. else if ($this->input->method() === 'patch')
  1601. {
  1602. // If no file type is provided, then there are probably just arguments
  1603. $this->_patch_args = $this->input->input_stream();
  1604. }
  1605. }
  1606.  
  1607. /**
  1608. * Parse the DELETE request arguments
  1609. *
  1610. * @access protected
  1611. * @return void
  1612. */
  1613. protected function _parse_delete()
  1614. {
  1615. // These should exist if a DELETE request
  1616. if ($this->input->method() === 'delete')
  1617. {
  1618. $this->_delete_args = $this->input->input_stream();
  1619. }
  1620. }
  1621.  
  1622. /**
  1623. * Parse the query parameters
  1624. *
  1625. * @access protected
  1626. * @return void
  1627. */
  1628. protected function _parse_query()
  1629. {
  1630. $this->_query_args = $this->input->get();
  1631. }
  1632.  
  1633. // INPUT FUNCTION --------------------------------------------------------------
  1634.  
  1635. /**
  1636. * Retrieve a value from a GET request
  1637. *
  1638. * @access public
  1639. * @param NULL $key Key to retrieve from the GET request
  1640. * If NULL an array of arguments is returned
  1641. * @param NULL $xss_clean Whether to apply XSS filtering
  1642. * @return array|string|NULL Value from the GET request; otherwise, NULL
  1643. */
  1644. public function get($key = NULL, $xss_clean = NULL)
  1645. {
  1646. if ($key === NULL)
  1647. {
  1648. return $this->_get_args;
  1649. }
  1650.  
  1651. return isset($this->_get_args[$key]) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : NULL;
  1652. }
  1653.  
  1654. /**
  1655. * Retrieve a value from a OPTIONS request
  1656. *
  1657. * @access public
  1658. * @param NULL $key Key to retrieve from the OPTIONS request.
  1659. * If NULL an array of arguments is returned
  1660. * @param NULL $xss_clean Whether to apply XSS filtering
  1661. * @return array|string|NULL Value from the OPTIONS request; otherwise, NULL
  1662. */
  1663. public function options($key = NULL, $xss_clean = NULL)
  1664. {
  1665. if ($key === NULL)
  1666. {
  1667. return $this->_options_args;
  1668. }
  1669.  
  1670. return isset($this->_options_args[$key]) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : NULL;
  1671. }
  1672.  
  1673. /**
  1674. * Retrieve a value from a HEAD request
  1675. *
  1676. * @access public
  1677. * @param NULL $key Key to retrieve from the HEAD request
  1678. * If NULL an array of arguments is returned
  1679. * @param NULL $xss_clean Whether to apply XSS filtering
  1680. * @return array|string|NULL Value from the HEAD request; otherwise, NULL
  1681. */
  1682. public function head($key = NULL, $xss_clean = NULL)
  1683. {
  1684. if ($key === NULL)
  1685. {
  1686. return $this->_head_args;
  1687. }
  1688.  
  1689. return isset($this->_head_args[$key]) ? $this->_xss_clean($this->_head_args[$key], $xss_clean) : NULL;
  1690. }
  1691.  
  1692. /**
  1693. * Retrieve a value from a POST request
  1694. *
  1695. * @access public
  1696. * @param NULL $key Key to retrieve from the POST request
  1697. * If NULL an array of arguments is returned
  1698. * @param NULL $xss_clean Whether to apply XSS filtering
  1699. * @return array|string|NULL Value from the POST request; otherwise, NULL
  1700. */
  1701. public function post($key = NULL, $xss_clean = NULL)
  1702. {
  1703. if ($key === NULL)
  1704. {
  1705. return $this->_post_args;
  1706. }
  1707.  
  1708. return isset($this->_post_args[$key]) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : NULL;
  1709. }
  1710.  
  1711. /**
  1712. * Retrieve a value from a PUT request
  1713. *
  1714. * @access public
  1715. * @param NULL $key Key to retrieve from the PUT request
  1716. * If NULL an array of arguments is returned
  1717. * @param NULL $xss_clean Whether to apply XSS filtering
  1718. * @return array|string|NULL Value from the PUT request; otherwise, NULL
  1719. */
  1720. public function put($key = NULL, $xss_clean = NULL)
  1721. {
  1722. if ($key === NULL)
  1723. {
  1724. return $this->_put_args;
  1725. }
  1726.  
  1727. return isset($this->_put_args[$key]) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : NULL;
  1728. }
  1729.  
  1730. /**
  1731. * Retrieve a value from a DELETE request
  1732. *
  1733. * @access public
  1734. * @param NULL $key Key to retrieve from the DELETE request
  1735. * If NULL an array of arguments is returned
  1736. * @param NULL $xss_clean Whether to apply XSS filtering
  1737. * @return array|string|NULL Value from the DELETE request; otherwise, NULL
  1738. */
  1739. public function delete($key = NULL, $xss_clean = NULL)
  1740. {
  1741. if ($key === NULL)
  1742. {
  1743. return $this->_delete_args;
  1744. }
  1745.  
  1746. return isset($this->_delete_args[$key]) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : NULL;
  1747. }
  1748.  
  1749. /**
  1750. * Retrieve a value from a PATCH request
  1751. *
  1752. * @access public
  1753. * @param NULL $key Key to retrieve from the PATCH request
  1754. * If NULL an array of arguments is returned
  1755. * @param NULL $xss_clean Whether to apply XSS filtering
  1756. * @return array|string|NULL Value from the PATCH request; otherwise, NULL
  1757. */
  1758. public function patch($key = NULL, $xss_clean = NULL)
  1759. {
  1760. if ($key === NULL)
  1761. {
  1762. return $this->_patch_args;
  1763. }
  1764.  
  1765. return isset($this->_patch_args[$key]) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : NULL;
  1766. }
  1767.  
  1768. /**
  1769. * Retrieve a value from the query parameters
  1770. *
  1771. * @access public
  1772. * @param NULL $key Key to retrieve from the query parameters
  1773. * If NULL an array of arguments is returned
  1774. * @param NULL $xss_clean Whether to apply XSS filtering
  1775. * @return array|string|NULL Value from the query parameters; otherwise, NULL
  1776. */
  1777. public function query($key = NULL, $xss_clean = NULL)
  1778. {
  1779. if ($key === NULL)
  1780. {
  1781. return $this->_query_args;
  1782. }
  1783.  
  1784. return isset($this->_query_args[$key]) ? $this->_xss_clean($this->_query_args[$key], $xss_clean) : NULL;
  1785. }
  1786.  
  1787. /**
  1788. * Sanitizes data so that Cross Site Scripting Hacks can be
  1789. * prevented
  1790. *
  1791. * @access protected
  1792. * @param string $value Input data
  1793. * @param bool $xss_clean Whether to apply XSS filtering
  1794. * @return string
  1795. */
  1796. protected function _xss_clean($value, $xss_clean)
  1797. {
  1798. is_bool($xss_clean) || $xss_clean = $this->_enable_xss;
  1799.  
  1800. return $xss_clean === TRUE ? $this->security->xss_clean($value) : $value;
  1801. }
  1802.  
  1803. /**
  1804. * Retrieve the validation errors
  1805. *
  1806. * @access public
  1807. * @return array
  1808. */
  1809. public function validation_errors()
  1810. {
  1811. $string = strip_tags($this->form_validation->error_string());
  1812.  
  1813. return explode(PHP_EOL, trim($string, PHP_EOL));
  1814. }
  1815.  
  1816. // SECURITY FUNCTIONS ---------------------------------------------------------
  1817.  
  1818. /**
  1819. * Perform LDAP Authentication
  1820. *
  1821. * @access protected
  1822. * @param string $username The username to validate
  1823. * @param string $password The password to validate
  1824. * @return bool
  1825. */
  1826. protected function _perform_ldap_auth($username = '', $password = NULL)
  1827. {
  1828. if (empty($username))
  1829. {
  1830. log_message('debug', 'LDAP Auth: failure, empty username');
  1831. return FALSE;
  1832. }
  1833.  
  1834. log_message('debug', 'LDAP Auth: Loading configuration');
  1835.  
  1836. $this->config->load('ldap', TRUE);
  1837.  
  1838. $ldap = [
  1839. 'timeout' => $this->config->item('timeout', 'ldap'),
  1840. 'host' => $this->config->item('server', 'ldap'),
  1841. 'port' => $this->config->item('port', 'ldap'),
  1842. 'rdn' => $this->config->item('binduser', 'ldap'),
  1843. 'pass' => $this->config->item('bindpw', 'ldap'),
  1844. 'basedn' => $this->config->item('basedn', 'ldap'),
  1845. ];
  1846.  
  1847. log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]'));
  1848.  
  1849. // Connect to the ldap server
  1850. $ldapconn = ldap_connect($ldap['host'], $ldap['port']);
  1851. if ($ldapconn)
  1852. {
  1853. log_message('debug', 'Setting timeout to '.$ldap['timeout'].' seconds');
  1854.  
  1855. ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldap['timeout']);
  1856.  
  1857. log_message('debug', 'LDAP Auth: Binding to '.$ldap['host'].' with dn '.$ldap['rdn']);
  1858.  
  1859. // Binding to the ldap server
  1860. $ldapbind = ldap_bind($ldapconn, $ldap['rdn'], $ldap['pass']);
  1861.  
  1862. // Verify the binding
  1863. if ($ldapbind === FALSE)
  1864. {
  1865. log_message('error', 'LDAP Auth: bind was unsuccessful');
  1866. return FALSE;
  1867. }
  1868.  
  1869. log_message('debug', 'LDAP Auth: bind successful');
  1870. }
  1871.  
  1872. // Search for user
  1873. if (($res_id = ldap_search($ldapconn, $ldap['basedn'], "uid=$username")) === FALSE)
  1874. {
  1875. log_message('error', 'LDAP Auth: User '.$username.' not found in search');
  1876. return FALSE;
  1877. }
  1878.  
  1879. if (ldap_count_entries($ldapconn, $res_id) !== 1)
  1880. {
  1881. log_message('error', 'LDAP Auth: Failure, username '.$username.'found more than once');
  1882. return FALSE;
  1883. }
  1884.  
  1885. if (($entry_id = ldap_first_entry($ldapconn, $res_id)) === FALSE)
  1886. {
  1887. log_message('error', 'LDAP Auth: Failure, entry of search result could not be fetched');
  1888. return FALSE;
  1889. }
  1890.  
  1891. if (($user_dn = ldap_get_dn($ldapconn, $entry_id)) === FALSE)
  1892. {
  1893. log_message('error', 'LDAP Auth: Failure, user-dn could not be fetched');
  1894. return FALSE;
  1895. }
  1896.  
  1897. // User found, could not authenticate as user
  1898. if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) === FALSE)
  1899. {
  1900. log_message('error', 'LDAP Auth: Failure, username/password did not match: ' . $user_dn);
  1901. return FALSE;
  1902. }
  1903.  
  1904. log_message('debug', 'LDAP Auth: Success '.$user_dn.' authenticated successfully');
  1905.  
  1906. $this->_user_ldap_dn = $user_dn;
  1907.  
  1908. ldap_close($ldapconn);
  1909.  
  1910. return TRUE;
  1911. }
  1912.  
  1913. /**
  1914. * Perform Library Authentication - Override this function to change the way the library is called
  1915. *
  1916. * @access protected
  1917. * @param string $username The username to validate
  1918. * @param string $password The password to validate
  1919. * @return bool
  1920. */
  1921. protected function _perform_library_auth($username = '', $password = NULL)
  1922. {
  1923. if (empty($username))
  1924. {
  1925. log_message('error', 'Library Auth: Failure, empty username');
  1926. return FALSE;
  1927. }
  1928.  
  1929. $auth_library_class = strtolower($this->config->item('auth_library_class'));
  1930. $auth_library_function = strtolower($this->config->item('auth_library_function'));
  1931.  
  1932. if (empty($auth_library_class))
  1933. {
  1934. log_message('debug', 'Library Auth: Failure, empty auth_library_class');
  1935. return FALSE;
  1936. }
  1937.  
  1938. if (empty($auth_library_function))
  1939. {
  1940. log_message('debug', 'Library Auth: Failure, empty auth_library_function');
  1941. return FALSE;
  1942. }
  1943.  
  1944. if (is_callable([$auth_library_class, $auth_library_function]) === FALSE)
  1945. {
  1946. $this->load->library($auth_library_class);
  1947. }
  1948.  
  1949. return $this->{$auth_library_class}->$auth_library_function($username, $password);
  1950. }
  1951.  
  1952. /**
  1953. * Check if the user is logged in
  1954. *
  1955. * @access protected
  1956. * @param string $username The user's name
  1957. * @param bool|string $password The user's password
  1958. * @return bool
  1959. */
  1960. protected function _check_login($username = NULL, $password = FALSE)
  1961. {
  1962. if (empty($username))
  1963. {
  1964. return FALSE;
  1965. }
  1966.  
  1967. $auth_source = strtolower($this->config->item('auth_source'));
  1968. $rest_auth = strtolower($this->config->item('rest_auth'));
  1969. $valid_logins = $this->config->item('rest_valid_logins');
  1970.  
  1971. if ( ! $this->config->item('auth_source') && $rest_auth === 'digest')
  1972. {
  1973. // For digest we do not have a password passed as argument
  1974. return md5($username.':'.$this->config->item('rest_realm').':'.(isset($valid_logins[$username]) ? $valid_logins[$username] : ''));
  1975. }
  1976.  
  1977. if ($password === FALSE)
  1978. {
  1979. return FALSE;
  1980. }
  1981.  
  1982. if ($auth_source === 'ldap')
  1983. {
  1984. log_message('debug', "Performing LDAP authentication for $username");
  1985.  
  1986. return $this->_perform_ldap_auth($username, $password);
  1987. }
  1988.  
  1989. if ($auth_source === 'library')
  1990. {
  1991. log_message('debug', "Performing Library authentication for $username");
  1992.  
  1993. return $this->_perform_library_auth($username, $password);
  1994. }
  1995.  
  1996. if (array_key_exists($username, $valid_logins) === FALSE)
  1997. {
  1998. return FALSE;
  1999. }
  2000.  
  2001. if ($valid_logins[$username] !== $password)
  2002. {
  2003. return FALSE;
  2004. }
  2005.  
  2006. return TRUE;
  2007. }
  2008.  
  2009. /**
  2010. * Check to see if the user is logged in with a PHP session key
  2011. *
  2012. * @access protected
  2013. * @return void
  2014. */
  2015. protected function _check_php_session()
  2016. {
  2017. // Get the auth_source config item
  2018. $key = $this->config->item('auth_source');
  2019.  
  2020. // If false, then the user isn't logged in
  2021. if ( ! $this->session->userdata($key))
  2022. {
  2023. // Display an error response
  2024. $this->response([
  2025. $this->config->item('rest_status_field_name') => FALSE,
  2026. $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unauthorized')
  2027. ], self::HTTP_UNAUTHORIZED);
  2028. }
  2029. }
  2030.  
  2031. /**
  2032. * Prepares for basic authentication
  2033. *
  2034. * @access protected
  2035. * @return void
  2036. */
  2037. protected function _prepare_basic_auth()
  2038. {
  2039. // If whitelist is enabled it has the first chance to kick them out
  2040. if ($this->config->item('rest_ip_whitelist_enabled'))
  2041. {
  2042. $this->_check_whitelist_auth();
  2043. }
  2044.  
  2045. // Returns NULL if the SERVER variables PHP_AUTH_USER and HTTP_AUTHENTICATION don't exist
  2046. $username = $this->input->server('PHP_AUTH_USER');
  2047. $http_auth = $this->input->server('HTTP_AUTHENTICATION') ?: $this->input->server('HTTP_AUTHORIZATION');
  2048.  
  2049. $password = NULL;
  2050. if ($username !== NULL)
  2051. {
  2052. $password = $this->input->server('PHP_AUTH_PW');
  2053. }
  2054. elseif ($http_auth !== NULL)
  2055. {
  2056. // If the authentication header is set as basic, then extract the username and password from
  2057. // HTTP_AUTHORIZATION e.g. my_username:my_password. This is passed in the .htaccess file
  2058. if (strpos(strtolower($http_auth), 'basic') === 0)
  2059. {
  2060. // Search online for HTTP_AUTHORIZATION workaround to explain what this is doing
  2061. list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6)));
  2062. }
  2063. }
  2064.  
  2065. // Check if the user is logged into the system
  2066. if ($this->_check_login($username, $password) === FALSE)
  2067. {
  2068. $this->_force_login();
  2069. }
  2070. }
  2071.  
  2072. /**
  2073. * Prepares for digest authentication
  2074. *
  2075. * @access protected
  2076. * @return void
  2077. */
  2078. protected function _prepare_digest_auth()
  2079. {
  2080. // If whitelist is enabled it has the first chance to kick them out
  2081. if ($this->config->item('rest_ip_whitelist_enabled'))
  2082. {
  2083. $this->_check_whitelist_auth();
  2084. }
  2085.  
  2086. // We need to test which server authentication variable to use,
  2087. // because the PHP ISAPI module in IIS acts different from CGI
  2088. $digest_string = $this->input->server('PHP_AUTH_DIGEST');
  2089. if ($digest_string === NULL)
  2090. {
  2091. $digest_string = $this->input->server('HTTP_AUTHORIZATION');
  2092. }
  2093.  
  2094. $unique_id = uniqid();
  2095.  
  2096. // The $_SESSION['error_prompted'] variable is used to ask the password
  2097. // again if none given or if the user enters wrong auth information
  2098. if (empty($digest_string))
  2099. {
  2100. $this->_force_login($unique_id);
  2101. }
  2102.  
  2103. // We need to retrieve authentication data from the $digest_string variable
  2104. $matches = [];
  2105. preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches);
  2106. $digest = (empty($matches[1]) || empty($matches[2])) ? [] : array_combine($matches[1], $matches[2]);
  2107.  
  2108. // For digest authentication the library function should return already stored md5(username:restrealm:password) for that username see rest.php::auth_library_function config
  2109. $username = $this->_check_login($digest['username'], TRUE);
  2110. if (array_key_exists('username', $digest) === FALSE || $username === FALSE)
  2111. {
  2112. $this->_force_login($unique_id);
  2113. }
  2114.  
  2115. $md5 = md5(strtoupper($this->request->method).':'.$digest['uri']);
  2116. $valid_response = md5($username.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$md5);
  2117.  
  2118. // Check if the string don't compare (case-insensitive)
  2119. if (strcasecmp($digest['response'], $valid_response) !== 0)
  2120. {
  2121. // Display an error response
  2122. $this->response([
  2123. $this->config->item('rest_status_field_name') => FALSE,
  2124. $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_invalid_credentials')
  2125. ], self::HTTP_UNAUTHORIZED);
  2126. }
  2127. }
  2128.  
  2129. /**
  2130. * Checks if the client's ip is in the 'rest_ip_blacklist' config and generates a 401 response
  2131. *
  2132. * @access protected
  2133. * @return void
  2134. */
  2135. protected function _check_blacklist_auth()
  2136. {
  2137. // Match an ip address in a blacklist e.g. 127.0.0.0, 0.0.0.0
  2138. $pattern = sprintf('/(?:,\s*|^)\Q%s\E(?=,\s*|$)/m', $this->input->ip_address());
  2139.  
  2140. // Returns 1, 0 or FALSE (on error only). Therefore implicitly convert 1 to TRUE
  2141. if (preg_match($pattern, $this->config->item('rest_ip_blacklist')))
  2142. {
  2143. // Display an error response
  2144. $this->response([
  2145. $this->config->item('rest_status_field_name') => FALSE,
  2146. $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_denied')
  2147. ], self::HTTP_UNAUTHORIZED);
  2148. }
  2149. }
  2150.  
  2151. /**
  2152. * Check if the client's ip is in the 'rest_ip_whitelist' config and generates a 401 response
  2153. *
  2154. * @access protected
  2155. * @return void
  2156. */
  2157. protected function _check_whitelist_auth()
  2158. {
  2159. $whitelist = explode(',', $this->config->item('rest_ip_whitelist'));
  2160.  
  2161. array_push($whitelist, '127.0.0.1', '0.0.0.0');
  2162.  
  2163. foreach ($whitelist as &$ip)
  2164. {
  2165. // As $ip is a reference, trim leading and trailing whitespace, then store the new value
  2166. // using the reference
  2167. $ip = trim($ip);
  2168. }
  2169.  
  2170. if (in_array($this->input->ip_address(), $whitelist) === FALSE)
  2171. {
  2172. $this->response([
  2173. $this->config->item('rest_status_field_name') => FALSE,
  2174. $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_unauthorized')
  2175. ], self::HTTP_UNAUTHORIZED);
  2176. }
  2177. }
  2178.  
  2179. /**
  2180. * Force logging in by setting the WWW-Authenticate header
  2181. *
  2182. * @access protected
  2183. * @param string $nonce A server-specified data string which should be uniquely generated
  2184. * each time
  2185. * @return void
  2186. */
  2187. protected function _force_login($nonce = '')
  2188. {
  2189. $rest_auth = $this->config->item('rest_auth');
  2190. $rest_realm = $this->config->item('rest_realm');
  2191. if (strtolower($rest_auth) === 'basic')
  2192. {
  2193. // See http://tools.ietf.org/html/rfc2617#page-5
  2194. header('WWW-Authenticate: Basic realm="'.$rest_realm.'"');
  2195. }
  2196. elseif (strtolower($rest_auth) === 'digest')
  2197. {
  2198. // See http://tools.ietf.org/html/rfc2617#page-18
  2199. header(
  2200. 'WWW-Authenticate: Digest realm="'.$rest_realm
  2201. .'", qop="auth", nonce="'.$nonce
  2202. .'", opaque="' . md5($rest_realm).'"');
  2203. }
  2204.  
  2205. if ($this->config->item('strict_api_and_auth') === true) {
  2206. $this->is_valid_request = false;
  2207. }
  2208.  
  2209. // Display an error response
  2210. $this->response([
  2211. $this->config->item('rest_status_field_name') => FALSE,
  2212. $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unauthorized')
  2213. ], self::HTTP_UNAUTHORIZED);
  2214. }
  2215.  
  2216. /**
  2217. * Updates the log table with the total access time
  2218. *
  2219. * @access protected
  2220. * @author Chris Kacerguis
  2221. * @return bool TRUE log table updated; otherwise, FALSE
  2222. */
  2223. protected function _log_access_time()
  2224. {
  2225. if($this->_insert_id == ''){
  2226. return false;
  2227. }
  2228.  
  2229. $payload['rtime'] = $this->_end_rtime - $this->_start_rtime;
  2230.  
  2231. return $this->rest->db->update(
  2232. $this->config->item('rest_logs_table'), $payload, [
  2233. 'id' => $this->_insert_id
  2234. ]);
  2235. }
  2236.  
  2237. /**
  2238. * Updates the log table with HTTP response code
  2239. *
  2240. * @access protected
  2241. * @author Justin Chen
  2242. * @param $http_code int HTTP status code
  2243. * @return bool TRUE log table updated; otherwise, FALSE
  2244. */
  2245. protected function _log_response_code($http_code)
  2246. {
  2247. if($this->_insert_id == ''){
  2248. return false;
  2249. }
  2250.  
  2251. $payload['response_code'] = $http_code;
  2252.  
  2253. return $this->rest->db->update(
  2254. $this->config->item('rest_logs_table'), $payload, [
  2255. 'id' => $this->_insert_id
  2256. ]);
  2257. }
  2258.  
  2259. /**
  2260. * Check to see if the API key has access to the controller and methods
  2261. *
  2262. * @access protected
  2263. * @return bool TRUE the API key has access; otherwise, FALSE
  2264. */
  2265. protected function _check_access()
  2266. {
  2267. // If we don't want to check access, just return TRUE
  2268. if ($this->config->item('rest_enable_access') === FALSE)
  2269. {
  2270. return TRUE;
  2271. }
  2272.  
  2273. //check if the key has all_access
  2274. $accessRow = $this->rest->db
  2275. ->where('key', $this->rest->key)
  2276. ->get($this->config->item('rest_access_table'))->row_array();
  2277.  
  2278. if (!empty($accessRow) && !empty($accessRow['all_access']))
  2279. {
  2280. return TRUE;
  2281. }
  2282.  
  2283. // Fetch controller based on path and controller name
  2284. $controller = implode(
  2285. '/', [
  2286. $this->router->directory,
  2287. $this->router->class
  2288. ]);
  2289.  
  2290. // Remove any double slashes for safety
  2291. $controller = str_replace('//', '/', $controller);
  2292.  
  2293. // Query the access table and get the number of results
  2294. return $this->rest->db
  2295. ->where('key', $this->rest->key)
  2296. ->where('controller', $controller)
  2297. ->get($this->config->item('rest_access_table'))
  2298. ->num_rows() > 0;
  2299. }
  2300.  
  2301. /**
  2302. * Checks allowed domains, and adds appropriate headers for HTTP access control (CORS)
  2303. *
  2304. * @access protected
  2305. * @return void
  2306. */
  2307. protected function _check_cors()
  2308. {
  2309. // Convert the config items into strings
  2310. $allowed_headers = implode(', ', $this->config->item('allowed_cors_headers'));
  2311. $allowed_methods = implode(', ', $this->config->item('allowed_cors_methods'));
  2312.  
  2313. // If we want to allow any domain to access the API
  2314. if ($this->config->item('allow_any_cors_domain') === TRUE)
  2315. {
  2316. header('Access-Control-Allow-Origin: *');
  2317. header('Access-Control-Allow-Headers: '.$allowed_headers);
  2318. header('Access-Control-Allow-Methods: '.$allowed_methods);
  2319. }
  2320. else
  2321. {
  2322. // We're going to allow only certain domains access
  2323. // Store the HTTP Origin header
  2324. $origin = $this->input->server('HTTP_ORIGIN');
  2325. if ($origin === NULL)
  2326. {
  2327. $origin = '';
  2328. }
  2329.  
  2330. // If the origin domain is in the allowed_cors_origins list, then add the Access Control headers
  2331. if (in_array($origin, $this->config->item('allowed_cors_origins')))
  2332. {
  2333. header('Access-Control-Allow-Origin: '.$origin);
  2334. header('Access-Control-Allow-Headers: '.$allowed_headers);
  2335. header('Access-Control-Allow-Methods: '.$allowed_methods);
  2336. }
  2337. }
  2338.  
  2339. // If the request HTTP method is 'OPTIONS', kill the response and send it to the client
  2340. if ($this->input->method() === 'options')
  2341. {
  2342. exit;
  2343. }
  2344. }
  2345.  
  2346. public function check_login_admin()
  2347. {
  2348. if(isset($_SESSION['login'])){
  2349. if(isset($_SESSION['user_id'])){
  2350. if(isset($_SESSION['level_id'])){
  2351.  
  2352. }else{
  2353. header("location:login");
  2354. }
  2355. }else{
  2356. header("location:login");
  2357. }
  2358. }else{
  2359. header("location:login");
  2360. }
  2361. }
  2362. }
Add Comment
Please, Sign In to add comment