vars[$index] = $value; } public function __get($index) { return $this->vars[$index]; } } abstract class Controller { protected $registry; public $ActionAjaxOff; protected $methodAccess; protected $apiAction; protected $apiParams; protected $apiModule; protected $publicAction = array(); private $allowJwt = array(); protected $appID; protected $tokenID; protected $generalActions=array(); protected $isFile=false; private $allowedMimeType = [ 'image/jpeg', 'image/png', 'image/jpg', 'video/mp4', 'application/pdf', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/plain', 'application/octet-stream', 'application/zip', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/vnd.ms-word.document.macroEnabled.12', 'application/vnd.ms-word.template.macroEnabled.12', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'application/vnd.ms-excel.sheet.macroEnabled.12', 'application/vnd.ms-excel.template.macroEnabled.12', 'application/vnd.ms-excel.addin.macroEnabled.12', 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' ]; public function __construct($registry) { // session_id('hcportal-session-id'); // session_start(); // Http::enabledCors(); $this->registry = $registry; $this->methodAccess = $_SERVER['REQUEST_METHOD']; $this->allowJwt = array('dologin', 'dologout', 'refreshToken', 'generateNewToken', 'loginauth'); // connect main DB // $this->registry = $registry; // $this->registry->db = Connections::getInstance( // $this->registry->config->host, // $this->registry->config->db, // $this->registry->config->socket, // $this->registry->config->user, // $this->registry->config->password, // $this->registry->config->dbms // ); if($this->registry->config->dbMainConType!=='local') { $this->registry->db = Connections::getInstance($this->registry->config->dbMainConType); }else { $this->registry->db = Connections::getInstance( $this->registry->config->dbMainConType, $this->registry->config->host, $this->registry->config->socket, $this->registry->config->user, $this->registry->config->password ); } // $handler = new Session($registry); // $result= session_set_save_handler($handler,true); // session_start(); // session_start(); // if (!interface_exists('SessionHandlerInterface')) { // exit('ATTENTION: the session handler implemented by Predis requires PHP >= 5.4.0 ' . // "or a polyfill for SessionHandlerInterface provided by an external package.\n"); // } // $single_server=[ // 'scheme' => 'tcp', // 'host' => '10.1.200.218', // 'port' => 6388, // ]; // $client = new Client($single_server, ['prefix' => 'sessions:']); // // Set `gc_maxlifetime` to specify a time-to-live of 5 seconds for session keys. // $handler = new Handler($client, ['gc_maxlifetime' => get_cfg_var("session.gc_maxlifetime")]); // // Register the session handler. // $handler->register(); // // We just set a fixed session ID only for the sake of our example. // session_id('hcportalsessionid'); if(!isset($_SESSION)) { session_start(); } // check mime_type $this->checkContentFile(); } abstract public function index(); protected function checkToken() { try { $token = Http::getTokenJWT(); // get token ID $tokenPart = explode('.', $token); if (count($tokenPart) != 4) { throw new \ErrorException('token part invalid'); } $stmt = $this->registry->db->prepare('select appID,tokenID,chipper,data,expired from jwt where id=:id'); $stmt->bindValue(':id', $tokenPart[3], \PDO::PARAM_INT); $stmt->execute(); $rs = $stmt->fetchAll(\PDO::FETCH_ASSOC); if (count($rs) == 0) { throw new \ErrorException('token jwt not exist'); } $this->appID=$rs[0]['appID']; $this->tokenID=$rs[0]['tokenID']; $now = time(); if ($rs[0]['expired'] < $now) { throw new \Exception('Time Token refresh Exceded'); } // update expired $stmt = $this->registry->db->prepare('update jwt set expired=:expired where id=:id'); $stmt->bindValue(':id', $tokenPart[3], \PDO::PARAM_INT); $stmt->bindValue(':expired', time() + __LIFETIMEJWT, \PDO::PARAM_INT); $stmt->execute(); $newToken = $tokenPart[0] . '.' . $tokenPart[1] . '.' . $tokenPart[2]; $data = Token::decodeJWTNew($newToken, $rs[0]['chipper']); if (is_numeric($data)) { if ($data === 8) // expired token { $this->registry->log->customError('tokenError', 'Module Controller / check Token : expired token , token :' . $data); Http::tokenExpired(array('message' => 'Token need refresh')); } else { throw new \ErrorException('decode Error token :' . $data); } } $rData = json_decode(json_encode($data->data), true); \Helper::setSession($rData); return true; } catch (\ErrorException $e) { $this->registry->log->customError('tokenError', 'Module Controller / check Token :' . $e->getMessage()); Http::UnauthorizedResponseJson(array('message' => 'Wrong Token')); //return false; } catch (\Exception $e) { $this->registry->log->customError('tokenError', 'Module Controller / check Token :' . $e->getMessage()); Http::UnauthorizedResponseJson(array('message' => $e->getMessage())); } catch (\PDOException $e) { $this->registry->log->customError('tokenError', 'Module Controller / check Token :' . $e->getMessage()); Http::UnauthorizedResponseJson(array('message' => 'query error ')); } } protected function checkTokenOld() { try { $token = Http::getTokenJWT(); $data = Token::decodeJWTNew($token); if (is_numeric($data)) { if ($data === 8) // expired token { $this->registry->log->customError('tokenError', 'Module Controller / check Token : expired token , token :' . $data); Http::tokenExpired(array('message' => 'Wrong Token')); } else { throw new \ErrorException('decode Error token :' . $data); } } $rData = json_decode(json_encode($data->data), true); \Helper::setSession($rData); return true; } catch (\ErrorException $e) { $this->registry->log->customError('tokenError', 'Module Controller / check Token :' . $e->getMessage()); Http::UnauthorizedResponseJson(array('message' => 'Wrong Token')); //return false; } catch (\Exception $e) { $this->registry->log->customError('tokenError', 'Module Controller / check Token :' . $e->getMessage()); Http::UnauthorizedResponseJson(array('message' => $e->getMessage())); } catch (\PDOException $e) { $this->registry->log->customError('tokenError', 'Module Controller / check Token :' . $e->getMessage()); Http::UnauthorizedResponseJson(array('message' => 'query error ')); } } protected function checkRulesAccess() { $rule = new Rule($this->registry); if (!in_array($this->apiAction, $this->publicAction)) { $hasAccess = $rule->hasAccess($this->apiModule, $this->apiAction); if ($hasAccess == false) { Http::ErrorQueryResponse('operation not permit', 'json'); } } } protected function checkAPIAccess() { /* check method access */ $this->allowOptionMethod(); if (!in_array($this->methodAccess, array('POST', 'GET', 'DELETE'))) { Http::UnauthorizedResponseJson(array('message' => 'Method Not allowed')); } $this->apiAction = ''; switch ($this->methodAccess) { case 'POST': /* check and get action */ $this->apiAction = Http::GetvarData('action'); if (!isset($this->apiAction)) { $jtext = Http::GetBodyRequest(); $this->apiParams = \Firebase\JWT\JWT::jsonDecode($jtext); if (!isset($this->apiParams->action)) { Http::UnauthorizedResponseJson(array('message' => 'Action not set')); } $this->apiAction = $this->apiParams->action; } break; default: // GET // DELETE $this->apiAction = Http::GetvarData('action'); if (strlen($this->apiAction) === 0) { Http::UnauthorizedResponseJson(array('message' => 'Action not set')); } break; } /* check token */ $isAllowed = $this->checkToken(); if (!$isAllowed) { Http::UnauthorizedResponseJson(array('message' => 'Wrong Token')); } /* check rule */ $this->checkRulesAccess(); } protected function isAuthorized() { /* check method access */ $this->allowOptionMethod(); if (!in_array($this->methodAccess, array('POST', 'GET', 'DELETE'))) { Http::UnauthorizedResponseJson(array('message' => 'Method Not allowed')); } $this->apiAction = ''; // var_dump($this->methodAccess); switch ($this->methodAccess) { case 'POST': /* check and get action */ if($this->isFile){ $aText['action']=Http::GetVarData('action','post'); $this->apiParams=\Firebase\JWT\JWT::jsonDecode(\Firebase\JWT\JWT::jsonEncode($aText)); $this->apiAction = Http::GetVarData('action','post'); }else{ $jtext = Http::GetBodyRequest(); $this->apiParams = \Firebase\JWT\JWT::jsonDecode($jtext); if (!isset($this->apiParams->action)) { Http::UnauthorizedResponseJson(array('message' => 'Action not set')); } $this->apiAction = $this->apiParams->action; } break; default: // GET // DELETE $this->apiAction = Http::GetvarData('action'); $this->apiParams = json_decode(json_encode(Http::getAllRequest())); if (strlen($this->apiAction) === 0) { Http::UnauthorizedResponseJson(array('message' => 'Action not set')); } break; } if (!in_array($this->apiAction, $this->allowJwt)) { /* check token */ $isAllowed = $this->checkToken(); if (!$isAllowed) { Http::UnauthorizedResponseJson(array('message' => 'Wrong Token')); } } if (is_array($this->generalActions)) { /* check rule */ if(!in_array($this->apiAction,$this->generalActions)){ $this->checkRulesAccess(); } } /* process request */ $this->prosesRequest(); } protected function checkAPIAccessEvaluation() { if ($this->methodAccess == 'OPTIONS') { if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) { Http::ResponseJson('ok', '0', '1'); } } $isAllowed = $this->checkTokenEvaluation(); if (!$isAllowed) { Http::UnauthorizedResponseJson(array('message' => 'Wrong Token')); } } protected function checkTokenEvaluation() { try { $token = Http::getTokenJWT(); $data = Token::decodeJWT($token); if (!isset($data->data)) { throw new \ErrorException('decode Error token :' . $token); } $_SESSION = array(); session_destroy(); $_SESSION['group'] = $data->data->group; $_SESSION['username'] = $data->data->username; $_SESSION['name'] = isset($data->data->name) ? $data->data->name : $data->data->nama; $_SESSION['section'] = isset($data->data->section) ? $data->data->section : $data->data->secion; $_SESSION['userID'] = $data->data->userID; $_SESSION['empNo'] = isset($data->data->empNo) ? $data->data->empNo : ''; $_SESSION['empSite'] = $data->data->empSite; $_SESSION['empSubArea'] = isset($data->data->empSubArea) ? $data->data->empSubArea : ''; $_SESSION['flagApp'] = isset($data->data->flagApp) ? $data->data->flagApp : ''; $_SESSION['nationality'] = isset($data->data->nationality) ? $data->data->nationality : ''; $_SESSION['role'] = isset($data->data->role) ? $data->data->role : ''; // if jwt valid set session var return true; } catch (\ErrorException $e) { $this->registry->log->error('Module Controller / check Token Eval :' . $e->getMessage()); return false; } } protected function allowOptionMethod() { if ($this->methodAccess == 'OPTIONS') { if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) { Http::ResponseJson(array('ok'), '0', '1'); } } } private function prosesRequest() { switch ($this->methodAccess) { case 'POST': $this->executePost(); break; case 'GET': $this->executeGet(); break; case 'DELETE': $this->executeDelete(); break; default: Http::ErrorQueryResponse('method not permit'); break; } } protected function executePost() { $act = $this->apiAction; if (method_exists($this, $act)) { $this->$act(); } else { Http::ErrorQueryResponse('Action not registered'); } } private function executeGet() { $act = $this->apiAction; if (method_exists($this, $act)) { $this->$act(); } else { Http::ErrorQueryResponse('Action not registered'); } } protected function executeDelete() { } protected function extendAllowJwt(array $extended) { foreach ($extended as $value) { array_push($this->allowJwt, $value); } } /** * fungsi ini untuk convert message dari api. * untuk keperluan migrasi FE ke framework yang baru * karena fokus utama migrasi FE dulu jadi BE yang menyesuaikan * * @param message * @return $result : string */ protected function convertMessages($message) { $result = $message; switch($message){ case 'PAYROLL.MESSAGE.SUCCMESINS': $result = 'MESSAGE.SUCCMESINS'; break; case 'PAYROLL.MESSAGE.FAILMESEXIST': $result = 'MESSAGE.DATA_ALREADY_EXIST'; break; case 'PAYROLL.MESSAGE.FAILMESUNKNOWN': $result = 'MESSAGE.FAILMESUNKNOWN'; break; case 'PAYROLL.MESSAGE.FAILMESERRREQ': $result = 'MESSAGE.FAILMESERRREQ'; break; case 'PAYROLL.MESSAGE.SUCCMESDEL': $result = 'MESSAGE.SUCCMESDEL'; break; case 'PAYROLL.MESSAGE.SUCCMESUPD': $result = 'MESSAGE.SUCCMESUPD'; break; case 'PAYROLL.MESSAGE.FAILMESQUERY': $result = 'MESSAGE.FAILMESQUERY'; break; case 'MENU.MASTER_DATA.ADMINISTRATIVE_AREA.MAIN.CANTDELETE': $result = 'MESSAGE.CANTDELETE'; break; } return $result; } /** * fungsi ini untuk convert response menjadi format pagination. * untuk keperluan migrasi FE ke framework yang baru * karena fokus utama migrasi FE dulu jadi BE yang menyesuaikan * * @param array * @return array */ protected function convertToPaginationFormat($array) { $total = count($array); $aData['iTotalDisplayRecords'] = $total; $aData['iTotalRecords'] = $total; $aData['aData'] = $array; return $aData; } private function checkContentFile(){ $this->isFile=Http::isMultipartFormData(); if($this->isFile){ if (!empty($_FILES) && is_array($_FILES) && count($_FILES) > 0) { foreach ($_FILES as $file) { $filepath = $file['tmp_name']; // $filesize = filesize($filepath); $fileinfo = finfo_open(FILEINFO_MIME_TYPE); $filetype = finfo_file($fileinfo, $filepath); finfo_close($fileinfo); if (!in_array($filetype, $this->allowedMimeType)) { Http::ErrorQueryResponse(array('name' => $file['name'], 'message' =>'15220-failed'), 'json'); } } } } } protected function setSession($token) { // get token ID $tokenPart = explode('.', $token); \Helper::dump($tokenPart); if (count($tokenPart) != 4) { throw new \ErrorException('token part invalid'); } $stmt = $this->registry->db->prepare('select appID,tokenID,chipper,data,expired from jwt where id=:id'); $stmt->bindValue(':id', $tokenPart[3], \PDO::PARAM_INT); $stmt->execute(); $rs = $stmt->fetchAll(\PDO::FETCH_ASSOC); if (count($rs) == 0) { throw new \ErrorException('token jwt not exist'); } $this->appID=$rs[0]['appID']; $this->tokenID=$rs[0]['tokenID']; $now = time(); if ($rs[0]['expired'] < $now) { throw new \Exception('Time Token refresh Exceded'); } // update expired $stmt = $this->registry->db->prepare('update jwt set expired=:expired where id=:id'); $stmt->bindValue(':id', $tokenPart[3], \PDO::PARAM_INT); $stmt->bindValue(':expired', time() + __LIFETIMEJWT, \PDO::PARAM_INT); $stmt->execute(); $newToken = $tokenPart[0] . '.' . $tokenPart[1] . '.' . $tokenPart[2]; $data = Token::decodeJWTNew($newToken, $rs[0]['chipper']); if (is_numeric($data)) { if ($data === 8) // expired token { $this->registry->log->customError('tokenError', 'Module Controller / check Token : expired token , token :' . $data); Http::tokenExpired(array('message' => 'Token need refresh')); } else { throw new \ErrorException('decode Error token :' . $data); } } $rData = json_decode(json_encode($data->data), true); \Helper::setSession($rData); } } class Router { private $registry; private $path; private $args = array(); public $file; public $controller; public $action; public $parts; private $controllerPath; private $prefix; public function __construct($registry,$prefix='') { $this->registry = $registry; $this->prefix=$prefix; } public function loader() { try { /*** a new controller class instance , pembuatan controller object***/ $class = $this->controller; $this->registry->controller = $class; $this->registry->action = $this->action; $ClassName = ucfirst($class); $mod = strtolower($class); $aModules = explode('/', $this->controllerPath); $jumModules = count($aModules); //$mod1 = substr($this->controllerPath, 1); $mod1 = $this->controllerPath; $strslash = substr($this->controllerPath, 0, 1); if ($strslash == '/' || $strslash == '\\') { $mod1 = substr($this->controllerPath, 1); } $newmod = str_replace('/', '\\', $mod1); $namespaces = "\\modules\\{$newmod}\\controller\\{$ClassName}Controller"; $this->registry->ContPath = $mod1; $controller = new $namespaces($this->registry); /*** check if the action is callable ***/ if (is_callable(array($controller, $this->action)) == false) { $action = 'index'; } else { $action = $this->action; } /*** run the action , ini sama kayak execute function yang ada pada controller pada mvc sebelumnya * ***/ if ($this->registry->config->ajax == 'on') { if (!empty($controller->ActionAjaxOff)) { if (!in_array($action, $controller->ActionAjaxOff)) { // if true if (!$this->registry->isAjax) { exit('ajax request required'); } } } else { if (!$this->registry->isAjax) { exit('ajax request required'); } } } else { if ($this->registry->isAjax) { exit('please set ajax config to "on" if request ajax required'); } } $controller->$action(); } catch (\Exception $e) { $this->registry->log->error('Core / Loader :' . $e->getMessage() . ' Line :' . $e->getLine() . ' ' . $e->getFile()); Http::InternalServerError('error loader'); } } private function getController() { try { /* get variable*/ $this->controller = $this->getControllerName(); $j = 0; if (!(empty($this->parts[2]) or $this->parts[2] == '-')) { for ($i = 2; $i < count($this->parts); $i++) { $this->args[$j] = $this->parts[$i]; $j++; } $this->registry->vars = $this->args; } else { $this->registry->vars = 'null'; } /*** set the file path ***/ return $this->controller; } catch (\Exception $e) { $this->registry->log->error('Core / Loader :' . $e->getMessage() . ' Line :' . $e->getLine() . ' ' . $e->getFile()); \Aiko\Http::InternalServerError('Error loader'); } } public function getControllerName() { try { $restrict = ''; if ($this->registry->config->restrict == 'yes') { if (isset($this->registry->config->ipconfig)) { $ip = $this->getRealIpAddr(); $register = in_array($ip, $this->registry->config->ipconfig); if ($ip != '127.0.0.1') { if (!$register) { $restrict = 'restrict'; } } } else { $restrict = 'restrict'; } } $this->getName($restrict); $this->Request_check(); return $this->controller; } catch (\Exception $e) { $this->registry->log->error('Core / Loader :' . $e->getMessage() . ' Line :' . $e->getLine()); \Aiko\Http::InternalServerError('Error loader'); } } private function getName($restrict) { try { if ($restrict == 'restrict') { $this->controller = 'restrict'; $this->controllerPath = 'restrict'; } else { $route = (empty($_GET['rt'])) ? '' : $_GET['rt']; if (empty($route)) { // jika route tidak ada / pada awal page $route = 'index'; } else { // clean root with prefix $route= $this->cleanRoute($route); /*** get the parts of the route ***/ $this->parts = explode('/', $route); // set controller name // cek apakan part yang pertama memiliki controller kalau tidak ditemukan return 404 if (!is_dir(__SITE_PATH . '/src/modules/' . $this->parts[0])) { $this->controller = 'error404'; $this->controllerPath = 'error404'; } else { $i = 0; $path = ''; $found = false; do { $path .= '/' . $this->parts[$i]; $dir = __SITE_PATH . '/src/modules' . $path; if (file_exists($dir . '/controller')) { $found = true; break; } $i++; } while ($i < count($this->parts)); if ($found) { $this->controller = $this->parts[$i]; $this->controllerPath = $path; } else { $this->controller = 'error404'; $this->controllerPath = 'error404'; } if (isset($this->parts[$i + 1])) { // set action name $this->action = $this->parts[$i + 1]; } } } // cek apakah controller kosong, jika kosong set ke index if (empty($this->controller)) { $this->controller = 'index'; $this->controllerPath = 'index'; } /*** Get action ***/ if (empty($this->action)) { $this->action = 'index'; } } } catch (\Exception $e) { $this->registry->log->error('Core / Loader :' . $e->getMessage() . ' Line :' . $e->getLine()); \Aiko\Http::InternalServerError('Error loader'); } } private function Request_check() { $this->registry->isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) and strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'; } public function getControllerPath() { return $this->controllerPath; } private function getRealIpAddr() { if (!empty($_SERVER['HTTP_CLIENT_IP'])) { //check ip from share internet $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { //to check ip is pass from proxy $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $ip = $_SERVER['REMOTE_ADDR']; } return $ip; } private function cleanRoute($route):string{ $prefixLength=strlen($this->prefix); if ($prefixLength==0){ return $route; } $routePrefix=substr($route,0,$prefixLength); if($this->prefix!==$routePrefix){ Http::InternalServerError('failed route'); } $newRoute= substr($route,$prefixLength); if(strlen($newRoute)==0 || $newRoute=='/'){ $newRoute='index'; } // check apakah string pertama route / ? if(substr($newRoute,0,1)=='/'){ $newRoute=substr($newRoute,1); } return $newRoute; } }