diff --git a/application/config/config.php b/application/config/config.php index 1c0993c92..591b84f2c 100644 --- a/application/config/config.php +++ b/application/config/config.php @@ -503,3 +503,15 @@ $config['rewrite_short_tags'] = FALSE; | Array: array('10.0.1.200', '192.168.5.0/24') */ $config['proxy_ips'] = ''; + +/* +|-------------------------------------------------------------------------- +| FHComplete Build Version +|-------------------------------------------------------------------------- +| +| Version Number of the Current Build +| This is used to invalidate Cache for JS and CSS Files +| +| Example: 2019102901 +*/ +$config['fhcomplete_build_version'] = '2019102903'; diff --git a/application/config/navigation.php b/application/config/navigation.php index d6f4b8452..1d8efb4c5 100644 --- a/application/config/navigation.php +++ b/application/config/navigation.php @@ -82,6 +82,13 @@ $config['navigation_header'] = array( 'expand' => true, 'sort' => 10, 'requiredPermissions' => 'admin:r' + ), + 'logsviewer' => array( + 'link' => site_url('system/LogsViewer'), + 'description' => 'Logs', + 'expand' => true, + 'sort' => 20, + 'requiredPermissions' => 'system/developer:r' ) ) ) diff --git a/application/config/rest.php b/application/config/rest.php index 9bbbf40ef..833a99881 100644 --- a/application/config/rest.php +++ b/application/config/rest.php @@ -1,6 +1,6 @@ '1234', 'test' => 'test']; - -/* -|-------------------------------------------------------------------------- -| Global IP Whitelisting -|-------------------------------------------------------------------------- -| -| Limit connections to your REST server to whitelisted IP addresses +| Limit connections to your REST server to White-listed IP addresses | | Usage: | 1. Set to TRUE and select an auth option for extreme security (client's IP -| address must be in whitelist and they must also log in) -| 2. Set to TRUE with auth set to FALSE to allow whitelisted IPs access with no login -| 3. Set to FALSE but set 'auth_override_class_method' to 'whitelist' to -| restrict certain methods to IPs in your whitelist +| address must be in white-list and they must also log in) +| 2. Set to TRUE with auth set to FALSE to allow White-listed IPs access with no login +| 3. Set to FALSE but set 'auth_override_class_method' to 'white-list' to +| restrict certain methods to IPs in your white-list | */ $config['rest_ip_whitelist_enabled'] = TRUE; /* |-------------------------------------------------------------------------- -| REST IP Whitelist +| REST IP White-list |-------------------------------------------------------------------------- | | Limit connections to your REST server with a comma separated @@ -240,42 +107,6 @@ $config['rest_ip_whitelist_enabled'] = TRUE; */ $config['rest_ip_whitelist'] = '127.0.0.1'; -/* -|-------------------------------------------------------------------------- -| Global IP Blacklisting -|-------------------------------------------------------------------------- -| -| Prevent connections to the REST server from blacklisted IP addresses -| -| Usage: -| 1. Set to TRUE and add any IP address to 'rest_ip_blacklist' -| -*/ -$config['rest_ip_blacklist_enabled'] = FALSE; - -/* -|-------------------------------------------------------------------------- -| REST IP Blacklist -|-------------------------------------------------------------------------- -| -| Prevent connections from the following IP addresses -| -| e.g: '123.456.789.0, 987.654.32.1' -| -*/ -$config['rest_ip_blacklist'] = ''; - -/* -|-------------------------------------------------------------------------- -| REST Database Group -|-------------------------------------------------------------------------- -| -| Connect to a database group for keys, logging, etc. It will only connect -| if you have any of these features enabled -| -*/ -$config['rest_database_group'] = 'default'; - /* |-------------------------------------------------------------------------- | REST API Keys Table Name @@ -298,6 +129,7 @@ $config['rest_keys_table'] = 'ci_apikey'; | Default table schema: | CREATE TABLE `keys` ( | `id` INT(11) NOT NULL AUTO_INCREMENT, +| `user_id` INT(11) NOT NULL, | `key` VARCHAR(40) NOT NULL, | `level` INT(2) NOT NULL, | `ignore_limits` TINYINT(1) NOT NULL DEFAULT '0', @@ -310,45 +142,6 @@ $config['rest_keys_table'] = 'ci_apikey'; */ $config['rest_enable_keys'] = TRUE; -/* -|-------------------------------------------------------------------------- -| REST Table Key Column Name -|-------------------------------------------------------------------------- -| -| If not using the default table schema in 'rest_enable_keys', specify the -| column name to match e.g. my_key -| -*/ -$config['rest_key_column'] = 'key'; - -/* -|-------------------------------------------------------------------------- -| REST API Limits method -|-------------------------------------------------------------------------- -| -| Specify the method used to limit the API calls -| -| Available methods are : -| $config['rest_limits_method'] = 'API_KEY'; // Put a limit per api key -| $config['rest_limits_method'] = 'METHOD_NAME'; // Put a limit on method calls -| $config['rest_limits_method'] = 'ROUTED_URL'; // Put a limit on the routed URL -| -*/ -$config['rest_limits_method'] = 'ROUTED_URL'; - -/* -|-------------------------------------------------------------------------- -| REST Key Length -|-------------------------------------------------------------------------- -| -| Length of the created keys. Check your default database schema on the -| maximum length allowed -| -| Note: The maximum length is 40 -| -*/ -$config['rest_key_length'] = 40; - /* |-------------------------------------------------------------------------- | REST API Key Variable @@ -364,156 +157,10 @@ $config['rest_key_name'] = 'FHC-API-KEY'; /* |-------------------------------------------------------------------------- -| REST Enable Logging +| REST Methods name format |-------------------------------------------------------------------------- | -| When set to TRUE, the REST API will log actions based on the column names 'key', 'date', -| 'time' and 'ip_address'. This is a general rule that can be overridden in the -| $this->method array for each controller -| -| Default table schema: -| CREATE TABLE `logs` ( -| `id` INT(11) NOT NULL AUTO_INCREMENT, -| `uri` VARCHAR(255) NOT NULL, -| `method` VARCHAR(6) NOT NULL, -| `params` TEXT DEFAULT NULL, -| `api_key` VARCHAR(40) NOT NULL, -| `ip_address` VARCHAR(45) NOT NULL, -| `time` INT(11) NOT NULL, -| `rtime` FLOAT DEFAULT NULL, -| `authorized` VARCHAR(1) NOT NULL, -| `response_code` smallint(3) DEFAULT '0', -| PRIMARY KEY (`id`) -| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +| REST Controllers methods name format | */ -$config['rest_enable_logging'] = FALSE; - -/* -|-------------------------------------------------------------------------- -| REST API Logs Table Name -|-------------------------------------------------------------------------- -| -| If not using the default table schema in 'rest_enable_logging', specify the -| table name to match e.g. my_logs -| -*/ -$config['rest_logs_table'] = 'logs'; - -/* -|-------------------------------------------------------------------------- -| REST Method Access Control -|-------------------------------------------------------------------------- -| When set to TRUE, the REST API will check the access table to see if -| the API key can access that controller. 'rest_enable_keys' must be enabled -| to use this -| -| Default table schema: -| CREATE TABLE `access` ( -| `id` INT(11) unsigned NOT NULL AUTO_INCREMENT, -| `key` VARCHAR(40) NOT NULL DEFAULT '', -| `controller` VARCHAR(50) NOT NULL DEFAULT '', -| `date_created` DATETIME DEFAULT NULL, -| `date_modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -| PRIMARY KEY (`id`) -| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -| -*/ -$config['rest_enable_access'] = FALSE; - -/* -|-------------------------------------------------------------------------- -| REST API Access Table Name -|-------------------------------------------------------------------------- -| -| If not using the default table schema in 'rest_enable_access', specify the -| table name to match e.g. my_access -| -*/ -$config['rest_access_table'] = 'access'; - -/* -|-------------------------------------------------------------------------- -| REST API Param Log Format -|-------------------------------------------------------------------------- -| -| When set to TRUE, the REST API log parameters will be stored in the database as JSON -| Set to FALSE to log as serialized PHP -| -*/ -$config['rest_logs_json_params'] = FALSE; - -/* -|-------------------------------------------------------------------------- -| REST Enable Limits -|-------------------------------------------------------------------------- -| -| When set to TRUE, the REST API will count the number of uses of each method -| by an API key each hour. This is a general rule that can be overridden in the -| $this->method array in each controller -| -| Default table schema: -| CREATE TABLE `limits` ( -| `id` INT(11) NOT NULL AUTO_INCREMENT, -| `uri` VARCHAR(255) NOT NULL, -| `count` INT(10) NOT NULL, -| `hour_started` INT(11) NOT NULL, -| `api_key` VARCHAR(40) NOT NULL, -| PRIMARY KEY (`id`) -| ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -| -| To specify the limits within the controller's __construct() method, add per-method -| limits with: -| -| $this->method['METHOD_NAME']['limit'] = [NUM_REQUESTS_PER_HOUR]; -| -| See application/controllers/api/example.php for examples -*/ -$config['rest_enable_limits'] = FALSE; - -/* -|-------------------------------------------------------------------------- -| REST API Limits Table Name -|-------------------------------------------------------------------------- -| -| If not using the default table schema in 'rest_enable_limits', specify the -| table name to match e.g. my_limits -| -*/ -$config['rest_limits_table'] = 'limits'; - -/* -|-------------------------------------------------------------------------- -| REST Ignore HTTP Accept -|-------------------------------------------------------------------------- -| -| Set to TRUE to ignore the HTTP Accept and speed up each request a little. -| Only do this if you are using the $this->rest_format or /format/xml in URLs -| -*/ -$config['rest_ignore_http_accept'] = FALSE; - -/* -|-------------------------------------------------------------------------- -| REST AJAX Only -|-------------------------------------------------------------------------- -| -| Set to TRUE to allow AJAX requests only. Set to FALSE to accept HTTP requests -| -| Note: If set to TRUE and the request is not AJAX, a 505 response with the -| error message 'Only AJAX requests are accepted.' will be returned. -| -| Hint: This is good for production environments -| -*/ -$config['rest_ajax_only'] = FALSE; - -/* -|-------------------------------------------------------------------------- -| REST Language File -|-------------------------------------------------------------------------- -| -| Language file to load from the language directory -| -*/ -$config['rest_language'] = 'english'; +$config['rest_methods_name_format'] = '%2$s%1$s'; diff --git a/application/config/routes.php b/application/config/routes.php index 791b6d03c..36bfdcb1e 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -1,5 +1,6 @@ my_controller/index -| my-controller/my-method -> my_controller/my_method +| Examples: my-controller/index -> my_controller/index +| my-controller/my-method -> my_controller/my_method */ $route['default_controller'] = 'Vilesci'; -$route['404_override'] = ''; $route['translate_uri_dashes'] = FALSE; // Class name conflicts @@ -59,4 +59,4 @@ $route['api/v1/organisation/[F|f]achbereich/(:any)'] = 'api/v1/organisation/fach $route['api/v1/organisation/[G|g]eschaeftsjahr/(:any)'] = 'api/v1/organisation/geschaeftsjahr2/$1'; $route['api/v1/organisation/[O|o]rganisationseinheit/(:any)'] = 'api/v1/organisation/organisationseinheit2/$1'; $route['api/v1/ressource/[B|b]etriebsmittelperson/(:any)'] = 'api/v1/ressource/betriebsmittelperson2/$1'; -$route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1'; \ No newline at end of file +$route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1'; diff --git a/application/controllers/api/v1/CheckUserAuth.php b/application/controllers/api/v1/CheckUserAuth.php index 4d6522fe2..ee751e886 100644 --- a/application/controllers/api/v1/CheckUserAuth.php +++ b/application/controllers/api/v1/CheckUserAuth.php @@ -2,7 +2,7 @@ if (!defined('BASEPATH')) exit('No direct script access allowed'); -class CheckUserAuth extends REST_Controller +class CheckUserAuth extends RESTFul_Controller { /** * Course API constructor. diff --git a/application/controllers/api/v1/Test.php b/application/controllers/api/v1/Test.php index 41feb1a16..c9918f52b 100644 --- a/application/controllers/api/v1/Test.php +++ b/application/controllers/api/v1/Test.php @@ -5,7 +5,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); /** * Testing class for REST calls and authentication */ -class Test extends REST_Controller +class Test extends RESTFul_Controller { public function __construct() { diff --git a/application/controllers/api/v1/person/Benutzer.php b/application/controllers/api/v1/person/Benutzer.php index 23fcdadac..f1ea4f149 100644 --- a/application/controllers/api/v1/person/Benutzer.php +++ b/application/controllers/api/v1/person/Benutzer.php @@ -37,7 +37,7 @@ class Benutzer extends APIv1_Controller if (isset($uid)) { - $result = $this->BenutzerModel->load($uid); + $result = $this->BenutzerModel->load(array('uid' => $uid)); $this->response($result, REST_Controller::HTTP_OK); } diff --git a/application/controllers/system/LogsViewer.php b/application/controllers/system/LogsViewer.php index 55cf38d82..8caf9f3a7 100644 --- a/application/controllers/system/LogsViewer.php +++ b/application/controllers/system/LogsViewer.php @@ -14,7 +14,7 @@ class LogsViewer extends Auth_Controller { parent::__construct( array( - 'index' => 'admin:r' + 'index' => 'system/developer:r' ) ); diff --git a/application/controllers/system/infocenter/InfoCenter.php b/application/controllers/system/infocenter/InfoCenter.php index c803520ed..2f66e0e2d 100644 --- a/application/controllers/system/infocenter/InfoCenter.php +++ b/application/controllers/system/infocenter/InfoCenter.php @@ -24,6 +24,8 @@ class InfoCenter extends Auth_Controller const FILTER_ID = 'filter_id'; const PREV_FILTER_ID = 'prev_filter_id'; + const RELOAD_DATASET = 'reloadDataset'; + const KEEP_TABLESORTER_FILTER = 'keepTsFilter'; private $_uid; // contains the UID of the logged user @@ -100,9 +102,11 @@ class InfoCenter extends Auth_Controller 'reloadNotizen' => 'infocenter:r', 'reloadLogs' => 'infocenter:r', 'outputAkteContent' => 'infocenter:r', - 'getParkedDate' => 'infocenter:r', + 'getPostponeDate' => 'infocenter:r', 'park' => 'infocenter:rw', 'unpark' => 'infocenter:rw', + 'setOnHold' => 'infocenter:rw', + 'removeOnHold' => 'infocenter:rw', 'getStudienjahrEnd' => 'infocenter:r', 'setNavigationMenuArrayJson' => 'infocenter:r' ) @@ -234,7 +238,7 @@ class InfoCenter extends Auth_Controller $redirectLink = '/'.self::INFOCENTER_URI.'?'.self::FHC_CONTROLLER_ID.'='.$this->getControllerId(); // Force reload of Dataset after Unlock - $redirectLink .= '&reloadDataset=true'; + $redirectLink .= '&'.self::RELOAD_DATASET.'=true&'.self::KEEP_TABLESORTER_FILTER.'=true'; $currentFilterId = $this->input->get(self::FILTER_ID); if (isset($currentFilterId)) @@ -711,11 +715,32 @@ class InfoCenter extends Auth_Controller * Gets the date until which a person is parked * @param $person_id */ - public function getParkedDate($person_id) + public function getPostponeDate($person_id) { + $result = array( + 'type' => null, + 'date' => null + ); + $parkedDate = $this->personloglib->getParkedDate($person_id); - $this->outputJsonSuccess(array($parkedDate)); + if (isset($parkedDate)) + { + $result['type'] = 'parked'; + $result['date'] = $parkedDate; + } + else + { + $onholdDate = $this->personloglib->getOnHoldDate($person_id); + + if (isset($onholdDate)) + { + $result['type'] = 'onhold'; + $result['date'] = $onholdDate; + } + } + + $this->outputJsonSuccess($result); } /** @@ -743,6 +768,31 @@ class InfoCenter extends Auth_Controller $this->outputJson($result); } + /** + * Sets a person on hold ("zurückstellen") + */ + public function setOnHold() + { + $person_id = $this->input->post('person_id'); + $date = $this->input->post('onholddate'); + + $result = $this->personloglib->setOnHold($person_id, date_format(date_create($date), 'Y-m-d'), self::TAETIGKEIT, self::APP, null, $this->_uid); + + $this->outputJson($result); + } + + /** + * Removed on hold status of a person + */ + public function removeOnHold() + { + $person_id = $this->input->post('person_id'); + + $result = $this->personloglib->removeOnHold($person_id); + + $this->outputJson($result); + } + /** * Gets the End date of the current Studienjahr */ @@ -894,10 +944,16 @@ class InfoCenter extends Auth_Controller $freigegebenLink = site_url(self::INFOCENTER_URI.'/'.self::FREIGEGEBEN_PAGE); $reihungstestAbsolviertLink = site_url(self::INFOCENTER_URI.'/'.self::REIHUNGSTESTABSOLVIERT_PAGE); $currentFilterId = $this->input->get(self::FILTER_ID); + $reloadDatasetParam = self::RELOAD_DATASET.'=true'; if (isset($currentFilterId)) { - $freigegebenLink .= '?'.self::PREV_FILTER_ID.'='.$currentFilterId; - $reihungstestAbsolviertLink .= '?'.self::PREV_FILTER_ID.'='.$currentFilterId; + $freigegebenLink .= '?'.self::PREV_FILTER_ID.'='.$currentFilterId.'&'.$reloadDatasetParam; + $reihungstestAbsolviertLink .= '?'.self::PREV_FILTER_ID.'='.$currentFilterId.'&'.$reloadDatasetParam; + } + else + { + $freigegebenLink .= '?'.$reloadDatasetParam; + $reihungstestAbsolviertLink .= '?'.$reloadDatasetParam; } $this->navigationlib->setSessionMenu( @@ -950,7 +1006,7 @@ class InfoCenter extends Auth_Controller $origin_page = $this->input->get(self::ORIGIN_PAGE); - $link = site_url(self::INFOCENTER_URI.'/'.self::INDEX_PAGE); + $link = site_url(self::INFOCENTER_URI); if ($origin_page == self::FREIGEGEBEN_PAGE) { $link = site_url(self::INFOCENTER_URI.'/'.self::FREIGEGEBEN_PAGE); @@ -963,7 +1019,7 @@ class InfoCenter extends Auth_Controller $prevFilterId = $this->input->get(self::PREV_FILTER_ID); if (isset($prevFilterId)) { - $link .= '?'.self::FILTER_ID.'='.$prevFilterId; + $link .= '?'.self::FILTER_ID.'='.$prevFilterId.'&'.self::RELOAD_DATASET.'=true&'.self::KEEP_TABLESORTER_FILTER.'=true'; } $this->navigationlib->setSessionMenu( @@ -993,13 +1049,14 @@ class InfoCenter extends Auth_Controller $this->load->library('NavigationLib', array(self::NAVIGATION_PAGE => self::INFOCENTER_URI.'/'.$page)); // Generate the home link with the eventually loaded filter - $homeLink = site_url(self::INFOCENTER_URI.'/'.self::INDEX_PAGE); - $freigegebenLink = site_url(self::INFOCENTER_URI.'/'.self::FREIGEGEBEN_PAGE); - $absolviertLink = site_url(self::INFOCENTER_URI.'/'.self::REIHUNGSTESTABSOLVIERT_PAGE); + $reloadDatasetParam = '?'.self::RELOAD_DATASET.'=true'; + $homeLink = site_url(self::INFOCENTER_URI.'/'.self::INDEX_PAGE.$reloadDatasetParam); + $freigegebenLink = site_url(self::INFOCENTER_URI.'/'.self::FREIGEGEBEN_PAGE.$reloadDatasetParam); + $absolviertLink = site_url(self::INFOCENTER_URI.'/'.self::REIHUNGSTESTABSOLVIERT_PAGE.$reloadDatasetParam); $prevFilterId = $this->input->get(self::PREV_FILTER_ID); if (isset($prevFilterId)) { - $homeLink .= '?'.self::FILTER_ID.'='.$prevFilterId; + $homeLink .= '&'.self::FILTER_ID.'='.$prevFilterId; } $this->navigationlib->setSessionElementMenu( diff --git a/application/core/APIv1_Controller.php b/application/core/APIv1_Controller.php index b0c98c6e5..14b4b626d 100644 --- a/application/core/APIv1_Controller.php +++ b/application/core/APIv1_Controller.php @@ -3,9 +3,9 @@ if (!defined('BASEPATH')) exit('No direct script access allowed'); /** - * REST_Controller takes care about authentication and it loads the AuthLib + * */ -abstract class APIv1_Controller extends REST_Controller +abstract class APIv1_Controller extends RESTFul_Controller { private $_requiredPermissions; @@ -23,13 +23,14 @@ abstract class APIv1_Controller extends REST_Controller /** * This method is automatically called by CodeIgniter after the execution of the constructor is completed - * - Cheks if the AuthLib was loaded, if not it means that the authentication failed + * - Cheks if the Authlib was loaded, if not it means that the authentication failed * - Loads the permsission lib and calls permissionlib->isEntitled * - Checks if the caller is allowed to access to this content with the given permissions * if it is not allowed will set the HTTP header with code 401 * - Calls the parent (REST_Controller) _remap method to performs other checks + * NOTE: this methods override the parent method!!! */ - public function _remap($object_called, $arguments) + public function _remap($object_called, $arguments = []) { if (isset($this->authlib)) // if set then the authentication is ok { diff --git a/application/core/RESTFul_Controller.php b/application/core/RESTFul_Controller.php new file mode 100644 index 000000000..984f193de --- /dev/null +++ b/application/core/RESTFul_Controller.php @@ -0,0 +1,215 @@ +load->helper('hlp_return_object'); + + // Loads helper session to manage the php session + $this->load->helper('hlp_session'); + + // Loads helper with generic utility function + $this->load->helper('hlp_common'); + } + + /** + * Totally overrode parent's _perform_library_auth method to keep file and class name + * for AuthLib and to call AuthLib with the extra parameter + */ + protected function _perform_library_auth($username = '', $password = NULL) + { + if (empty($username)) + { + log_message('error', 'Library Auth: Failure, empty username'); + return FALSE; + } + + $auth_library_class = $this->config->item('auth_library_class'); + $auth_library_function = $this->config->item('auth_library_function'); + + if (empty($auth_library_class)) + { + log_message('debug', 'Library Auth: Failure, empty auth_library_class'); + return FALSE; + } + + if (empty($auth_library_function)) + { + log_message('debug', 'Library Auth: Failure, empty auth_library_function'); + return FALSE; + } + + if (is_callable([$auth_library_class, $auth_library_function]) === FALSE) + { + $this->load->library($auth_library_class, array(false)); + } + + return $this->{strtolower($auth_library_class)}->$auth_library_function($username, $password); + } + + /** + * Totally overrode parent's _remap method to change the naming convention of controllers methods + */ + public function _remap($object_called, $arguments = []) + { + // Should we answer if not over SSL? + if ($this->config->item('force_https') && $this->request->ssl === FALSE) + { + $this->response([ + $this->config->item('rest_status_field_name') => FALSE, + $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unsupported') + ], self::HTTP_FORBIDDEN); + + $this->is_valid_request = false; + } + + // Remove the supported format from the function name e.g. index.json => index + $object_called = preg_replace('/^(.*)\.(?:'.implode('|', array_keys($this->_supported_formats)).')$/', '$1', $object_called); + + // NOTE: START changes + $controller_method = $object_called.'_'.$this->request->method; // Method name fallback + // If the config entry rest_methods_name_format is provided and is not empty then use it to produce the method name + if (!empty($this->config->item('rest_methods_name_format'))) + { + $controller_method = sprintf($this->config->item('rest_methods_name_format'), $object_called, $this->request->method); + } + // END changes + + // Does this method exist? If not, try executing an index method + if (!method_exists($this, $controller_method)) { + $controller_method = "index_" . $this->request->method; + array_unshift($arguments, $object_called); + } + + // Do we want to log this method (if allowed by config)? + $log_method = ! (isset($this->methods[$controller_method]['log']) && $this->methods[$controller_method]['log'] === FALSE); + + // Use keys for this method? + $use_key = ! (isset($this->methods[$controller_method]['key']) && $this->methods[$controller_method]['key'] === FALSE); + + // They provided a key, but it wasn't valid, so get them out of here + if ($this->config->item('rest_enable_keys') && $use_key && $this->_allow === FALSE) + { + if ($this->config->item('rest_enable_logging') && $log_method) + { + $this->_log_request(); + } + + // fix cross site to option request error + if($this->request->method == 'options') { + exit; + } + + $this->response([ + $this->config->item('rest_status_field_name') => FALSE, + $this->config->item('rest_message_field_name') => sprintf($this->lang->line('text_rest_invalid_api_key'), $this->rest->key) + ], self::HTTP_FORBIDDEN); + + $this->is_valid_request = false; + } + + // Check to see if this key has access to the requested controller + if ($this->config->item('rest_enable_keys') && $use_key && empty($this->rest->key) === FALSE && $this->_check_access() === FALSE) + { + if ($this->config->item('rest_enable_logging') && $log_method) + { + $this->_log_request(); + } + + $this->response([ + $this->config->item('rest_status_field_name') => FALSE, + $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_unauthorized') + ], self::HTTP_UNAUTHORIZED); + + $this->is_valid_request = false; + } + + // Sure it exists, but can they do anything with it? + if (! method_exists($this, $controller_method)) + { + $this->response([ + $this->config->item('rest_status_field_name') => FALSE, + $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unknown_method') + ], self::HTTP_METHOD_NOT_ALLOWED); + + $this->is_valid_request = false; + } + + // Doing key related stuff? Can only do it if they have a key right? + if ($this->config->item('rest_enable_keys') && empty($this->rest->key) === FALSE) + { + // Check the limit + if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE) + { + $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')]; + $this->response($response, self::HTTP_UNAUTHORIZED); + + $this->is_valid_request = false; + } + + // If no level is set use 0, they probably aren't using permissions + $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0; + + // If no level is set, or it is lower than/equal to the key's level + $authorized = $level <= $this->rest->level; + // IM TELLIN! + if ($this->config->item('rest_enable_logging') && $log_method) + { + $this->_log_request($authorized); + } + if($authorized === FALSE) + { + // They don't have good enough perms + $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_permissions')]; + $this->response($response, self::HTTP_UNAUTHORIZED); + + $this->is_valid_request = false; + } + } + + //check request limit by ip without login + elseif ($this->config->item('rest_limits_method') == "IP_ADDRESS" && $this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE) + { + $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')]; + $this->response($response, self::HTTP_UNAUTHORIZED); + + $this->is_valid_request = false; + } + + // No key stuff, but record that stuff is happening + elseif ($this->config->item('rest_enable_logging') && $log_method) + { + $this->_log_request($authorized = TRUE); + } + + // Call the controller method and passed arguments + try + { + if ($this->is_valid_request) { + call_user_func_array([$this, $controller_method], $arguments); + } + } + catch (Exception $ex) + { + if ($this->config->item('rest_handle_exceptions') === FALSE) { + throw $ex; + } + + // If the method doesn't exist, then the error will be caught and an error response shown + $_error = &load_class('Exceptions', 'core'); + $_error->show_exception($ex); + } + } +} diff --git a/application/core/REST_Controller.php b/application/core/REST_Controller.php deleted file mode 100644 index 9cd276556..000000000 --- a/application/core/REST_Controller.php +++ /dev/null @@ -1,2166 +0,0 @@ - 'application/json', - 'array' => 'application/json', - 'csv' => 'application/csv', - 'html' => 'text/html', - 'jsonp' => 'application/javascript', - 'php' => 'text/plain', - 'serialized' => 'application/vnd.php.serialized', - 'xml' => 'application/xml' - ]; - - /** - * Information about the current API user - * - * @var object - */ - protected $_apiuser; - - /** - * Enable XSS flag - * Determines whether the XSS filter is always active when - * GET, OPTIONS, HEAD, POST, PUT, DELETE and PATCH data is encountered. - * Set automatically based on config setting - * - * @var bool - */ - protected $_enable_xss = FALSE; - - /** - * HTTP status codes and their respective description - * Note: Only the widely used HTTP status codes are used - * - * @var array - * @link http://www.restapitutorial.com/httpstatuscodes.html - */ - protected $http_status_codes = [ - self::HTTP_OK => 'OK', - self::HTTP_CREATED => 'CREATED', - self::HTTP_NO_CONTENT => 'NO CONTENT', - self::HTTP_NOT_MODIFIED => 'NOT MODIFIED', - self::HTTP_BAD_REQUEST => 'BAD REQUEST', - self::HTTP_UNAUTHORIZED => 'UNAUTHORIZED', - self::HTTP_FORBIDDEN => 'FORBIDDEN', - self::HTTP_NOT_FOUND => 'NOT FOUND', - self::HTTP_METHOD_NOT_ALLOWED => 'METHOD NOT ALLOWED', - self::HTTP_NOT_ACCEPTABLE => 'NOT ACCEPTABLE', - self::HTTP_CONFLICT => 'CONFLICT', - self::HTTP_INTERNAL_SERVER_ERROR => 'INTERNAL SERVER ERROR', - self::HTTP_NOT_IMPLEMENTED => 'NOT IMPLEMENTED' - ]; - - /** - * Extend this function to apply additional checking early on in the process - * - * @access protected - * @return void - */ - protected function early_checks() - { - // Loads helper message to manage returning messages - $this->load->helper('hlp_return_object'); - - // Loads helper session to manage the php session - $this->load->helper('hlp_session'); - - // Loads helper with generic utility function - $this->load->helper('hlp_common'); - } - - /** - * Constructor for the REST API - * - * @access public - * @param string $config Configuration filename minus the file extension - * e.g: my_rest.php is passed as 'my_rest' - * @return void - */ - public function __construct($config = 'rest') - { - parent::__construct(); - - // Disable XML Entity (security vulnerability) - libxml_disable_entity_loader(TRUE); - - // Check to see if PHP is equal to or greater than 5.4.x - if (is_php('5.4') === FALSE) - { - // CodeIgniter 3 is recommended for v5.4 or above - throw new Exception('Using PHP v' . PHP_VERSION . ', though PHP v5.4 or greater is required'); - } - - // Check to see if this is CI 3.x - $ci_version_number = explode('.', CI_VERSION, 2); - if ($ci_version_number[0] < 3) - { - throw new Exception('REST Server requires CodeIgniter 3.x'); - } - - // Set the default value of global xss filtering. Same approach as CodeIgniter 3 - $this->_enable_xss = ($this->config->item('global_xss_filtering') === TRUE); - - // Don't try to parse template variables like {elapsed_time} and {memory_usage} - // when output is displayed for not damaging data accidentally - $this->output->parse_exec_vars = FALSE; - - // Start the timer for how long the request takes - $this->_start_rtime = microtime(TRUE); - - // Load the rest.php configuration file - $this->load->config($config); - - // At present the library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter (no citation) - $this->load->library('format'); - - // Determine supported output formats from configiguration. - $supported_formats = $this->config->item('rest_supported_formats'); - - // Validate the configuration setting output formats - if (empty($supported_formats)) - { - $supported_formats = []; - } - - if (!is_array($supported_formats)) - { - $supported_formats = [$supported_formats]; - } - - // Add silently the default output format if it is missing. - $default_format = $this->_get_default_output_format(); - if (!in_array($default_format, $supported_formats)) - { - $supported_formats[] = $default_format; - } - - // Now update $this->_supported_formats - $this->_supported_formats = array_intersect_key($this->_supported_formats, array_flip($supported_formats)); - - // Get the language - $language = $this->config->item('rest_language'); - if ($language === NULL) - { - $language = 'en-US'; - } - - // Load the language file - $this->lang->load('rest_controller', $language); - - // Initialise the response, request and rest objects - $this->request = new stdClass(); - $this->response = new stdClass(); - $this->rest = new stdClass(); - - // Check to see if the current IP address is blacklisted - if ($this->config->item('rest_ip_blacklist_enabled') === TRUE) - { - $this->_check_blacklist_auth(); - } - - // Determine whether the connection is HTTPS - $this->request->ssl = is_https(); - - // How is this request being made? GET, POST, PATCH, DELETE, INSERT, PUT, HEAD or OPTIONS - $this->request->method = $this->_detect_method(); - - // Create an argument container if it doesn't exist e.g. _get_args - if (isset($this->{'_' . $this->request->method . '_args'}) === FALSE) - { - $this->{'_' . $this->request->method . '_args'} = []; - } - - // Set up the query parameters - $this->_parse_query(); - - // Set up the GET variables - $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); - - // Try to find a format for the request (means we have a request body) - $this->request->format = $this->_detect_input_format(); - - // Not all methods have a body attached with them - $this->request->body = NULL; - - $this->{'_parse_' . $this->request->method}(); - - // Now we know all about our request, let's try and parse the body if it exists - if ($this->request->format && $this->request->body) - { - $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array(); - // Assign payload arguments to proper method container - $this->{'_' . $this->request->method . '_args'} = $this->request->body; - } - - // Merge both for one mega-args variable - $this->_args = array_merge( - $this->_get_args, - $this->_options_args, - $this->_patch_args, - $this->_head_args, - $this->_put_args, - $this->_post_args, - $this->_delete_args, - $this->{'_' . $this->request->method . '_args'} - ); - - // Which format should the data be returned in? - $this->response->format = $this->_detect_output_format(); - - // Which language should the data be returned in? - $this->response->lang = $this->_detect_lang(); - - // Extend this function to apply additional checking early on in the process - $this->early_checks(); - - // Load DB if its enabled - if ($this->config->item('rest_database_group') && ($this->config->item('rest_enable_keys') || $this->config->item('rest_enable_logging'))) - { - $this->rest->db = $this->load->database($this->config->item('rest_database_group'), TRUE); - } - - // Use whatever database is in use (isset returns FALSE) - elseif (property_exists($this, 'db')) - { - $this->rest->db = $this->db; - } - - // Check if there is a specific auth type for the current class/method - // _auth_override_check could exit so we need $this->rest->db initialized before - $this->auth_override = $this->_auth_override_check(); - - // Checking for keys? GET TO WorK! - // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none' - if ($this->config->item('rest_enable_keys') && $this->auth_override !== TRUE) - { - $this->_allow = $this->_detect_api_key(); - } - - // Only allow ajax requests - if ($this->input->is_ajax_request() === FALSE && $this->config->item('rest_ajax_only')) - { - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ajax_only') - ], self::HTTP_NOT_ACCEPTABLE); - } - - // When there is no specific override for the current class/method, use the default auth value set in the config - if ($this->auth_override === FALSE && ($this->config->item('rest_enable_keys') && $this->_allow === TRUE)) - { - $rest_auth = strtolower($this->config->item('rest_auth')); - switch ($rest_auth) - { - case 'basic': - $this->_prepare_basic_auth(); - break; - case 'digest': - $this->_prepare_digest_auth(); - break; - case 'session': - $this->_check_php_session(); - break; - } - if ($this->config->item('rest_ip_whitelist_enabled') === TRUE) - { - $this->_check_whitelist_auth(); - } - } - } - - /** - * Deconstructor - * - * @author Chris Kacerguis - * @access public - * @return void - */ - public function __destruct() - { - // Get the current timestamp - $this->_end_rtime = microtime(TRUE); - - // Log the loading time to the log table - if ($this->config->item('rest_enable_logging') === TRUE) - { - $this->_log_access_time(); - } - } - - /** - * Requests are not made to methods directly, the request will be for - * an "object". This simply maps the object and method to the correct - * Controller method - * - * @access public - * @param string $object_called - * @param array $arguments The arguments passed to the controller method - */ - public function _remap($object_called, $arguments) - { - // Should we answer if not over SSL? - if ($this->config->item('force_https') && $this->request->ssl === FALSE) - { - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unsupported') - ], self::HTTP_FORBIDDEN); - } - - // Remove the supported format from the function name e.g. index.json => index - $object_called = preg_replace('/^(.*)\.(?:' . implode('|', array_keys($this->_supported_formats)) . ')$/', '$1', $object_called); - - //$controller_method = $object_called . '_' . $this->request->method; - // CamelCase compliant - $controller_method = $this->request->method.ucfirst($object_called); - - // Do we want to log this method (if allowed by config)? - $log_method = !(isset($this->methods[$controller_method]['log']) && $this->methods[$controller_method]['log'] === FALSE); - - // Use keys for this method? - $use_key = !(isset($this->methods[$controller_method]['key']) && $this->methods[$controller_method]['key'] === FALSE); - - // They provided a key, but it wasn't valid, so get them out of here - if ($this->config->item('rest_enable_keys') && $use_key && $this->_allow === FALSE) - { - if ($this->config->item('rest_enable_logging') && $log_method) - { - $this->_log_request(); - } - - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => sprintf($this->lang->line('text_rest_invalid_api_key'), $this->rest->key) - ], self::HTTP_FORBIDDEN); - } - - // Check to see if this key has access to the requested controller - if ($this->config->item('rest_enable_keys') && $use_key && empty($this->rest->key) === FALSE && $this->_check_access() === FALSE) - { - if ($this->config->item('rest_enable_logging') && $log_method) - { - $this->_log_request(); - } - - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_unauthorized') - ], self::HTTP_UNAUTHORIZED); - } - - // Sure it exists, but can they do anything with it? - if (method_exists($this, $controller_method) === FALSE) - { - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unknown_method') - ], self::HTTP_NOT_FOUND); - } - - // Doing key related stuff? Can only do it if they have a key right? - if ($this->config->item('rest_enable_keys') && empty($this->rest->key) === FALSE) - { - // Check the limit - if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE) - { - $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')]; - $this->response($response, self::HTTP_UNAUTHORIZED); - } - - // If no level is set use 0, they probably aren't using permissions - $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0; - - // If no level is set, or it is lower than/equal to the key's level - $authorized = $level <= $this->rest->level; - - // IM TELLIN! - if ($this->config->item('rest_enable_logging') && $log_method) - { - $this->_log_request($authorized); - } - - // They don't have good enough perms - $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_permissions')]; - $authorized || $this->response($response, self::HTTP_UNAUTHORIZED); - } - - // No key stuff, but record that stuff is happening - elseif ($this->config->item('rest_enable_logging') && $log_method) - { - $this->_log_request($authorized = TRUE); - } - - // Call the controller method and passed arguments - try - { - call_user_func_array([$this, $controller_method], $arguments); - } - catch (Exception $ex) - { - // If the method doesn't exist, then the error will be caught and an error response shown - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => [ - 'classname' => get_class($ex), - 'message' => $ex->getMessage() - ] - ], self::HTTP_INTERNAL_SERVER_ERROR); - } - } - - /** - * Takes mixed data and optionally a status code, then creates the response - * - * @access public - * @param array|NULL $data Data to output to the user - * @param int|NULL $http_code HTTP status code - * @param bool $continue TRUE to flush the response to the client and continue - * running the script; otherwise, exit - */ - public function response($data = NULL, $http_code = NULL, $continue = FALSE) - { - // If the HTTP status is not NULL, then cast as an integer - if ($http_code !== NULL) - { - // So as to be safe later on in the process - $http_code = (int) $http_code; - } - - // Set the output as NULL by default - $output = NULL; - // If data is NULL and no HTTP status code provided, then display, error and exit - if ($data === NULL && $http_code === NULL) - { - $http_code = self::HTTP_NOT_FOUND; - } - - // If data is not NULL and a HTTP status code provided, then continue - elseif ($data !== NULL) - { - // If the format method exists, call and return the output in that format - if (method_exists($this->format, 'to_' . $this->response->format)) - { - // Set the format header - $this->output->set_content_type($this->_supported_formats[$this->response->format], strtolower($this->config->item('charset'))); - $output = $this->format->factory($data)->{'to_' . $this->response->format}(); - - // An array must be parsed as a string, so as not to cause an array to string error - // Json is the most appropriate form for such a datatype - if ($this->response->format === 'array') - { - $output = $this->format->factory($output)->{'to_json'}(); - } - } - else - { - // If an array or object, then parse as a json, so as to be a 'string' - if (is_array($data) || is_object($data)) - { - $data = $this->format->factory($data)->{'to_json'}(); - } - // Format is not supported, so output the raw data as a string - $output = $data; - } - } - - // If not greater than zero, then set the HTTP status code as 200 by default - // Though perhaps 500 should be set instead, for the developer not passing a - // correct HTTP status code - $http_code > 0 || $http_code = self::HTTP_OK; - - $this->output->set_status_header($http_code); - - // JC: Log response code only if rest logging enabled - if ($this->config->item('rest_enable_logging') === TRUE) - { - $this->_log_response_code($http_code); - } - - // Output the data - $this->output->set_output($output); - - if ($continue === FALSE) - { - // Display the data and exit execution - $this->output->_display(); - exit; - } - - // Otherwise dump the output automatically - } - - /** - * Takes mixed data and optionally a status code, then creates the response - * within the buffers of the Output class. The response is sent to the client - * lately by the framework, after the current controller's method termination. - * All the hooks after the controller's method termination are executable - * - * @access public - * @param array|NULL $data Data to output to the user - * @param int|NULL $http_code HTTP status code - */ - public function set_response($data = NULL, $http_code = NULL) - { - $this->response($data, $http_code, TRUE); - } - - /** - * Get the input format e.g. json or xml - * - * @access protected - * @return string|NULL Supported input format; otherwise, NULL - */ - protected function _detect_input_format() - { - // Get the CONTENT-TYPE value from the SERVER variable - $content_type = $this->input->server('CONTENT_TYPE'); - - if (empty($content_type) === FALSE) - { - // Check all formats against the HTTP_ACCEPT header - foreach ($this->_supported_formats as $key => $value) - { - // $key = format e.g. csv - // $value = mime type e.g. application/csv - - // If a semi-colon exists in the string, then explode by ; and get the value of where - // the current array pointer resides. This will generally be the first element of the array - $content_type = (strpos($content_type, ';') !== FALSE ? current(explode(';', $content_type)) : $content_type); - - // If both the mime types match, then return the format - if ($content_type === $value) - { - return $key; - } - } - } - - return NULL; - } - - /** - * Gets the default format from the configuration. Fallbacks to 'json'. - * if the corresponding configuration option $config['rest_default_format'] - * is missing or is empty. - * - * @access protected - * @return string The default supported input format - */ - protected function _get_default_output_format() - { - $default_format = (string) $this->config->item('rest_default_format'); - return $default_format === '' ? 'json' : $default_format; - } - - /** - * Detect which format should be used to output the data - * - * @access protected - * @return mixed|NULL|string Output format - */ - protected function _detect_output_format() - { - // Concatenate formats to a regex pattern e.g. \.(csv|json|xml) - $pattern = '/\.(' . implode('|', array_keys($this->_supported_formats)) . ')($|\/)/'; - $matches = []; - - // Check if a file extension is used e.g. http://example.com/api/index.json?param1=param2 - if (preg_match($pattern, $this->uri->uri_string(), $matches)) - { - return $matches[1]; - } - - // Get the format parameter named as 'format' - if (isset($this->_get_args['format'])) - { - $format = strtolower($this->_get_args['format']); - - if (isset($this->_supported_formats[$format]) === TRUE) - { - return $format; - } - } - - // Get the HTTP_ACCEPT server variable - $http_accept = $this->input->server('HTTP_ACCEPT'); - - // Otherwise, check the HTTP_ACCEPT server variable - if ($this->config->item('rest_ignore_http_accept') === FALSE && $http_accept !== NULL) - { - // Check all formats against the HTTP_ACCEPT header - foreach (array_keys($this->_supported_formats) as $format) - { - // Has this format been requested? - if (strpos($http_accept, $format) !== FALSE) - { - if ($format !== 'html' && $format !== 'xml') - { - // If not HTML or XML assume it's correct - return $format; - } - elseif ($format === 'html' && strpos($http_accept, 'xml') === FALSE) - { - // HTML or XML have shown up as a match - // If it is truly HTML, it wont want any XML - return $format; - } - else if ($format === 'xml' && strpos($http_accept, 'html') === FALSE) - { - // If it is truly XML, it wont want any HTML - return $format; - } - } - } - } - - // Check if the controller has a default format - if (empty($this->rest_format) === FALSE) - { - return $this->rest_format; - } - - // Obtain the default format from the configuration - return $this->_get_default_output_format(); - } - - /** - * Get the HTTP request string e.g. get or post - * - * @access protected - * @return string|NULL Supported request method as a lowercase string; otherwise, NULL if not supported - */ - protected function _detect_method() - { - // Declare a variable to store the method - $method = NULL; - - // Determine whether the 'enable_emulate_request' setting is enabled - if ($this->config->item('enable_emulate_request') === TRUE) - { - $method = $this->input->post('_method'); - if ($method === NULL) - { - $method = $this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE'); - } - - $method = strtolower($method); - } - - if (empty($method)) - { - // Get the request method as a lowercase string - $method = $this->input->method(); - } - - return in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method) ? $method : 'get'; - } - - /** - * See if the user has provided an API key - * - * @access protected - * @return bool - */ - protected function _detect_api_key() - { - // Get the api key name variable set in the rest config file - $api_key_variable = $this->config->item('rest_key_name'); - - // Work out the name of the SERVER entry based on config - $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', $api_key_variable)); - - $this->rest->key = NULL; - $this->rest->level = NULL; - $this->rest->user_id = NULL; - $this->rest->ignore_limits = FALSE; - - // Find the key from server or arguments - if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) - { - if (!($row = $this->rest->db->where($this->config->item('rest_key_column'), $key)->get($this->config->item('rest_keys_table'))->row())) - { - return FALSE; - } - - $this->rest->key = $row->{$this->config->item('rest_key_column')}; - - isset($row->user_id) && $this->rest->user_id = $row->user_id; - isset($row->level) && $this->rest->level = $row->level; - isset($row->ignore_limits) && $this->rest->ignore_limits = $row->ignore_limits; - - $this->_apiuser = $row; - - /* - * If "is private key" is enabled, compare the ip address with the list - * of valid ip addresses stored in the database - */ - if (empty($row->is_private_key) === FALSE) - { - // Check for a list of valid ip addresses - if (isset($row->ip_addresses)) - { - // multiple ip addresses must be separated using a comma, explode and loop - $list_ip_addresses = explode(',', $row->ip_addresses); - $found_address = FALSE; - - foreach ($list_ip_addresses as $ip_address) - { - if ($this->input->ip_address() === trim($ip_address)) - { - // there is a match, set the the value to TRUE and break out of the loop - $found_address = TRUE; - break; - } - } - - return $found_address; - } - else - { - // There should be at least one IP address for this private key - return FALSE; - } - } - - return TRUE; - } - - // No key has been sent - return FALSE; - } - - /** - * Preferred return language - * - * @access protected - * @return string|NULL The language code - */ - protected function _detect_lang() - { - $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE'); - if ($lang === NULL) - { - return NULL; - } - - // It appears more than one language has been sent using a comma delimiter - if (strpos($lang, ',') !== FALSE) - { - $langs = explode(',', $lang); - - $return_langs = []; - foreach ($langs as $lang) - { - // Remove weight and trim leading and trailing whitespace - list($lang) = explode(';', $lang); - $return_langs[] = trim($lang); - } - - return $return_langs; - } - - // Otherwise simply return as a string - return $lang; - } - - /** - * Add the request to the log table - * - * @access protected - * @param bool $authorized TRUE the user is authorized; otherwise, FALSE - * @return bool TRUE the data was inserted; otherwise, FALSE - */ - protected function _log_request($authorized = FALSE) - { - // Insert the request into the log table - $is_inserted = $this->rest->db - ->insert( - $this->config->item('rest_logs_table'), [ - 'uri' => $this->uri->uri_string(), - 'method' => $this->request->method, - 'params' => $this->_args ? ($this->config->item('rest_logs_json_params') === TRUE ? json_encode($this->_args) : serialize($this->_args)) : NULL, - 'api_key' => isset($this->rest->key) ? $this->rest->key : '', - 'ip_address' => $this->input->ip_address(), - 'time' => time(), - 'authorized' => $authorized - ]); - - // Get the last insert id to update at a later stage of the request - $this->_insert_id = $this->rest->db->insert_id(); - - return $is_inserted; - } - - /** - * Check if the requests to a controller method exceed a limit - * - * @access protected - * @param string $controller_method The method being called - * @return bool TRUE the call limit is below the threshold; otherwise, FALSE - */ - protected function _check_limit($controller_method) - { - // They are special, or it might not even have a limit - if (empty($this->rest->ignore_limits) === FALSE) - { - // Everything is fine - return TRUE; - } - - switch ($this->config->item('rest_limits_method')) - { - case 'API_KEY': - $limited_uri = 'api-key:' . (isset($this->rest->key) ? $this->rest->key : ''); - $limited_method_name = isset($this->rest->key) ? $this->rest->key : ''; - break; - - case 'METHOD_NAME': - $limited_uri = 'method-name:' . $controller_method; - $limited_method_name = $controller_method; - break; - - case 'ROUTED_URL': - default: - $limited_uri = $this->uri->ruri_string(); - if (strpos(strrev($limited_uri), strrev($this->response->format)) === 0) - { - $limited_uri = substr($limited_uri,0, -strlen($this->response->format) - 1); - } - $limited_uri = 'uri:' . $limited_uri . ':' . $this->request->method; // It's good to differentiate GET from PUT - $limited_method_name = $controller_method; - break; - } - - if (isset($this->methods[$limited_method_name]['limit']) === FALSE ) - { - // Everything is fine - return TRUE; - } - - // How many times can you get to this method in a defined time_limit (default: 1 hour)? - $limit = $this->methods[$limited_method_name]['limit']; - - $time_limit = (isset($this->methods[$limited_method_name]['time']) ? $this->methods[$limited_method_name]['time'] : 3600); // 3600 = 60 * 60 - - // Get data about a keys' usage and limit to one row - $result = $this->rest->db - ->where('uri', $limited_uri) - ->where('api_key', $this->rest->key) - ->get($this->config->item('rest_limits_table')) - ->row(); - - // No calls have been made for this key - if ($result === NULL) - { - // Create a new row for the following key - $this->rest->db->insert($this->config->item('rest_limits_table'), [ - 'uri' => $limited_uri, - 'api_key' => isset($this->rest->key) ? $this->rest->key : '', - 'count' => 1, - 'hour_started' => time() - ]); - } - - // Been a time limit (or by default an hour) since they called - elseif ($result->hour_started < (time() - $time_limit)) - { - // Reset the started period and count - $this->rest->db - ->where('uri', $limited_uri) - ->where('api_key', isset($this->rest->key) ? $this->rest->key : '') - ->set('hour_started', time()) - ->set('count', 1) - ->update($this->config->item('rest_limits_table')); - } - - // They have called within the hour, so lets update - else - { - // The limit has been exceeded - if ($result->count >= $limit) - { - return FALSE; - } - - // Increase the count by one - $this->rest->db - ->where('uri', $limited_uri) - ->where('api_key', $this->rest->key) - ->set('count', 'count + 1', FALSE) - ->update($this->config->item('rest_limits_table')); - } - - return TRUE; - } - - /** - * Check if there is a specific auth type set for the current class/method/HTTP-method being called - * - * @access protected - * @return bool - */ - protected function _auth_override_check() - { - // Assign the class/method auth type override array from the config - $auth_override_class_method = $this->config->item('auth_override_class_method'); - - // Check to see if the override array is even populated - if (!empty($auth_override_class_method)) - { - // check for wildcard flag for rules for classes - if (!empty($auth_override_class_method[$this->router->class]['*'])) // Check for class overrides - { - // None auth override found, prepare nothing but send back a TRUE override flag - if ($auth_override_class_method[$this->router->class]['*'] === 'none') - { - return TRUE; - } - - // Basic auth override found, prepare basic - if ($auth_override_class_method[$this->router->class]['*'] === 'basic') - { - $this->_prepare_basic_auth(); - - return TRUE; - } - - // Digest auth override found, prepare digest - if ($auth_override_class_method[$this->router->class]['*'] === 'digest') - { - $this->_prepare_digest_auth(); - - return TRUE; - } - - // Session auth override found, check session - if ($auth_override_class_method[$this->router->class]['*'] === 'session') - { - $this->_check_php_session(); - - return TRUE; - } - - // Whitelist auth override found, check client's ip against config whitelist - if ($auth_override_class_method[$this->router->class]['*'] === 'whitelist') - { - $this->_check_whitelist_auth(); - - return TRUE; - } - } - - // Check to see if there's an override value set for the current class/method being called - if (!empty($auth_override_class_method[$this->router->class][$this->router->method])) - { - // None auth override found, prepare nothing but send back a TRUE override flag - if ($auth_override_class_method[$this->router->class][$this->router->method] === 'none') - { - return TRUE; - } - - // Basic auth override found, prepare basic - if ($auth_override_class_method[$this->router->class][$this->router->method] === 'basic') - { - $this->_prepare_basic_auth(); - - return TRUE; - } - - // Digest auth override found, prepare digest - if ($auth_override_class_method[$this->router->class][$this->router->method] === 'digest') - { - $this->_prepare_digest_auth(); - - return TRUE; - } - - // Session auth override found, check session - if ($auth_override_class_method[$this->router->class][$this->router->method] === 'session') - { - $this->_check_php_session(); - - return TRUE; - } - - // Whitelist auth override found, check client's ip against config whitelist - if ($auth_override_class_method[$this->router->class][$this->router->method] === 'whitelist') - { - $this->_check_whitelist_auth(); - - return TRUE; - } - } - } - - // Assign the class/method/HTTP-method auth type override array from the config - $auth_override_class_method_http = $this->config->item('auth_override_class_method_http'); - - // Check to see if the override array is even populated - if (!empty($auth_override_class_method_http)) - { - // check for wildcard flag for rules for classes - if (!empty($auth_override_class_method_http[$this->router->class]['*'][$this->request->method])) - { - // None auth override found, prepare nothing but send back a TRUE override flag - if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'none') - { - return TRUE; - } - - // Basic auth override found, prepare basic - if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'basic') - { - $this->_prepare_basic_auth(); - - return TRUE; - } - - // Digest auth override found, prepare digest - if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'digest') - { - $this->_prepare_digest_auth(); - - return TRUE; - } - - // Session auth override found, check session - if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'session') - { - $this->_check_php_session(); - - return TRUE; - } - - // Whitelist auth override found, check client's ip against config whitelist - if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'whitelist') - { - $this->_check_whitelist_auth(); - - return TRUE; - } - } - - // Check to see if there's an override value set for the current class/method/HTTP-method being called - if (!empty($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method])) - { - // None auth override found, prepare nothing but send back a TRUE override flag - if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'none') - { - return TRUE; - } - - // Basic auth override found, prepare basic - if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'basic') - { - $this->_prepare_basic_auth(); - - return TRUE; - } - - // Digest auth override found, prepare digest - if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'digest') - { - $this->_prepare_digest_auth(); - - return TRUE; - } - - // Session auth override found, check session - if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'session') - { - $this->_check_php_session(); - - return TRUE; - } - - // Whitelist auth override found, check client's ip against config whitelist - if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'whitelist') - { - $this->_check_whitelist_auth(); - - return TRUE; - } - } - } - return FALSE; - } - - /** - * Parse the GET request arguments - * - * @access protected - * @return void - */ - protected function _parse_get() - { - // Merge both the URI segments and query parameters - $this->_get_args = array_merge($this->_get_args, $this->_query_args); - } - - /** - * Parse the POST request arguments - * - * @access protected - * @return void - */ - protected function _parse_post() - { - $this->_post_args = $_POST; - - if ($this->request->format) - { - $this->request->body = $this->input->raw_input_stream; - } - } - - /** - * Parse the PUT request arguments - * - * @access protected - * @return void - */ - protected function _parse_put() - { - if ($this->request->format) - { - $this->request->body = $this->input->raw_input_stream; - } - else if ($this->input->method() === 'put') - { - // If no filetype is provided, then there are probably just arguments - $this->_put_args = $this->input->input_stream(); - } - } - - /** - * Parse the HEAD request arguments - * - * @access protected - * @return void - */ - protected function _parse_head() - { - // Parse the HEAD variables - parse_str(parse_url($this->input->server('REQUEST_URI'), PHP_URL_QUERY), $head); - - // Merge both the URI segments and HEAD params - $this->_head_args = array_merge($this->_head_args, $head); - } - - /** - * Parse the OPTIONS request arguments - * - * @access protected - * @return void - */ - protected function _parse_options() - { - // Parse the OPTIONS variables - parse_str(parse_url($this->input->server('REQUEST_URI'), PHP_URL_QUERY), $options); - - // Merge both the URI segments and OPTIONS params - $this->_options_args = array_merge($this->_options_args, $options); - } - - /** - * Parse the PATCH request arguments - * - * @access protected - * @return void - */ - protected function _parse_patch() - { - // It might be a HTTP body - if ($this->request->format) - { - $this->request->body = $this->input->raw_input_stream; - } - else if ($this->input->method() === 'patch') - { - // If no filetype is provided, then there are probably just arguments - $this->_patch_args = $this->input->input_stream(); - } - } - - /** - * Parse the DELETE request arguments - * - * @access protected - * @return void - */ - protected function _parse_delete() - { - // These should exist if a DELETE request - if ($this->input->method() === 'delete') - { - $this->_delete_args = $this->input->input_stream(); - } - } - - /** - * Parse the query parameters - * - * @access protected - * @return void - */ - protected function _parse_query() - { - $this->_query_args = $this->input->get(); - } - - // INPUT FUNCTION -------------------------------------------------------------- - - /** - * Retrieve a value from a GET request - * - * @access public - * @param NULL $key Key to retrieve from the GET request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the GET request; otherwise, NULL - */ - public function get($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_get_args; - } - - return isset($this->_get_args[$key]) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a OPTIONS request - * - * @access public - * @param NULL $key Key to retrieve from the OPTIONS request. - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the OPTIONS request; otherwise, NULL - */ - public function options($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_options_args; - } - - return isset($this->_options_args[$key]) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a HEAD request - * - * @access public - * @param NULL $key Key to retrieve from the HEAD request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the HEAD request; otherwise, NULL - */ - public function head($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_head_args; - } - - return isset($this->_head_args[$key]) ? $this->_xss_clean($this->_head_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a POST request - * - * @access public - * @param NULL $key Key to retrieve from the POST request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the POST request; otherwise, NULL - */ - public function post($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_post_args; - } - - return isset($this->_post_args[$key]) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a PUT request - * - * @access public - * @param NULL $key Key to retrieve from the PUT request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the PUT request; otherwise, NULL - */ - public function put($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_put_args; - } - - return isset($this->_put_args[$key]) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a DELETE request - * - * @access public - * @param NULL $key Key to retrieve from the DELETE request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the DELETE request; otherwise, NULL - */ - public function delete($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_delete_args; - } - - return isset($this->_delete_args[$key]) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a PATCH request - * - * @access public - * @param NULL $key Key to retrieve from the PATCH request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the PATCH request; otherwise, NULL - */ - public function patch($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_patch_args; - } - - return isset($this->_patch_args[$key]) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from the query parameters - * - * @access public - * @param NULL $key Key to retrieve from the query parameters - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the query parameters; otherwise, NULL - */ - public function query($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_query_args; - } - - return isset($this->_query_args[$key]) ? $this->_xss_clean($this->_query_args[$key], $xss_clean) : NULL; - } - - /** - * Sanitizes data so that Cross Site Scripting Hacks can be - * prevented - * - * @access protected - * @param string $value Input data - * @param bool $xss_clean Whether to apply XSS filtering - * @return string - */ - protected function _xss_clean($value, $xss_clean) - { - is_bool($xss_clean) || $xss_clean = $this->_enable_xss; - - return $xss_clean === TRUE ? $this->security->xss_clean($value) : $value; - } - - /** - * Retrieve the validation errors - * - * @access public - * @return array - */ - public function validation_errors() - { - $string = strip_tags($this->form_validation->error_string()); - - return explode(PHP_EOL, trim($string, PHP_EOL)); - } - - // SECURITY FUNCTIONS --------------------------------------------------------- - - /** - * Perform LDAP Authentication - * - * @access protected - * @param string $username The username to validate - * @param string $password The password to validate - * @return bool - */ - protected function _perform_ldap_auth($username = '', $password = NULL) - { - if (empty($username)) - { - log_message('debug', 'LDAP Auth: failure, empty username'); - return FALSE; - } - - log_message('debug', 'LDAP Auth: Loading configuration'); - - $this->config->load('ldap.php', TRUE); - - $ldap = [ - 'timeout' => $this->config->item('timeout', 'ldap'), - 'host' => $this->config->item('server', 'ldap'), - 'port' => $this->config->item('port', 'ldap'), - 'rdn' => $this->config->item('binduser', 'ldap'), - 'pass' => $this->config->item('bindpw', 'ldap'), - 'basedn' => $this->config->item('basedn', 'ldap'), - ]; - - log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]')); - - // Connect to the ldap server - $ldapconn = ldap_connect($ldap['host'], $ldap['port']); - if ($ldapconn) - { - log_message('debug', 'Setting timeout to ' . $ldap['timeout'] . ' seconds'); - - ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldap['timeout']); - - log_message('debug', 'LDAP Auth: Binding to ' . $ldap['host'] . ' with dn ' . $ldap['rdn']); - - // Binding to the ldap server - $ldapbind = ldap_bind($ldapconn, $ldap['rdn'], $ldap['pass']); - - // Verify the binding - if ($ldapbind === FALSE) - { - log_message('error', 'LDAP Auth: bind was unsuccessful'); - return FALSE; - } - - log_message('debug', 'LDAP Auth: bind successful'); - } - - // Search for user - if (($res_id = ldap_search($ldapconn, $ldap['basedn'], "uid=$username")) === FALSE) - { - log_message('error', 'LDAP Auth: User ' . $username . ' not found in search'); - return FALSE; - } - - if (ldap_count_entries($ldapconn, $res_id) !== 1) - { - log_message('error', 'LDAP Auth: Failure, username ' . $username . 'found more than once'); - return FALSE; - } - - if (($entry_id = ldap_first_entry($ldapconn, $res_id)) === FALSE) - { - log_message('error', 'LDAP Auth: Failure, entry of search result could not be fetched'); - return FALSE; - } - - if (($user_dn = ldap_get_dn($ldapconn, $entry_id)) === FALSE) - { - log_message('error', 'LDAP Auth: Failure, user-dn could not be fetched'); - return FALSE; - } - - // User found, could not authenticate as user - if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) === FALSE) - { - log_message('error', 'LDAP Auth: Failure, username/password did not match: ' . $user_dn); - return FALSE; - } - - log_message('debug', 'LDAP Auth: Success ' . $user_dn . ' authenticated successfully'); - - $this->_user_ldap_dn = $user_dn; - - ldap_close($ldapconn); - - return TRUE; - } - - /** - * Perform Library Authentication - Override this function to change the way the library is called - * - * @access protected - * @param string $username The username to validate - * @param string $password The password to validate - * @return bool - */ - protected function _perform_library_auth($username = '', $password = NULL) - { - if (empty($username)) - { - log_message('error', 'Library Auth: Failure, empty username'); - return FALSE; - } - - $auth_library_class = $this->config->item('auth_library_class'); - $auth_library_function = $this->config->item('auth_library_function'); - - if (empty($auth_library_class)) - { - log_message('debug', 'Library Auth: Failure, empty auth_library_class'); - return FALSE; - } - - if (empty($auth_library_function)) - { - log_message('debug', 'Library Auth: Failure, empty auth_library_function'); - return FALSE; - } - - if (is_callable([$auth_library_class, $auth_library_function]) === FALSE) - { - $this->load->library($auth_library_class, array(false)); - } - - return $this->{strtolower($auth_library_class)}->$auth_library_function($username, $password); - } - - /** - * Check if the user is logged in - * - * @access protected - * @param string $username The user's name - * @param bool|string $password The user's password - * @return bool - */ - protected function _check_login($username = NULL, $password = FALSE) - { - if (empty($username)) - { - return FALSE; - } - - $auth_source = strtolower($this->config->item('auth_source')); - $rest_auth = strtolower($this->config->item('rest_auth')); - $valid_logins = $this->config->item('rest_valid_logins'); - - if (!$this->config->item('auth_source') && $rest_auth === 'digest') - { - // For digest we do not have a password passed as argument - return md5($username . ':' . $this->config->item('rest_realm') . ':' . (isset($valid_logins[$username]) ? $valid_logins[$username] : '')); - } - - if ($password === FALSE) - { - return FALSE; - } - - if ($auth_source === 'ldap') - { - log_message('debug', "Performing LDAP authentication for $username"); - - return $this->_perform_ldap_auth($username, $password); - } - - if ($auth_source === 'library') - { - log_message('debug', "Performing Library authentication for $username"); - - return $this->_perform_library_auth($username, $password); - } - - if (array_key_exists($username, $valid_logins) === FALSE) - { - return FALSE; - } - - if ($valid_logins[$username] !== $password) - { - return FALSE; - } - - return TRUE; - } - - /** - * Check to see if the user is logged in with a PHP session key - * - * @access protected - * @return void - */ - protected function _check_php_session() - { - // Get the auth_source config item - $key = $this->config->item('auth_source'); - - // If falsy, then the user isn't logged in - if (!$this->session->userdata($key)) - { - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unauthorized') - ], self::HTTP_UNAUTHORIZED); - } - } - - /** - * Prepares for basic authentication - * - * @access protected - * @return void - */ - protected function _prepare_basic_auth() - { - // If whitelist is enabled it has the first chance to kick them out - if ($this->config->item('rest_ip_whitelist_enabled')) - { - $this->_check_whitelist_auth(); - } - - // Returns NULL if the SERVER variables PHP_AUTH_USER and HTTP_AUTHENTICATION don't exist - $username = $this->input->server('PHP_AUTH_USER'); - $http_auth = $this->input->server('HTTP_AUTHENTICATION'); - - $password = NULL; - if ($username !== NULL) - { - $password = $this->input->server('PHP_AUTH_PW'); - } - elseif ($http_auth !== NULL) - { - // If the authentication header is set as basic, then extract the username and password from - // HTTP_AUTHORIZATION e.g. my_username:my_password. This is passed in the .htaccess file - if (strpos(strtolower($http_auth), 'basic') === 0) - { - // Search online for HTTP_AUTHORIZATION workaround to explain what this is doing - list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6))); - } - } - - // Check if the user is logged into the system - if ($this->_check_login($username, $password) === FALSE) - { - $this->_force_login(); - } - } - - /** - * Prepares for digest authentication - * - * @access protected - * @return void - */ - protected function _prepare_digest_auth() - { - // If whitelist is enabled it has the first chance to kick them out - if ($this->config->item('rest_ip_whitelist_enabled')) - { - $this->_check_whitelist_auth(); - } - - // We need to test which server authentication variable to use, - // because the PHP ISAPI module in IIS acts different from CGI - $digest_string = $this->input->server('PHP_AUTH_DIGEST'); - if ($digest_string === NULL) - { - $digest_string = $this->input->server('HTTP_AUTHORIZATION'); - } - - $unique_id = uniqid(); - - // The $_SESSION['error_prompted'] variable is used to ask the password - // again if none given or if the user enters wrong auth information - if (empty($digest_string)) - { - $this->_force_login($unique_id); - } - - // We need to retrieve authentication data from the $digest_string variable - $matches = []; - preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); - $digest = (empty($matches[1]) || empty($matches[2])) ? [] : array_combine($matches[1], $matches[2]); - - // For digest authentication the library function should return - // already stored password for that username, even if it is hashed - $username = $this->_check_login($digest['username'], TRUE); - // If there no password - if (array_key_exists('username', $digest) === FALSE || $username === FALSE || $username === NULL) - { - $this->_force_login($unique_id); - } - // If the password was found for this username, generete the string md5('USERNAME:REALM:PASSWORD') - else - { - $username = md5($digest['username'].":".$this->config->item('rest_realm').":".$username); - } - - $md5 = md5(strtoupper($this->request->method) . ':' . $digest['uri']); - $valid_response = md5($username . ':' . $digest['nonce'] . ':' . $digest['nc'] . ':' . $digest['cnonce'] . ':' . $digest['qop'] . ':' . $md5); - - // Check if the string don't compare (case-insensitive) - if (strcasecmp($digest['response'], $valid_response) !== 0) - { - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_invalid_credentials') - ], self::HTTP_UNAUTHORIZED); - } - } - - /** - * Checks if the client's ip is in the 'rest_ip_blacklist' config and generates a 401 response - * - * @access protected - * @return void - */ - protected function _check_blacklist_auth() - { - // Match an ip address in a blacklist e.g. 127.0.0.0, 0.0.0.0 - $pattern = sprintf('/(?:,\s*|^)\Q%s\E(?=,\s*|$)/m', $this->input->ip_address()); - - // Returns 1, 0 or FALSE (on error only). Therefore implicitly convert 1 to TRUE - if (preg_match($pattern, $this->config->item('rest_ip_blacklist'))) - { - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_denied') - ], self::HTTP_UNAUTHORIZED); - } - } - - /** - * Check if the client's ip is in the 'rest_ip_whitelist' config and generates a 401 response - * - * @access protected - * @return void - */ - protected function _check_whitelist_auth() - { - $whitelist = explode(',', $this->config->item('rest_ip_whitelist')); - - array_push($whitelist, '127.0.0.1', '0.0.0.0'); - - foreach ($whitelist as &$ip) - { - // As $ip is a reference, trim leading and trailing whitespace, then store the new value - // using the reference - $ip = trim($ip); - } - - if (in_array($this->input->ip_address(), $whitelist) === FALSE) - { - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_unauthorized') - ], self::HTTP_UNAUTHORIZED); - } - } - - /** - * Force logging in by setting the WWW-Authenticate header - * - * @access protected - * @param string $nonce A server-specified data string which should be uniquely generated - * each time - * @return void - */ - protected function _force_login($nonce = '') - { - $rest_auth = $this->config->item('rest_auth'); - $rest_realm = $this->config->item('rest_realm'); - if (strtolower($rest_auth) === 'basic') - { - // See http://tools.ietf.org/html/rfc2617#page-5 - header('WWW-Authenticate: Basic realm="' . $rest_realm . '"'); - } - elseif (strtolower($rest_auth) === 'digest') - { - // See http://tools.ietf.org/html/rfc2617#page-18 - header( - 'WWW-Authenticate: Digest realm="' . $rest_realm - . '", qop="auth", nonce="' . $nonce - . '", opaque="' . md5($rest_realm) . '"'); - } - - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unauthorized') - ], self::HTTP_UNAUTHORIZED); - } - - /** - * Updates the log table with the total access time - * - * @access protected - * @author Chris Kacerguis - * @return bool TRUE log table updated; otherwise, FALSE - */ - protected function _log_access_time() - { - $payload['rtime'] = $this->_end_rtime - $this->_start_rtime; - - return $this->rest->db->update( - $this->config->item('rest_logs_table'), $payload, [ - 'id' => $this->_insert_id - ]); - } - - /** - * Updates the log table with HTTP response code - * - * @access protected - * @author Justin Chen - * @param $http_code int HTTP status code - * @return bool TRUE log table updated; otherwise, FALSE - */ - protected function _log_response_code($http_code) - { - $payload['response_code'] = $http_code; - - return $this->rest->db->update( - $this->config->item('rest_logs_table'), $payload, [ - 'id' => $this->_insert_id - ]); - } - - /** - * Check to see if the API key has access to the controller and methods - * - * @access protected - * @return bool TRUE the API key has access; otherwise, FALSE - */ - protected function _check_access() - { - // If we don't want to check access, just return TRUE - if ($this->config->item('rest_enable_access') === FALSE) - { - return TRUE; - } - - // Fetch controller based on path and controller name - $controller = implode( - '/', [ - $this->router->directory, - $this->router->class - ]); - - // Remove any double slashes for safety - $controller = str_replace('//', '/', $controller); - - // Query the access table and get the number of results - return $this->rest->db - ->where('key', $this->rest->key) - ->where('controller', $controller) - ->get($this->config->item('rest_access_table')) - ->num_rows() > 0; - } -} diff --git a/application/helpers/hlp_authentication_helper.php b/application/helpers/hlp_authentication_helper.php index 740823ff9..194f0b249 100644 --- a/application/helpers/hlp_authentication_helper.php +++ b/application/helpers/hlp_authentication_helper.php @@ -34,3 +34,31 @@ function getAuthUID() return isLogged() ? ($ci->authlib->getAuthObj())->{AuthLib::AO_USERNAME} : null; } + +/** + * If the user is NOT logged then a null value is returned. + * If the user is alredy logged, then it is possible to access to the authentication object + * that contains the firstname of the logged user + * NOTE: if the user is logged with a "foreign" method (ex. Bewerbungstool), + * then it is possible that the firstname is null! + */ +function getAuthFirstname() +{ + $ci =& get_instance(); // get CI instance + + return isLogged() ? ($ci->authlib->getAuthObj())->{AuthLib::AO_NAME} : null; +} + +/** + * If the user is NOT logged then a null value is returned. + * If the user is alredy logged, then it is possible to access to the authentication object + * that contains the surname of the logged user + * NOTE: if the user is logged with a "foreign" method (ex. Bewerbungstool), + * then it is possible that the surname is null! + */ +function getAuthSurname() +{ + $ci =& get_instance(); // get CI instance + + return isLogged() ? ($ci->authlib->getAuthObj())->{AuthLib::AO_SURNAME} : null; +} diff --git a/application/helpers/hlp_header_helper.php b/application/helpers/hlp_header_helper.php index 611325722..1a0d9dc49 100644 --- a/application/helpers/hlp_header_helper.php +++ b/application/helpers/hlp_header_helper.php @@ -46,13 +46,16 @@ function generateCSSsInclude($CSSs) { $cssLink = ''; + $ci =& get_instance(); + $cachetoken = '?'.$ci->config->item('fhcomplete_build_version'); + if (isset($CSSs)) { $tmpCSSs = is_array($CSSs) ? $CSSs : array($CSSs); for ($tmpCSSsCounter = 0; $tmpCSSsCounter < count($tmpCSSs); $tmpCSSsCounter++) { - $toPrint = sprintf($cssLink, base_url($tmpCSSs[$tmpCSSsCounter])).PHP_EOL; + $toPrint = sprintf($cssLink, base_url($tmpCSSs[$tmpCSSsCounter]).$cachetoken).PHP_EOL; if ($tmpCSSsCounter > 0) $toPrint = "\t\t".$toPrint; @@ -108,13 +111,16 @@ function generateJSsInclude($JSs) { $jsInclude = ''; + $ci =& get_instance(); + $cachetoken = '?'.$ci->config->item('fhcomplete_build_version'); + if (isset($JSs)) { $tmpJSs = is_array($JSs) ? $JSs : array($JSs); for ($tmpJSsCounter = 0; $tmpJSsCounter < count($tmpJSs); $tmpJSsCounter++) { - $toPrint = sprintf($jsInclude, base_url($tmpJSs[$tmpJSsCounter])).PHP_EOL; + $toPrint = sprintf($jsInclude, base_url($tmpJSs[$tmpJSsCounter].$cachetoken)).PHP_EOL; if ($tmpJSsCounter > 0) $toPrint = "\t\t".$toPrint; diff --git a/application/language/english/rest_controller_lang.php b/application/language/english/rest_controller_lang.php deleted file mode 100644 index 1c665bdc5..000000000 --- a/application/language/english/rest_controller_lang.php +++ /dev/null @@ -1,17 +0,0 @@ -_CI = &get_instance(); - - // Load the inflector helper - $this->_CI->load->helper('inflector'); - - // If the provided data is already formatted we should probably convert it to an array - if ($from_type !== NULL) - { - if (method_exists($this, '_from_' . $from_type)) - { - $data = call_user_func([$this, '_from_' . $from_type], $data); - } - else - { - throw new Exception('Format class does not support conversion from "' . $from_type . '".'); - } - } - - // Set the member variable to the data passed - $this->_data = $data; - } - - /** - * Create an instance of the format class - * e.g: echo $this->format->factory(['foo' => 'bar'])->to_csv(); - * - * @param mixed $data Data to convert/parse - * @param string $from_type Type to convert from e.g. json, csv, html - * - * @return object Instance of the format class - */ - public function factory($data, $from_type = NULL) - { - // $class = __CLASS__; - // return new $class(); - - return new static($data, $from_type); - } - - // FORMATTING OUTPUT --------------------------------------------------------- - - /** - * Format data as an array - * - * @param mixed|NULL $data Optional data to pass, so as to override the data passed - * to the constructor - * @return array Data parsed as an array; otherwise, an empty array - */ - public function to_array($data = NULL) - { - // If no data is passed as a parameter, then use the data passed - // via the constructor - if ($data === NULL && func_num_args() === 0) - { - $data = $this->_data; - } - - // Cast as an array if not already - if (is_array($data) === FALSE) - { - $data = (array) $data; - } - - $array = []; - foreach ((array) $data as $key => $value) - { - if (is_object($value) === TRUE || is_array($value) === TRUE) - { - $array[$key] = $this->to_array($value); - } - else - { - $array[$key] = $value; - } - } - - return $array; - } - - /** - * Format data as XML - * - * @param mixed|NULL $data Optional data to pass, so as to override the data passed - * to the constructor - * @param NULL $structure - * @param string $basenode - * @return mixed - */ - public function to_xml($data = NULL, $structure = NULL, $basenode = 'xml') - { - if ($data === NULL && func_num_args() === 0) - { - $data = $this->_data; - } - - // turn off compatibility mode as simple xml throws a wobbly if you don't. - if (ini_get('zend.ze1_compatibility_mode') == 1) - { - ini_set('zend.ze1_compatibility_mode', 0); - } - - if ($structure === NULL) - { - $structure = simplexml_load_string("<$basenode />"); - } - - // Force it to be something useful - if (is_array($data) === FALSE && is_object($data) === FALSE) - { - $data = (array) $data; - } - - foreach ($data as $key => $value) - { - - //change false/true to 0/1 - if (is_bool($value)) - { - $value = (int) $value; - } - - // no numeric keys in our xml please! - if (is_numeric($key)) - { - // make string key... - $key = (singular($basenode) != $basenode) ? singular($basenode) : 'item'; - } - - // replace anything not alpha numeric - $key = preg_replace('/[^a-z_\-0-9]/i', '', $key); - - if ($key === '_attributes' && (is_array($value) || is_object($value))) - { - $attributes = $value; - if (is_object($attributes)) - { - $attributes = get_object_vars($attributes); - } - - foreach ($attributes as $attribute_name => $attribute_value) - { - $structure->addAttribute($attribute_name, $attribute_value); - } - } - // if there is another array found recursively call this function - elseif (is_array($value) || is_object($value)) - { - $node = $structure->addChild($key); - - // recursive call. - $this->to_xml($value, $node, $key); - } - else - { - // add single node. - $value = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8'); - - $structure->addChild($key, $value); - } - } - - return $structure->asXML(); - } - - /** - * Format data as HTML - * - * @param mixed|NULL $data Optional data to pass, so as to override the data passed - * to the constructor - * @return mixed - */ - public function to_html($data = NULL) - { - // If no data is passed as a parameter, then use the data passed - // via the constructor - if ($data === NULL && func_num_args() === 0) - { - $data = $this->_data; - } - - // Cast as an array if not already - if (is_array($data) === FALSE) - { - $data = (array) $data; - } - - // Check if it's a multi-dimensional array - if (isset($data[0]) && count($data) !== count($data, COUNT_RECURSIVE)) - { - // Multi-dimensional array - $headings = array_keys($data[0]); - } - else - { - // Single array - $headings = array_keys($data); - $data = [$data]; - } - - // Load the table library - $this->_CI->load->library('table'); - - $this->_CI->table->set_heading($headings); - - foreach ($data as $row) - { - // Suppressing the "array to string conversion" notice - // Keep the "evil" @ here - $row = @array_map('strval', $row); - - $this->_CI->table->add_row($row); - } - - return $this->_CI->table->generate(); - } - - /** - * @link http://www.metashock.de/2014/02/create-csv-file-in-memory-php/ - * @param mixed|NULL $data Optional data to pass, so as to override the data passed - * to the constructor - * @param string $delimiter The optional delimiter parameter sets the field - * delimiter (one character only). NULL will use the default value (,) - * @param string $enclosure The optional enclosure parameter sets the field - * enclosure (one character only). NULL will use the default value (") - * @return string A csv string - */ - public function to_csv($data = NULL, $delimiter = ',', $enclosure = '"') - { - // Use a threshold of 1 MB (1024 * 1024) - $handle = fopen('php://temp/maxmemory:1048576', 'w'); - if ($handle === FALSE) - { - return NULL; - } - - // If no data is passed as a parameter, then use the data passed - // via the constructor - if ($data === NULL && func_num_args() === 0) - { - $data = $this->_data; - } - - // If NULL, then set as the default delimiter - if ($delimiter === NULL) - { - $delimiter = ','; - } - - // If NULL, then set as the default enclosure - if ($enclosure === NULL) - { - $enclosure = '"'; - } - - // Cast as an array if not already - if (is_array($data) === FALSE) - { - $data = (array) $data; - } - - // Check if it's a multi-dimensional array - if (isset($data[0]) && count($data) !== count($data, COUNT_RECURSIVE)) - { - // Multi-dimensional array - $headings = array_keys($data[0]); - } - else - { - // Single array - $headings = array_keys($data); - $data = [$data]; - } - - // Apply the headings - fputcsv($handle, $headings, $delimiter, $enclosure); - - foreach ($data as $record) - { - // If the record is not an array, then break. This is because the 2nd param of - // fputcsv() should be an array - if (is_array($record) === FALSE) - { - break; - } - - // Suppressing the "array to string conversion" notice. - // Keep the "evil" @ here. - $record = @ array_map('strval', $record); - - // Returns the length of the string written or FALSE - fputcsv($handle, $record, $delimiter, $enclosure); - } - - // Reset the file pointer - rewind($handle); - - // Retrieve the csv contents - $csv = stream_get_contents($handle); - - // Close the handle - fclose($handle); - - return $csv; - } - - /** - * Encode data as json - * - * @param mixed|NULL $data Optional data to pass, so as to override the data passed - * to the constructor - * @return string Json representation of a value - */ - public function to_json($data = NULL) - { - // If no data is passed as a parameter, then use the data passed - // via the constructor - if ($data === NULL && func_num_args() === 0) - { - $data = $this->_data; - } - - // Get the callback parameter (if set) - $callback = $this->_CI->input->get('callback'); - - if (empty($callback) === TRUE) - { - return json_encode($data); - } - - // We only honour a jsonp callback which are valid javascript identifiers - elseif (preg_match('/^[a-z_\$][a-z0-9\$_]*(\.[a-z_\$][a-z0-9\$_]*)*$/i', $callback)) - { - // Return the data as encoded json with a callback - return $callback . '(' . json_encode($data) . ');'; - } - - // An invalid jsonp callback function provided. - // Though I don't believe this should be hardcoded here - $data['warning'] = 'INVALID JSONP CALLBACK: ' . $callback; - - return json_encode($data); - } - - /** - * Encode data as a serialized array - * - * @param mixed|NULL $data Optional data to pass, so as to override the data passed - * to the constructor - * @return string Serialized data - */ - public function to_serialized($data = NULL) - { - // If no data is passed as a parameter, then use the data passed - // via the constructor - if ($data === NULL && func_num_args() === 0) - { - $data = $this->_data; - } - - return serialize($data); - } - - /** - * Format data using a PHP structure - * - * @param mixed|NULL $data Optional data to pass, so as to override the data passed - * to the constructor - * @return mixed String representation of a variable - */ - public function to_php($data = NULL) - { - // If no data is passed as a parameter, then use the data passed - // via the constructor - if ($data === NULL && func_num_args() === 0) - { - $data = $this->_data; - } - - return var_export($data, TRUE); - } - - // INTERNAL FUNCTIONS - - /** - * @param $data XML string - * @return SimpleXMLElement XML element object; otherwise, empty array - */ - protected function _from_xml($data) - { - return $data ? (array) simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA) : []; - } - - /** - * @param string $data CSV string - * @param string $delimiter The optional delimiter parameter sets the field - * delimiter (one character only). NULL will use the default value (,) - * @param string $enclosure The optional enclosure parameter sets the field - * enclosure (one character only). NULL will use the default value (") - * @return array A multi-dimensional array with the outer array being the number of rows - * and the inner arrays the individual fields - */ - protected function _from_csv($data, $delimiter = ',', $enclosure = '"') - { - // If NULL, then set as the default delimiter - if ($delimiter === NULL) - { - $delimiter = ','; - } - - // If NULL, then set as the default enclosure - if ($enclosure === NULL) - { - $enclosure = '"'; - } - - return str_getcsv($data, $delimiter, $enclosure); - } - - /** - * @param $data Encoded json string - * @return mixed Decoded json string with leading and trailing whitespace removed - */ - protected function _from_json($data) - { - return json_decode(trim($data)); - } - - /** - * @param string Data to unserialized - * @return mixed Unserialized data - */ - protected function _from_serialize($data) - { - return unserialize(trim($data)); - } - - /** - * @param $data Data to trim leading and trailing whitespace - * @return string Data with leading and trailing whitespace removed - */ - protected function _from_php($data) - { - return trim($data); - } - -} diff --git a/application/libraries/PersonLogLib.php b/application/libraries/PersonLogLib.php index b56937dfe..299fc8ce2 100644 --- a/application/libraries/PersonLogLib.php +++ b/application/libraries/PersonLogLib.php @@ -8,6 +8,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); class PersonLogLib { const PARKED_LOGNAME = 'Parked'; + const ONHOLD_LOGNAME = 'Onhold'; /** * Constructor @@ -91,26 +92,20 @@ class PersonLogLib */ public function park($person_id, $date, $taetigkeit_kurzbz, $app = 'core', $oe_kurzbz = null, $user = null) { - $logdata = array( + $onhold = $this->getOnHoldDate($person_id); + + if (hasData($onhold)) + return error("Person already on hold"); + + $logjson = array( 'name' => self::PARKED_LOGNAME ); - $data = array( - 'person_id' => $person_id, - 'zeitpunkt' => $date, - 'taetigkeit_kurzbz' => $taetigkeit_kurzbz, - 'app' => $app, - 'oe_kurzbz' => $oe_kurzbz, - 'logtype_kurzbz' => 'Processstate', - 'logdata' => json_encode($logdata), - 'insertvon' => $user - ); - - return $this->ci->PersonLogModel->insert($data); + return $this->_savePsLog($person_id, $date, $taetigkeit_kurzbz, $logjson, $app, $oe_kurzbz, $user); } /** - * Unparks a person, i.e. removes all log entries in the future + * Unparks a person, i.e. removes all log entries in the future with logname for parking * @param $person_id * @return array with deleted logids */ @@ -131,17 +126,9 @@ class PersonLogLib { $deleted[] = $log->log_id; } - else - { - return $delresult; - } } } } - else - { - return $result; - } return success($deleted); } @@ -172,4 +159,111 @@ class PersonLogLib return $parkeddate; } + + /** + * Sets person on hold, i.e. marks a person so no actions are expected for the person (e.g. as a prestudent). + * Done by adding a logentry with a special name. can be undone only manually by clicking button. + * @param $person_id + * @param $date + * @param $taetigkeit_kurzbz + * @param string $app + * @param null $oe_kurzbz + * @param null $user + * @return array + */ + public function setOnHold($person_id, $date, $taetigkeit_kurzbz, $app = 'core', $oe_kurzbz = null, $user = null) + { + $parked = $this->getParkedDate($person_id); + + if (hasData($parked)) + return error("Person already parked"); + + $logjson = array( + 'name' => self::ONHOLD_LOGNAME + ); + + return $this->_savePsLog($person_id, $date, $taetigkeit_kurzbz, $logjson, $app, $oe_kurzbz, $user); + } + + /** + * Removes on hold status, i.e. removes all log entries with logname for on hold + * @param $person_id + * @return array + */ + public function removeOnHold($person_id) + { + $deleted = array(); + + $result = $this->ci->PersonLogModel->filterLog($person_id); + if (hasData($result)) + { + foreach ($result->retval as $log) + { + $logdata = json_decode($log->logdata); + if (isset($logdata->name) && $logdata->name === self::ONHOLD_LOGNAME) + { + $delresult = $this->ci->PersonLogModel->deleteLog($log->log_id); + if (isSuccess($delresult)) + { + $deleted[] = $log->log_id; + } + } + } + } + return success($deleted); + } + + /** + * Gets date until which a person is on hold + * @param $person_id + * @return the date if person is on hold, null otherwise + */ + public function getOnHoldDate($person_id) + { + $result = $this->ci->PersonLogModel->filterLog($person_id); + + $onholddate = null; + + if (hasData($result)) + { + foreach ($result->retval as $log) + { + $logdata = json_decode($log->logdata); + if (isset($logdata->name) && $logdata->name === self::ONHOLD_LOGNAME) + { + $onholddate = $log->zeitpunkt; + break; + } + } + } + + return $onholddate; + } + + /** + * Saves a processstate log with specified parameters, including a specified log date. + * @param $person_id + * @param $date + * @param $taetigkeit_kurzbz + * @param $logjson + * @param string $app + * @param null $oe_kurzbz + * @param null $user + * @return mixed + */ + private function _savePsLog($person_id, $date, $taetigkeit_kurzbz, $logjson, $app = 'core', $oe_kurzbz = null, $user = null) + { + $data = array( + 'person_id' => $person_id, + 'zeitpunkt' => $date, + 'taetigkeit_kurzbz' => $taetigkeit_kurzbz, + 'app' => $app, + 'oe_kurzbz' => $oe_kurzbz, + 'logtype_kurzbz' => 'Processstate', + 'logdata' => json_encode($logjson), + 'insertvon' => $user + ); + + return $this->ci->PersonLogModel->insert($data); + } } diff --git a/application/views/system/infocenter/infocenterData.php b/application/views/system/infocenter/infocenterData.php index 04b99b057..49f0c7af6 100644 --- a/application/views/system/infocenter/infocenterData.php +++ b/application/views/system/infocenter/infocenterData.php @@ -7,6 +7,7 @@ $TAETIGKEIT_KURZBZ = '\'bewerbung\', \'kommunikation\''; $LOGDATA_NAME = '\'Login with code\', \'Login with user\', \'New application\', \'Interessent rejected\''; $LOGDATA_NAME_PARKED = '\'Parked\''; + $LOGDATA_NAME_ONHOLD = '\'Onhold\''; $LOGTYPE_KURZBZ = '\'Processstate\''; $STATUS_KURZBZ = '\'Wartender\', \'Bewerber\', \'Aufgenommener\', \'Student\''; $ADDITIONAL_STG = '10021,10027'; @@ -24,6 +25,7 @@ pl.zeitpunkt AS "LockDate", pl.lockuser AS "LockUser", pd.parkdate AS "ParkDate", + ohd.onholddate AS "OnholdDate", ( SELECT l.zeitpunkt FROM system.tbl_log l @@ -228,6 +230,14 @@ AND l.logdata->>\'name\' = '.$LOGDATA_NAME_PARKED.' AND l.zeitpunkt >= NOW() ) pd USING(person_id) + LEFT JOIN ( + SELECT l.person_id, + l.zeitpunkt AS onholddate + FROM system.tbl_log l + WHERE l.logtype_kurzbz = '.$LOGTYPE_KURZBZ.' + AND l.logdata->>\'name\' = '.$LOGDATA_NAME_ONHOLD.' + AND l.zeitpunkt >= NOW() + ) ohd USING(person_id) WHERE EXISTS ( SELECT 1 @@ -278,6 +288,7 @@ ucfirst($this->p->t('global', 'sperrdatum')), ucfirst($this->p->t('global', 'gesperrtVon')), ucfirst($this->p->t('global', 'parkdatum')), + ucfirst($this->p->t('global', 'rueckstelldatum')), ucfirst($this->p->t('global', 'letzteAktion')), 'Aktionstyp', 'AnzahlAktePflicht', @@ -340,6 +351,11 @@ $datasetRaw->{'ParkDate'} = '-'; } + if ($datasetRaw->{'OnholdDate'} == null) + { + $datasetRaw->{'OnholdDate'} = '-'; + } + if ($datasetRaw->{'StgAbgeschickt'} == null) { $datasetRaw->{'StgAbgeschickt'} = '-'; @@ -376,6 +392,11 @@ $mark = FilterWidget::DEFAULT_MARK_ROW_CLASS; } + if ($datasetRaw->OnholdDate != null) + { + $mark = "text-success"; + } + // Parking has priority over locking if ($datasetRaw->ParkDate != null) { diff --git a/application/views/system/infocenter/infocenterDetails.php b/application/views/system/infocenter/infocenterDetails.php index 9e708d076..ab4e3533e 100644 --- a/application/views/system/infocenter/infocenterDetails.php +++ b/application/views/system/infocenter/infocenterDetails.php @@ -34,7 +34,14 @@ 'nichtsZumAusparken', 'fehlerBeimAusparken', 'fehlerBeimParken', - 'bewerberGeparktBis' + 'bewerberGeparktBis', + 'bewerberOnHold', + 'bewerberOnHoldEntfernen', + 'bewerberOnHoldBis', + 'nichtsZumEntfernen', + 'fehlerBeimEntfernen', + 'rueckstelldatumUeberschritten', + 'parkenZurueckstellenInfo' ), 'ui' => array( 'gespeichert', @@ -176,7 +183,7 @@
| '.$p->t('global/bezeichnung').' | '.$p->t('global/organisationseinheit').' | -'.$p->t('global/semester').' | -'.$p->t('global/institut').' | '.$p->t('profil/gueltigvon').' | -'.$p->t('profil/gueltigbis').' | -'.$p->t('profil/gueltigbis').' | '. + ($adminOrOwnUser ? ''.$p->t('profil/wochenstunden').' | ' : ''). + ' '; + $wochenstunden_sum = 0.00; + while($row_funktion = $db->db_fetch_object($result_funktion)) { echo " @@ -544,13 +564,50 @@ if (!defined('CIS_PROFIL_FUNKTIONEN_ANZEIGEN') || CIS_PROFIL_FUNKTIONEN_ANZEIGEN echo ' - '.$row_funktion->bf_bezeichnung; echo "".$row_funktion->organisationseinheittyp_kurzbz.' '.$row_funktion->oe_bezeichnung." | -$row_funktion->semester | -$row_funktion->fachbereich_kurzbz | ".$datum_obj->formatDatum($row_funktion->datum_von,'d.m.Y')." | -".$datum_obj->formatDatum($row_funktion->datum_bis,'d.m.Y')." | - "; +".$datum_obj->formatDatum($row_funktion->datum_bis,'d.m.Y')." | ". + ($adminOrOwnUser ? "".number_format($row_funktion->wochenstunden, 2)." | " : ""). + ""; + + if(isset($row_funktion->wochenstunden) && $adminOrOwnUser) + $wochenstunden_sum += (double)$row_funktion->wochenstunden; } - echo '
|---|