Commit 92e0ac4c39ec27c137c4be3b240dff452fa3447f
Merge branch 'edge' of git@github.com:ktgit/knowledgetree into edge
Showing
3 changed files
with
316 additions
and
33 deletions
webservice/clienttools/ajaxhandler.php
| 1 | 1 | <?php |
| 2 | 2 | class ajaxHandler{ |
| 3 | + protected $rawRequestObject=NULL; | |
| 4 | + protected $digestToken=NULL; | |
| 5 | + protected $remoteIp=NULL; | |
| 6 | + | |
| 3 | 7 | public $ret=NULL; |
| 4 | 8 | public $req=NULL; |
| 5 | 9 | public $version=NULL; |
| ... | ... | @@ -9,60 +13,310 @@ class ajaxHandler{ |
| 9 | 13 | public $authenticator=NULL; |
| 10 | 14 | public $noAuthRequireList=array(); |
| 11 | 15 | public $standardServices=array('system'); |
| 16 | + | |
| 17 | + protected $errors=array(); | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 1.Parse JSON | |
| 21 | + * 2.Check Request Validity (hash/ip/expiration token) | |
| 22 | + * 3.Preliminary Session Check | |
| 23 | + * if no session or invalid session | |
| 24 | + * 3.1 Use credentials to create a new session. | |
| 25 | + * 3.3 Update Authentication segment with new sessionid | |
| 26 | + * 4.Authentication Check | |
| 27 | + * 5.Service Dispatch | |
| 28 | + */ | |
| 29 | + | |
| 30 | + public function __construct(&$response=NULL,&$kt,$noAuthRequests=''){ | |
| 31 | + | |
| 32 | + //========================= Preparations | |
| 33 | + // set the response object | |
| 34 | + if(get_class($response)=='jsonResponseObject'){ | |
| 35 | + $this->ret=&$response; | |
| 36 | + }else{ | |
| 37 | + $this->ret=new jsonResponseObject(); | |
| 38 | + } | |
| 39 | + $this->log("[__construct]ENTERING PREPARATIONS"); | |
| 40 | + | |
| 41 | + $this->remoteIp = (getenv(HTTP_X_FORWARDED_FOR)) ? getenv(HTTP_X_FORWARDED_FOR) : getenv(REMOTE_ADDR); | |
| 42 | + $this->log("[__construct]Remote IP determined as: {$this->remoteIp}"); | |
| 12 | 43 | |
| 13 | - public function __construct(&$ret=NULL,&$kt,$noAuthRequests=''){ | |
| 14 | - // set a local copy of the json request wrapper | |
| 15 | 44 | $noAuthRequests=is_array($noAuthRequests)?$noAuthRequests:split(',',(string)$noAuthRequests); |
| 16 | 45 | $this->registerNoAuthRequest($noAuthRequests); |
| 17 | - $this->req=new jsonWrapper(isset($_GET['request'])?$_GET['request']:(isset($_POST['request'])?$_POST['request']:'')); | |
| 46 | + | |
| 47 | + $this->rawRequestObject=isset($_GET['request'])?$_GET['request']:(isset($_POST['request'])?$_POST['request']:''); | |
| 48 | + $this->digestToken=isset($_GET['msgAuth'])?$_GET['msgAuth']:(isset($_POST['msgAuth'])?$_POST['msgAuth']:''); | |
| 49 | + $this->log("[__construct]DigestToken Found: {$this->digestToken}"); | |
| 50 | + | |
| 51 | + $this->ret->addDebug('Raw Request',$this->rawRequestObject); | |
| 52 | + $this->ret->addDebug('DigestToken Received',$this->digestToken); | |
| 53 | + $this->ret->addDebug('Remote IP',$this->remoteIp); | |
| 54 | + | |
| 55 | + | |
| 56 | + if($this->auth['session'])session_id($this->auth['session']); | |
| 57 | + $this->session=session_id(); | |
| 58 | + $this->log("[__construct]Session Restarted as: {$this->session}"); | |
| 59 | + // session_id('BLANK_SESSION'); | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + //========================= 1. Parse Json | |
| 64 | + $this->log("[__construct]ENTERING Parse Json"); | |
| 65 | + $this->req=new jsonWrapper($this->rawRequestObject); | |
| 18 | 66 | $this->auth=$this->structArray('user,pass,passhash,appType,session,token,version',$this->req->jsonArray['auth']); |
| 19 | 67 | $this->request=$this->structArray('service,function,parameters',$this->req->jsonArray['request']); |
| 20 | 68 | |
| 69 | + //Add additional parameters | |
| 21 | 70 | $add_params=array_merge($_GET,$_POST); |
| 22 | 71 | unset($add_params['request'],$add_params['datasource']); |
| 23 | 72 | $this->request['parameters']=array_merge($this->request['parameters'],$add_params); |
| 24 | - | |
| 25 | - | |
| 26 | - // set the response object | |
| 27 | - if(get_class($ret)=='jsonResponseObject'){ | |
| 28 | - $this->ret=&$ret; | |
| 29 | - }else{ | |
| 30 | - $this->ret=new jsonResponseObject(); | |
| 31 | - } | |
| 73 | + | |
| 74 | + if(!$this->auth['debug'])$this->ret->includeDebug=false; | |
| 75 | + | |
| 32 | 76 | $this->ret->setRequest($this->req->jsonArray); |
| 33 | 77 | $this->ret->setTitle($this->request['service'].'::'.$this->request['function']); |
| 34 | 78 | $this->ret->setDebug('Server Versions',$this->getServerVersions()); |
| 35 | - | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + //========================= 2. Test System Requirements | |
| 85 | + $this->log("[__construct]ENTERING Test System Requirements"); | |
| 36 | 86 | if(get_class($kt)=='KTAPI'){ |
| 37 | 87 | $this->kt=&$kt; |
| 38 | 88 | }else{ |
| 39 | 89 | $this->ret->addError('KnowledgeTree Object not Received in '.__CLASS__.' constructor. Quitting.'); |
| 40 | 90 | return $this->render(); |
| 41 | 91 | } |
| 92 | + | |
| 93 | + | |
| 94 | + //TODO: Get rid of this service | |
| 95 | + $this->loadService('auth'); | |
| 96 | + $this->authenticator=new auth($this,$this->ret,$this->kt,$this->request,$this->auth); | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + //========================= 3. Check Request Validity | |
| 101 | + $this->log("[__construct]ENTERING Check Request Validity"); | |
| 102 | + if(!$this->checkRequestValidity())return $this->render(); | |
| 103 | + if(!$this->checkTokenValidity())return $this->render(); | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + //========================= 4. Preliminary Session Check | |
| 109 | + $this->log("[__construct]ENTERING Preliminary Session Check"); | |
| 110 | + if(!$this->checkSessionValidity()){ | |
| 111 | + $this->creatNewSession(); //(login) This may fail, be the user is still allowed to dispatch to the | |
| 112 | + } | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + //========================= 5. Authentication Check | |
| 119 | + $this->log("[__construct]ENTERING Authentication Check"); | |
| 120 | + if(!$this->isStandardService() && !$this->isNoAuthRequiredRequest()){ | |
| 121 | + //Authentication is Required | |
| 122 | + $this->log("[__construct]Determined Authentication is required"); | |
| 123 | + if(!$this->checkCredentials()){ | |
| 124 | + throw new Exception('User Credentials Necessary for Requested Service'); | |
| 125 | + return $this->render(); | |
| 126 | + } | |
| 127 | + } | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + //========================= 6. Service Dispatch | |
| 135 | + $this->log("[__construct]ENTERING Service Dispatch"); | |
| 136 | + $this->dispatch(); | |
| 137 | + return $this->render(); | |
| 138 | + } | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + protected function checkRequestValidity(){ | |
| 152 | + $this->log("[checkRequestvalidity]Entering..."); | |
| 153 | + $securityHash=md5(md5($this->rawRequestObject).'_'.$this->auth['token'].'_'.$this->getUserPass()); | |
| 154 | + $digestToken=$this->digestToken; | |
| 155 | + $this->log("[checkRequestvalidity]comparing {$securityHash} with {$digestToken} as received"); | |
| 42 | 156 | |
| 43 | - // Prepare | |
| 44 | - if(!$this->isStandardService()){ | |
| 45 | - $this->loadService('auth'); | |
| 46 | - $this->authenticator=new auth($this,$this->ret,$this->kt,$this->request,$this->auth); | |
| 47 | - | |
| 157 | + $passed=$securityHash==$digestToken; | |
| 158 | + | |
| 159 | + $data=array( | |
| 160 | + 'Received Token' =>$digestToken, | |
| 161 | + 'Expected Token' =>$securityHash, | |
| 162 | + 'Passed' =>$passed, | |
| 163 | + '' | |
| 164 | + ); | |
| 165 | + $this->ret->addDebug('Message Digest Security',$data); | |
| 166 | + | |
| 167 | + if(!$passed){ | |
| 168 | + $this->log("[checkRequestvalidity]Failed Validity Test"); | |
| 169 | + throw new Exception('Message Integrity Was Compromised.'); | |
| 170 | + } | |
| 171 | + return $passed; | |
| 172 | + } | |
| 48 | 173 | |
| 49 | - //Make sure a token exists before continuing | |
| 50 | - if(!$this->verifyToken())return $this->render(); | |
| 51 | 174 | |
| 175 | + protected function checkSessionValidity(){ | |
| 176 | + $valid=$this->start_session(); | |
| 177 | + $this->auth['session']=session_id(); | |
| 178 | + $this->ret->setStatus('session_id',session_id()); | |
| 179 | + $this->ret->addDebug('Auth',array('Session Check'=>$valid)); | |
| 180 | +// echo $valid?'true':'false'.'<br /><br /><br /><br /><br /><br /><br />'; | |
| 181 | + return $valid; | |
| 182 | + } | |
| 52 | 183 | |
| 53 | - if(!$this->verifySession()){ | |
| 54 | - $this->doLogin(); | |
| 55 | - $isAuthRequired=$this->isNoAuthRequiredRequest(); | |
| 56 | - $isAuthenticated=$this->isAuthenticated(); | |
| 57 | - if(!$isAuthRequired && !$isAuthenticated)return $this->render(); | |
| 58 | - } | |
| 184 | + //TODO: Alter this to verify whether token was used before or whether it is new | |
| 185 | + protected function checkTokenValidity(){ | |
| 186 | + $token=$this->auth['token']; | |
| 187 | + $tokenList=$_SESSION['JAPI_TOKEN_STORE']?$_SESSION['JAPI_TOKEN_STORE']:array(); | |
| 188 | + $valid=!in_array($token,$tokenList); | |
| 189 | + if($valid){ | |
| 190 | + $tokenList[$token]=$token; | |
| 191 | + $_SESSION['JAPI_TOKEN_STORE']=$tokenList; | |
| 192 | + }else{ | |
| 193 | + $this->error('Invalid Token - Already Used'); | |
| 194 | + $this->log('Invalid Token - Already Used'); | |
| 59 | 195 | } |
| 60 | 196 | |
| 61 | - $this->dispatch(); | |
| 62 | - | |
| 63 | - return $this->render(); | |
| 197 | + return $valid; | |
| 198 | + } | |
| 199 | + | |
| 200 | + | |
| 201 | + protected function creatNewSession(){ | |
| 202 | + $this->ret->addDebug('Auth',array('Attempting to Create a New Session')); | |
| 203 | + if($this->checkCredentials()){ | |
| 204 | + $ssession=KTAPI_UserSession::_check_session($this->getUserObject(),$this->remoteIp,$this->auth['appType']); | |
| 205 | + $session=$ssession[0]; | |
| 206 | + $this->ret->addDebug('####################################Session Created : '.$session); | |
| 207 | + $this->auth['session']=session_id(); | |
| 208 | + $this->ret->setStatus('session_id',session_id()); | |
| 209 | + return true; | |
| 210 | + }else{ | |
| 211 | + return false; | |
| 212 | + } | |
| 213 | + } | |
| 214 | + | |
| 215 | + protected function start_session(){ | |
| 216 | + $app_type=$this->auth['appType']; | |
| 217 | + $session_id=$this->auth['session']; | |
| 218 | + $ip=$this->remoteIp; | |
| 219 | + | |
| 220 | + $session=$this->kt->get_session(); | |
| 221 | + | |
| 222 | + if(get_class($session)=='KTAPI_UserSession'){ | |
| 223 | + return true; | |
| 224 | + }else{ | |
| 225 | + $session = $this->kt->get_active_session($session_id, $ip, $app_type); | |
| 226 | + | |
| 227 | + if (PEAR::isError($session)){ | |
| 228 | + return false; | |
| 229 | + } | |
| 230 | + $this->auth['session']=session_id(); | |
| 231 | + $this->ret->setStatus('session_id',session_id()); | |
| 232 | + return true; | |
| 233 | + } | |
| 234 | + | |
| 235 | + | |
| 236 | + } | |
| 237 | + | |
| 238 | + | |
| 239 | + protected function getUserPass(){ | |
| 240 | + $l_pass=md5('@NO_AUTH_NEEDED@'); | |
| 241 | + $u=$this->getUserObject(); | |
| 242 | + if($u){ | |
| 243 | + $l_pass=$this->getUserObject()->getPassword(); | |
| 244 | + } | |
| 245 | + return $l_pass; | |
| 246 | + } | |
| 247 | + | |
| 248 | + protected function getUserObject(){ | |
| 249 | + $kt=$this->kt; | |
| 250 | + $user=$this->auth['user']; | |
| 251 | + $o_user=$kt->get_user_object_by_username($user); | |
| 252 | + | |
| 253 | + if(PEAR::isError($o_user)){ | |
| 254 | + if(!isset($this->errors['usernotfound']))$this->ret->addError('User '.$user.' not found'); | |
| 255 | + $this->errors['usernotfound']=true; | |
| 256 | + return false; | |
| 257 | + }else{ | |
| 258 | + $this->log("[getUserObject] Found User: ".$o_user->getName()); | |
| 259 | + } | |
| 260 | + return $o_user; | |
| 261 | + } | |
| 262 | + | |
| 263 | + protected function checkCredentials(){ | |
| 264 | + $user=$this->auth['user']; | |
| 265 | + $passHash=$this->auth['passhash']; | |
| 266 | + | |
| 267 | + $kt=$this->kt; | |
| 268 | + | |
| 269 | + $o_user=$kt->get_user_object_by_username($user); | |
| 270 | + | |
| 271 | + if(PEAR::isError($o_user)){ | |
| 272 | + if(!isset($this->errors['usernotfound']))$this->ret->addError('User '.$user.' not found'); | |
| 273 | + $this->errors['usernotfound']=true; | |
| 274 | + return false; | |
| 275 | + } | |
| 276 | + | |
| 277 | + try{ | |
| 278 | + $l_pass=$o_user->getPassword(); | |
| 279 | + $l_passHash=md5($l_pass.$this->auth['token']); | |
| 280 | + | |
| 281 | + $passed=$passHash==$l_passHash; | |
| 282 | + | |
| 283 | + $this->ret->setDebug('Auth',array( | |
| 284 | + 'User Real Password'=>$l_pass, | |
| 285 | + 'User Real Password Hash'=>$l_passHash, | |
| 286 | + 'Received Password Hash'=>$passHash, | |
| 287 | + 'passed'=>$passed | |
| 288 | + )); | |
| 289 | + | |
| 290 | + return $passed; | |
| 291 | + | |
| 292 | + }catch(Exception $e){ | |
| 293 | + throw new Exception('Unknown credentialCheck error encountered'); | |
| 294 | + return false; | |
| 295 | + } | |
| 296 | + | |
| 297 | + return ture; | |
| 298 | + } | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + protected function log($str=''){ | |
| 303 | + $this->ret->log($str); | |
| 304 | + } | |
| 305 | + | |
| 306 | + | |
| 307 | + protected function error($errMsg=NULL){ | |
| 308 | + $this->ret->addError($errMsg); | |
| 64 | 309 | } |
| 65 | 310 | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 66 | 320 | private function structArray($structString=NULL,$arr=NULL){ |
| 67 | 321 | $struct=array_flip(split(',',(string)$structString)); |
| 68 | 322 | return array_merge($struct,is_array($arr)?$arr:array()); |
| ... | ... | @@ -91,7 +345,17 @@ class ajaxHandler{ |
| 91 | 345 | } |
| 92 | 346 | |
| 93 | 347 | public function isStandardService(){ |
| 94 | - return in_array($this->request['service'],$this->standardServices); | |
| 348 | + $isStandardService=in_array($this->request['service'],$this->standardServices); | |
| 349 | + | |
| 350 | + $debug=array( | |
| 351 | + 'requested service' =>$this->request['service'], | |
| 352 | + 'standard services' =>$this->standardServices, | |
| 353 | + 'isStandardService' =>$isStandardService | |
| 354 | + ); | |
| 355 | + | |
| 356 | + $this->ret->addDebug('ajaxhandler::isStandardService',$debug); | |
| 357 | + | |
| 358 | + return $isStandardService; | |
| 95 | 359 | } |
| 96 | 360 | |
| 97 | 361 | |
| ... | ... | @@ -188,7 +452,17 @@ class ajaxHandler{ |
| 188 | 452 | public function isNoAuthRequiredRequest(){ |
| 189 | 453 | $req=$this->request; |
| 190 | 454 | $reqString=strtolower("{$req['service']}.{$req['function']}"); |
| 191 | - return in_array($reqString,$this->noAuthRequireList); | |
| 455 | + $requiresAuth=in_array($reqString,$this->noAuthRequireList); | |
| 456 | + | |
| 457 | + $debug=array( | |
| 458 | + 'requested service method' =>$reqString, | |
| 459 | + 'no auth required list' =>$this->noAuthRequireList, | |
| 460 | + 'requires auth' =>$requiresAuth | |
| 461 | + ); | |
| 462 | + | |
| 463 | + $this->ret->addDebug('ajaxhandler::isNoAuthRequiredRequest',$debug); | |
| 464 | + | |
| 465 | + return $requiresAuth; | |
| 192 | 466 | } |
| 193 | 467 | |
| 194 | 468 | } | ... | ... |
webservice/clienttools/comms.php
webservice/clienttools/jsonWrapper.php
| ... | ... | @@ -9,6 +9,7 @@ class jsonResponseObject{ |
| 9 | 9 | protected $errors=array(); |
| 10 | 10 | protected $status=array('session_id'=>'','random_token'=>''); |
| 11 | 11 | protected $data=array(); |
| 12 | + protected $log=array(); | |
| 12 | 13 | protected $request=array(); |
| 13 | 14 | protected $debug=array(); |
| 14 | 15 | public $additional=array(); |
| ... | ... | @@ -28,7 +29,8 @@ class jsonResponseObject{ |
| 28 | 29 | ), |
| 29 | 30 | 'data' =>array(), |
| 30 | 31 | 'request' =>array(), |
| 31 | - 'debug' =>array() | |
| 32 | + 'debug' =>array(), | |
| 33 | + 'log' =>array() | |
| 32 | 34 | ); |
| 33 | 35 | |
| 34 | 36 | |
| ... | ... | @@ -49,9 +51,12 @@ class jsonResponseObject{ |
| 49 | 51 | } |
| 50 | 52 | |
| 51 | 53 | public function setDebug($varName=NULL,$value=NULL){ |
| 54 | + if(is_array($this->debug[$varName]) && is_array($value))$value=array_merge($this->debug[$varName],$value); | |
| 52 | 55 | $this->debug[$varName]=$value; |
| 53 | 56 | } |
| 54 | 57 | |
| 58 | + public function addDebug($varName=NULL,$value=NULL){$this->setDebug($varName,$value);} | |
| 59 | + | |
| 55 | 60 | public function setRequest($request=NULL){ |
| 56 | 61 | $this->request=$request; |
| 57 | 62 | } |
| ... | ... | @@ -61,6 +66,10 @@ class jsonResponseObject{ |
| 61 | 66 | $this->title=$title; |
| 62 | 67 | } |
| 63 | 68 | |
| 69 | + public function log($str){ | |
| 70 | + $this->log[]='['.date('h:i:s').'] '.$str; | |
| 71 | + } | |
| 72 | + | |
| 64 | 73 | public function getJson(){ |
| 65 | 74 | $response=array_merge(array( |
| 66 | 75 | 'requestName' =>$this->title, |
| ... | ... | @@ -72,6 +81,7 @@ class jsonResponseObject{ |
| 72 | 81 | 'data' =>$this->data, |
| 73 | 82 | 'request' =>$this->request, |
| 74 | 83 | 'debug' =>$this->debug, |
| 84 | + 'log' =>$this->log | |
| 75 | 85 | ),$this->additional); |
| 76 | 86 | if(!$this->includeDebug) unset($response['debug']); |
| 77 | 87 | |
| ... | ... | @@ -91,7 +101,6 @@ class jsonWrapper{ |
| 91 | 101 | public $jsonArray=array(); |
| 92 | 102 | |
| 93 | 103 | public function __construct($content=NULL){ |
| 94 | -// $content=stripslashes($content); | |
| 95 | 104 | $this->raw=$content; |
| 96 | 105 | $content=@json_decode($content,true); |
| 97 | 106 | if(!is_array($content))throw new jsonContentException('Invalid JSON input',jsonContentException::INPUT_ERROR); | ... | ... |