/** * handle session exception for http requests * * we force the client to delete session cookie, but we don't destroy * the session on server side. This way we prevent session DOS from thrid party */ public function sessionException() { Tinebase_Session::expireSessionCookie(); echo "\n <script type='text/javascript'>\n window.location.href = window.location.href;\n </script>\n "; /* ob_start(); $html = $this->login(); $html = ob_get_clean(); $script = " <script type='text/javascript'> exception = {code: 401}; Ext.onReady(function() { Ext.MessageBox.show({ title: _('Authorisation Required'), msg: _('Your session is not valid. You need to login again.'), buttons: Ext.Msg.OK, icon: Ext.MessageBox.WARNING }); }); </script>"; echo preg_replace('/<\/head.*>/', $script . '</head>', $html); */ }
/** * (non-PHPdoc) * @see Tinebase_Server_Interface::handle() */ public function handle(\Zend\Http\Request $request = null, $body = null) { $this->_request = $request instanceof \Zend\Http\Request ? $request : Tinebase_Core::get(Tinebase_Core::REQUEST); $this->_body = $body !== null ? $body : fopen('php://input', 'r'); $request = $request instanceof \Zend\Http\Request ? $request : new \Zend\Http\PhpEnvironment\Request(); // only for debugging //Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " raw request: " . $request->__toString()); // handle CORS requests if ($request->getHeaders()->has('ORIGIN') && !$request->getHeaders()->has('X-FORWARDED-HOST')) { /** * First the client sends a preflight request * * METHOD: OPTIONS * Access-Control-Request-Headers:x-requested-with, content-type * Access-Control-Request-Method:POST * Origin:http://other.site * Referer:http://other.site/example.html * User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36 * * We have to respond with * * Access-Control-Allow-Credentials:true * Access-Control-Allow-Headers:x-requested-with, x-tine20-request-type, content-type, x-tine20-jsonkey * Access-Control-Allow-Methods:POST * Access-Control-Allow-Origin:http://other.site * * Then the client sends the standard JSON request with two additional headers * * METHOD: POST * Origin:http://other.site * Referer:http://other.site/example.html * Standard-JSON-Rquest-Headers... * * We have to add two additional headers to our standard response * * Access-Control-Allow-Credentials:true * Access-Control-Allow-Origin:http://other.site */ $origin = $request->getHeaders('ORIGIN')->getFieldValue(); $uri = \Zend\Uri\UriFactory::factory($origin); if (in_array($uri->getScheme(), array('http', 'https'))) { $allowedOrigins = array_merge((array) Tinebase_Core::getConfig()->get(Tinebase_Config::ALLOWEDJSONORIGINS, array()), array($this->_request->getServer('SERVER_NAME'))); if (in_array($uri->getHost(), $allowedOrigins)) { // this headers have to be sent, for any CORS'ed JSON request header('Access-Control-Allow-Origin: ' . $origin); header('Access-Control-Allow-Credentials: true'); } // check for CORS preflight request if ($request->getMethod() == \Zend\Http\Request::METHOD_OPTIONS && $request->getHeaders()->has('ACCESS-CONTROL-REQUEST-METHOD')) { $this->_methods = array('handleCors'); if (in_array($uri->getHost(), $allowedOrigins)) { header('Access-Control-Allow-Methods: POST'); header('Access-Control-Allow-Headers: x-requested-with, x-tine20-request-type, content-type, x-tine20-jsonkey'); header('Access-Control-Max-Age: 3600'); // cache result of OPTIONS request for 1 hour } else { Tinebase_Core::getLogger()->WARN(__METHOD__ . '::' . __LINE__ . " unhandled CORS preflight request from {$origin}"); Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . " you may want to set \"'allowedJsonOrigins' => array('{$uri->getHost()}'),\" to config.inc.php"); Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " allowed origins: " . print_r($allowedOrigins, TRUE)); } // stop further processing => is OPTIONS request return; } } } $exception = false; if (Tinebase_Session::sessionExists()) { try { Tinebase_Core::startCoreSession(); } catch (Zend_Session_Exception $zse) { $exception = new Tinebase_Exception_AccessDenied('Not Authorised', 401); // expire session cookie for client Tinebase_Session::expireSessionCookie(); } } if ($exception === false) { try { Tinebase_Core::initFramework(); } catch (Exception $exception) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' initFramework exception: ' . $exception); } } } $json = $request->getContent(); $json = Tinebase_Core::filterInputForDatabase($json); if (substr($json, 0, 1) == '[') { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' batched request'); } $isBatchedRequest = true; $requests = Zend_Json::decode($json); } else { $isBatchedRequest = false; $requests = array(Zend_Json::decode($json)); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { $_requests = $requests; foreach (array('password', 'oldPassword', 'newPassword') as $field) { if (isset($requests[0]["params"][$field])) { $_requests[0]["params"][$field] = "*******"; } } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' is JSON request. rawdata: ' . print_r($_requests, true)); } } $response = array(); foreach ($requests as $requestOptions) { if ($requestOptions !== NULL) { $request = new Zend_Json_Server_Request(); $request->setOptions($requestOptions); $response[] = $exception ? $this->_handleException($request, $exception) : $this->_handle($request); } else { if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Got empty request options: skip request.'); } $response[] = NULL; } } if (!headers_sent()) { header('Content-type: application/json'); } echo $isBatchedRequest ? '[' . implode(',', $response) . ']' : $response[0]; }
/** * create new setup user session * * @param string $_username * @param string $_password * @return bool */ public function login($_username, $_password) { $setupAuth = new Setup_Auth($_username, $_password); $authResult = Zend_Auth::getInstance()->authenticate($setupAuth); if ($authResult->isValid()) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Valid credentials, setting username in session and registry.'); Tinebase_Session::regenerateId(); Setup_Core::set(Setup_Core::USER, $_username); Setup_Session::getSessionNamespace()->setupuser = $_username; return true; } else { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Invalid credentials! ' . print_r($authResult->getMessages(), TRUE)); Tinebase_Session::expireSessionCookie(); sleep(2); return false; } }
/** * (non-PHPdoc) * @see Tinebase_Server_Interface::handle() */ public function handle(\Zend\Http\Request $request = null, $body = null) { $this->_request = $request instanceof \Zend\Http\Request ? $request : Tinebase_Core::get(Tinebase_Core::REQUEST); $this->_body = $body !== null ? $body : fopen('php://input', 'r'); $server = new Tinebase_Http_Server(); $server->setClass('Tinebase_Frontend_Http', 'Tinebase'); $server->setClass('Filemanager_Frontend_Download', 'Download'); try { if (Tinebase_Session::sessionExists()) { try { Tinebase_Core::startCoreSession(); } catch (Zend_Session_Exception $zse) { // expire session cookie for client Tinebase_Session::expireSessionCookie(); } } Tinebase_Core::initFramework(); if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Is HTTP request. method: ' . $this->getRequestMethod()); } if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' REQUEST: ' . print_r($_REQUEST, TRUE)); } // register additional HTTP apis only available for authorised users if (Tinebase_Session::isStarted() && Zend_Auth::getInstance()->hasIdentity()) { if (empty($_REQUEST['method'])) { $_REQUEST['method'] = 'Tinebase.mainScreen'; } $applicationParts = explode('.', $this->getRequestMethod()); $applicationName = ucfirst($applicationParts[0]); if (Tinebase_Core::getUser() && Tinebase_Core::getUser()->hasRight($applicationName, Tinebase_Acl_Rights_Abstract::RUN)) { try { $server->setClass($applicationName . '_Frontend_Http', $applicationName); } catch (Exception $e) { Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " Failed to add HTTP API for application '{$applicationName}' Exception: \n" . $e); } } } else { if (empty($_REQUEST['method'])) { $_REQUEST['method'] = 'Tinebase.login'; } // sessionId got send by client, but we don't use sessions for non authenticated users if (Tinebase_Session::sessionExists()) { // expire session cookie on client Tinebase_Session::expireSessionCookie(); } } $this->_method = $this->getRequestMethod(); $server->handle($_REQUEST); } catch (Zend_Json_Server_Exception $zjse) { // invalid method requested or not authenticated, etc. Tinebase_Exception::log($zjse); Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . ' Attempt to request a privileged Http-API method without valid session from "' . $_SERVER['REMOTE_ADDR']); header('HTTP/1.0 403 Forbidden'); exit; } catch (Exception $exception) { Tinebase_Exception::log($exception, false); try { $setupController = Setup_Controller::getInstance(); if ($setupController->setupRequired()) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Setup required'); } $this->_method = 'Tinebase.setupRequired'; } else { if (preg_match('/download|export/', $this->_method)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Server error during download/export - exit with 500'); } header('HTTP/1.0 500 Internal Server Error'); exit; } else { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Show mainscreen with setup exception'); } $this->_method = 'Tinebase.exception'; } } $server->handle(array('method' => $this->_method)); } catch (Exception $e) { header('HTTP/1.0 503 Service Unavailable'); die('Service Unavailable'); } } }