diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index 97719079a..274d38981 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -35,6 +35,7 @@ require_once __DIR__.'/services/CeleryService.php'; require_once __DIR__.'/services/SoundcloudService.php'; require_once __DIR__.'/forms/helpers/ValidationTypes.php'; require_once __DIR__.'/forms/helpers/CustomDecorators.php'; +require_once __DIR__.'/controllers/plugins/PageLayoutInitPlugin.php'; require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php'; require_once __DIR__.'/controllers/plugins/Maintenance.php'; require_once __DIR__.'/controllers/plugins/ConversionTracking.php'; @@ -52,20 +53,21 @@ if (array_key_exists("REQUEST_URI", $_SERVER) && (stripos($_SERVER["REQUEST_URI" die(); } +Zend_Session::setOptions(array('strict' => true)); + + Config::setAirtimeVersion(); require_once (CONFIG_PATH . 'navigation.php'); Zend_Validate::setDefaultNamespaces("Zend"); -Application_Model_Auth::pinSessionToClient(Zend_Auth::getInstance()); - $front = Zend_Controller_Front::getInstance(); $front->registerPlugin(new RabbitMqPlugin()); $front->registerPlugin(new Zend_Controller_Plugin_ConversionTracking()); $front->throwExceptions(false); -//localization configuration -Application_Model_Locale::configureLocalization(); + + /* The bootstrap class should only be used to initialize actions that return a view. Actions that return JSON will not use the bootstrap class! */ @@ -78,55 +80,7 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap $view->doctype('XHTML1_STRICT'); } - protected function _initGlobals() - { - $view = $this->getResource('view'); - $baseUrl = Application_Common_OsPath::getBaseDir(); - $view->headScript()->appendScript("var baseUrl = '$baseUrl';"); - $this->_initTranslationGlobals($view); - - $user = Application_Model_User::GetCurrentUser(); - if (!is_null($user)) { - $userType = $user->getType(); - } else { - $userType = ""; - } - $view->headScript()->appendScript("var userType = '$userType';"); - } - - /** - * Create a global namespace to hold a session token for CSRF prevention - */ - protected function _initCsrfNamespace() - { - $csrf_namespace = new Zend_Session_Namespace('csrf_namespace'); - // Check if the token exists - if (!$csrf_namespace->authtoken) { - // If we don't have a token, regenerate it and set a 1 week timeout - // Should we log the user out here if the token is expired? - $csrf_namespace->authtoken = sha1(uniqid(rand(), 1)); - $csrf_namespace->setExpirationSeconds(168 * 60 * 60); - } - - //Here we are closing the session for writing because otherwise no requests - //in this session will be handled in parallel. This gives a major boost to the perceived performance - //of the application (page load times are more consistent, no lock contention). - session_write_close(); - } - - /** - * Ideally, globals should be written to a single js file once - * from a php init function. This will save us from having to - * reinitialize them every request - */ - private function _initTranslationGlobals() - { - $view = $this->getResource('view'); - $view->headScript()->appendScript("var PRODUCT_NAME = '" . PRODUCT_NAME . "';"); - $view->headScript()->appendScript("var USER_MANUAL_URL = '" . USER_MANUAL_URL . "';"); - $view->headScript()->appendScript("var COMPANY_NAME = '" . COMPANY_NAME . "';"); - } protected function _initTasks() { /* We need to wrap this here so that we aren't checking when we're running the unit test suite @@ -139,115 +93,7 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap } } - protected function _initHeadLink() - { - $CC_CONFIG = Config::getConfig(); - $view = $this->getResource('view'); - - $baseUrl = Application_Common_OsPath::getBaseDir(); - - $view->headLink(array('rel' => 'icon', 'href' => $baseUrl . 'favicon.ico?' . $CC_CONFIG['airtime_version'], 'type' => 'image/x-icon'), 'PREPEND') - ->appendStylesheet($baseUrl . 'css/bootstrap.css?' . $CC_CONFIG['airtime_version']) - ->appendStylesheet($baseUrl . 'css/redmond/jquery-ui-1.8.8.custom.css?' . $CC_CONFIG['airtime_version']) - ->appendStylesheet($baseUrl . 'css/pro_dropdown_3.css?' . $CC_CONFIG['airtime_version']) - ->appendStylesheet($baseUrl . 'css/qtip/jquery.qtip.min.css?' . $CC_CONFIG['airtime_version']) - ->appendStylesheet($baseUrl . 'css/styles.css?' . $CC_CONFIG['airtime_version']) - ->appendStylesheet($baseUrl . 'css/masterpanel.css?' . $CC_CONFIG['airtime_version']) - ->appendStylesheet($baseUrl . 'css/tipsy/jquery.tipsy.css?' . $CC_CONFIG['airtime_version']); - } - - protected function _initHeadScript() - { - $CC_CONFIG = Config::getConfig(); - - $view = $this->getResource('view'); - - $baseUrl = Application_Common_OsPath::getBaseDir(); - - $view->headScript()->appendFile($baseUrl . 'js/libs/jquery-1.8.3.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/libs/jquery-ui-1.8.24.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/bootstrap/bootstrap.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/libs/underscore-min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - - // ->appendFile($baseUrl . 'js/libs/jquery.stickyPanel.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/qtip/jquery.qtip.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/jplayer/jquery.jplayer.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/sprintf/sprintf-0.7-beta1.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/cookie/jquery.cookie.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/i18n/jquery.i18n.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'locale/general-translation-table?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'locale/datatables-translation-table?' . $CC_CONFIG['airtime_version'], 'text/javascript') - - ->appendScript("$.i18n.setDictionary(general_dict)") - ->appendScript("var baseUrl='$baseUrl'"); - - //These timezones are needed to adjust javascript Date objects on the client to make sense to the user's set timezone - //or the server's set timezone. - $serverTimeZone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); - $now = new DateTime("now", $serverTimeZone); - $offset = $now->format("Z") * -1; - $view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds"); - - if (class_exists("Zend_Auth", false) && Zend_Auth::getInstance()->hasIdentity()) { - $userTimeZone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); - $now = new DateTime("now", $userTimeZone); - $offset = $now->format("Z") * -1; - $view->headScript()->appendScript("var userTimezoneOffset = {$offset}; //in seconds"); - } - - //scripts for now playing bar - $view->headScript()->appendFile($baseUrl . 'js/airtime/airtime_bootstrap.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/airtime/dashboard/helperfunctions.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/airtime/dashboard/dashboard.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/airtime/dashboard/versiontooltip.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/tipsy/jquery.tipsy.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - - ->appendFile($baseUrl . 'js/airtime/common/common.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') - ->appendFile($baseUrl . 'js/airtime/common/audioplaytest.js?' . $CC_CONFIG['airtime_version'], 'text/javascript'); - - $user = Application_Model_User::getCurrentUser(); - if (!is_null($user)) { - $userType = $user->getType(); - } else { - $userType = ""; - } - - $view->headScript()->appendScript("var userType = '$userType';"); - if (array_key_exists('REQUEST_URI', $_SERVER) //Doesn't exist for unit tests - && strpos($_SERVER['REQUEST_URI'], 'Dashboard/stream-player') === false - && strpos($_SERVER['REQUEST_URI'], 'audiopreview') === false - && $_SERVER['REQUEST_URI'] != "/") { - $plan_level = strval(Application_Model_Preference::GetPlanLevel()); - // Since the Hobbyist plan doesn't come with Live Chat support, don't enable it - if (Application_Model_Preference::GetLiveChatEnabled() && $plan_level !== 'hobbyist') { - $client_id = strval(Application_Model_Preference::GetClientId()); - $station_url = $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; - $view->headScript()->appendScript("var livechat_client_id = '$client_id';\n" . - "var livechat_plan_type = '$plan_level';\n" . - "var livechat_station_url = 'http://$station_url';"); - $view->headScript()->appendFile($baseUrl . 'js/airtime/common/livechat.js?' . $CC_CONFIG['airtime_version'], 'text/javascript'); - } - } - - /* - if (isset($CC_CONFIG['demo']) && $CC_CONFIG['demo'] == 1) { - $view->headScript()->appendFile($baseUrl.'js/libs/google-analytics.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); - }*/ - } - - protected function _initViewHelpers() - { - $view = $this->getResource('view'); - $view->addHelperPath(APPLICATION_PATH . 'views/helpers', 'Airtime_View_Helper'); - $view->assign('suspended', (Application_Model_Preference::getProvisioningStatus() == PROVISIONING_STATUS_SUSPENDED)); - } - - protected function _initTitle() - { - $view = $this->getResource('view'); - $view->headTitle(Application_Model_Preference::GetHeadTitle()); - } protected function _initZFDebug() { @@ -293,6 +139,7 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { $front = Zend_Controller_Front::getInstance(); $front->registerPlugin(new Zend_Controller_Plugin_Maintenance()); + $front->registerPlugin(new PageLayoutInitPlugin($this)); } } diff --git a/airtime_mvc/application/common/SecurityHelper.php b/airtime_mvc/application/common/SecurityHelper.php index baf4ca46b..f36ae638a 100644 --- a/airtime_mvc/application/common/SecurityHelper.php +++ b/airtime_mvc/application/common/SecurityHelper.php @@ -13,16 +13,11 @@ class SecurityHelper { return $arr; } - public static function verifyAjaxCSRFToken($observedToken) { + public static function verifyCSRFToken($observedToken) { $current_namespace = new Zend_Session_Namespace('csrf_namespace'); $observed_csrf_token = $observedToken; $expected_csrf_token = $current_namespace->authtoken; - if ($observed_csrf_token == $expected_csrf_token){ - return true; - }else{ - return false; - } - + return ($observed_csrf_token == $expected_csrf_token); } } \ No newline at end of file diff --git a/airtime_mvc/application/common/TaskManager.php b/airtime_mvc/application/common/TaskManager.php index c1687defa..c44be2552 100644 --- a/airtime_mvc/application/common/TaskManager.php +++ b/airtime_mvc/application/common/TaskManager.php @@ -114,6 +114,9 @@ final class TaskManager { * otherwise false */ private function _isUserSessionRequest() { + if (!Zend_Session::isStarted()) { + return false; + } $auth = Zend_Auth::getInstance(); $data = $auth->getStorage()->read(); return !empty($data); diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index d30013d8d..ca0d182ef 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -11,6 +11,8 @@ class ApiController extends Zend_Controller_Action public function init() { + + //Ignore API key and session authentication for these APIs: $ignoreAuth = array("live-info", "live-info-v2", "week-info", @@ -25,6 +27,11 @@ class ApiController extends Zend_Controller_Action "show-logo" ); + if (Zend_Session::isStarted()) { + Logging::error("Session already started for an API request. Check your code because + this will negatively impact performance."); + } + $params = $this->getRequest()->getParams(); if (!in_array($params['action'], $ignoreAuth)) { $this->checkAuth(); @@ -73,13 +80,23 @@ class ApiController extends Zend_Controller_Action $CC_CONFIG = Config::getConfig(); $api_key = $this->_getParam('api_key'); - if (!in_array($api_key, $CC_CONFIG["apiKey"]) && - is_null(Zend_Auth::getInstance()->getStorage()->read())) { - header('HTTP/1.0 401 Unauthorized'); - print _('You are not allowed to access this resource.'); - exit; + if (in_array($api_key, $CC_CONFIG["apiKey"])) { + return true; } - return true; + + //Start the session so the authentication is + //enforced by the ACL plugin. + Zend_Session::start(); + $authAdapter = Zend_Auth::getInstance(); + Application_Model_Auth::pinSessionToClient($authAdapter); + + if ((Zend_Auth::getInstance()->hasIdentity())) { + return true; + } + + header('HTTP/1.0 401 Unauthorized'); + print _('You are not allowed to access this resource.'); + exit(); } public function versionAction() diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index 2be8bc7e4..eb8cd5e88 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -25,7 +25,10 @@ class BillingController extends Zend_Controller_Action { $baseUrl = Application_Common_OsPath::getBaseDir(); $this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']); Billing::ensureClientIdIsValid(); - + + //Zend's CSRF token element requires the session to be open for writing + session_start(); + $request = $this->getRequest(); $form = new Application_Form_BillingUpgradeDowngrade(); @@ -219,7 +222,10 @@ class BillingController extends Zend_Controller_Action { $CC_CONFIG = Config::getConfig(); $baseUrl = Application_Common_OsPath::getBaseDir(); $this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']); - + + //Zend's CSRF token element requires the session to be open for writing + session_start(); + $request = $this->getRequest(); $form = new Application_Form_BillingClient(); Billing::ensureClientIdIsValid(); diff --git a/airtime_mvc/application/controllers/LoginController.php b/airtime_mvc/application/controllers/LoginController.php index b40178526..60c9b27be 100644 --- a/airtime_mvc/application/controllers/LoginController.php +++ b/airtime_mvc/application/controllers/LoginController.php @@ -8,8 +8,14 @@ class LoginController extends Zend_Controller_Action public function init() { - //Open the session for writing, because we close it for writing by default in Bootstrap.php as an optimization. - session_start(); + $CC_CONFIG = Config::getConfig(); + $baseUrl = Application_Common_OsPath::getBaseDir(); + + $this->view->headLink(array('rel' => 'icon', 'href' => $baseUrl . 'favicon.ico?' . $CC_CONFIG['airtime_version'], 'type' => 'image/x-icon'), 'PREPEND') + ->appendStylesheet($baseUrl . 'css/bootstrap.css?' . $CC_CONFIG['airtime_version']) + ->appendStylesheet($baseUrl . 'css/redmond/jquery-ui-1.8.8.custom.css?' . $CC_CONFIG['airtime_version']) + ->appendStylesheet($baseUrl . 'css/styles.css?' . $CC_CONFIG['airtime_version']); + } public function indexAction() @@ -22,13 +28,20 @@ class LoginController extends Zend_Controller_Action //Enable AJAX requests from www.airtime.pro for the sign-in process. CORSHelper::enableATProCrossOriginRequests($request, $response); - + Application_Model_Locale::configureLocalization($request->getcookie('airtime_locale', $stationLocale)); - $auth = Zend_Auth::getInstance(); - - if ($auth->hasIdentity()) { - $this->_redirect('Showbuilder'); + + if (Zend_Session::isStarted()) { + //Open the session for writing, because we close it for writing by default in Bootstrap.php as an optimization. + session_start(); + + $auth = Zend_Auth::getInstance(); + $auth->getStorage(); + + if ($auth->hasIdentity()) { + $this->_redirect('Showbuilder'); + } } //uses separate layout without a navigation. @@ -43,6 +56,10 @@ class LoginController extends Zend_Controller_Action $message = _("Please enter your username and password."); if ($request->isPost()) { + + //Open the session for writing, because we close it for writing by default in Bootstrap.php as an optimization. + //session_start(); + // if the post contains recaptcha field, which means form had recaptcha field. // Hence add the element for validation. if (array_key_exists('recaptcha_response_field', $request->getPost())) { @@ -117,6 +134,9 @@ class LoginController extends Zend_Controller_Action public function logoutAction() { + //Open the session for writing, because we close it for writing by default in Bootstrap.php as an optimization. + session_start(); + $auth = Zend_Auth::getInstance(); $auth->clearIdentity(); // Unset all session variables relating to CSRF prevention on logout diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index 478d1d332..6e991ba24 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -134,7 +134,7 @@ class PreferenceController extends Zend_Controller_Action // Remove reliance on .phtml files to render requests $this->_helper->viewRenderer->setNoRender(true); - if (!SecurityHelper::verifyAjaxCSRFToken($this->_getParam('csrf_token'))) { + if (!SecurityHelper::verifyCSRFToken($this->_getParam('csrf_token'))) { Logging::error(__FILE__ . ': Invalid CSRF token'); $this->_helper->json->sendJson(array("jsonrpc" => "2.0", "valid" => false, "error" => "CSRF token did not match.")); return; @@ -486,7 +486,7 @@ class PreferenceController extends Zend_Controller_Action $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); - if (!SecurityHelper::verifyAjaxCSRFToken($this->_getParam('csrf_token'))) { + if (!SecurityHelper::verifyCSRFToken($this->_getParam('csrf_token'))) { Logging::error(__FILE__ . ': Invalid CSRF token'); $this->_helper->json->sendJson(array("jsonrpc" => "2.0", "valid" => false, "error" => "CSRF token did not match.")); return; diff --git a/airtime_mvc/application/controllers/plugins/Acl_plugin.php b/airtime_mvc/application/controllers/plugins/Acl_plugin.php index c08533ab9..35df192d6 100644 --- a/airtime_mvc/application/controllers/plugins/Acl_plugin.php +++ b/airtime_mvc/application/controllers/plugins/Acl_plugin.php @@ -109,9 +109,10 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract public function preDispatch(Zend_Controller_Request_Abstract $request) { $controller = strtolower($request->getControllerName()); - Application_Model_Auth::pinSessionToClient(Zend_Auth::getInstance()); if (in_array($controller, array( + "index", + "login", "api", "auth", "error", @@ -123,7 +124,10 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract ))) { $this->setRoleName("G"); - } elseif (!Zend_Auth::getInstance()->hasIdentity()) { + } + elseif (Zend_Session::isStarted() && !Zend_Auth::getInstance()->hasIdentity()) { + + //The controller uses sessions but we don't have an identity yet. // If we don't have an identity and we're making a RESTful request, // we need to do API key verification @@ -165,6 +169,7 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract } } } else { //We have a session/identity. + // If we have an identity and we're making a RESTful request, // we need to check the CSRF token if ($_SERVER['REQUEST_METHOD'] != "GET" && $request->getModuleName() == "rest") { @@ -222,11 +227,7 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract } private function verifyCSRFToken($token) { - $current_namespace = new Zend_Session_Namespace('csrf_namespace'); - $observed_csrf_token = $token; - $expected_csrf_token = $current_namespace->authtoken; - - return ($observed_csrf_token == $expected_csrf_token); + SecurityHelper::verifyCSRFToken($token); } private function verifyAPIKey() { diff --git a/airtime_mvc/application/controllers/plugins/ConversionTracking.php b/airtime_mvc/application/controllers/plugins/ConversionTracking.php index 09904828d..ae0fc2781 100644 --- a/airtime_mvc/application/controllers/plugins/ConversionTracking.php +++ b/airtime_mvc/application/controllers/plugins/ConversionTracking.php @@ -4,6 +4,10 @@ class Zend_Controller_Plugin_ConversionTracking extends Zend_Controller_Plugin_A { public function preDispatch(Zend_Controller_Request_Abstract $request) { + if (!Zend_Session::isStarted()) { + return; + } + //If user is a super admin and old plan level is set to trial.... if (Application_Common_GoogleAnalytics::didPaidConversionOccur($request)) { diff --git a/airtime_mvc/application/controllers/plugins/PageLayoutInitPlugin.php b/airtime_mvc/application/controllers/plugins/PageLayoutInitPlugin.php new file mode 100644 index 000000000..091bdcfcd --- /dev/null +++ b/airtime_mvc/application/controllers/plugins/PageLayoutInitPlugin.php @@ -0,0 +1,232 @@ +_bootstrap = $boostrap; + } + + /** + * Start the session depending on which controller your request is going to. + * We start the session explicitly here so that we can avoid starting sessions + * needlessly for (stateless) requests to the API. + * @param Zend_Controller_Request_Abstract $request + * @throws Zend_Session_Exception + */ + public function routeShutdown(Zend_Controller_Request_Abstract $request) + { + $controller = strtolower($request->getControllerName()); + $action = strtolower($request->getActionName()); + + //List of controllers where we don't need a session, and we don't need + //all the standard HTML / JS boilerplate. + if (!in_array($controller, array( + "index", //Radio Page + "api", + "auth", + "error", + "locale", + "upgrade", + 'whmcs-login', + "provisioning", + "embed" + )) + ) { + //Start the session + Zend_Session::start(); + Application_Model_Auth::pinSessionToClient(Zend_Auth::getInstance()); + + //localization configuration + Application_Model_Locale::configureLocalization(); + + $this->_initGlobals(); + $this->_initCsrfNamespace(); + $this->_initHeadLink(); + $this->_initHeadScript(); + $this->_initTitle(); + $this->_initTranslationGlobals(); + $this->_initViewHelpers(); + } + } + + protected function _initGlobals() + { + if (!Zend_Session::isStarted()) { + return; + } + + $view = $this->_bootstrap->getResource('view'); + $baseUrl = Application_Common_OsPath::getBaseDir(); + + $view->headScript()->appendScript("var baseUrl = '$baseUrl';"); + $this->_initTranslationGlobals($view); + + $user = Application_Model_User::GetCurrentUser(); + if (!is_null($user)) { + $userType = $user->getType(); + } else { + $userType = ""; + } + $view->headScript()->appendScript("var userType = '$userType';"); + } + + /** + * Create a global namespace to hold a session token for CSRF prevention + */ + protected function _initCsrfNamespace() + { + /* + if (!Zend_Session::isStarted()) { + return; + }*/ + + $csrf_namespace = new Zend_Session_Namespace('csrf_namespace'); + // Check if the token exists + if (!$csrf_namespace->authtoken) { + // If we don't have a token, regenerate it and set a 1 week timeout + // Should we log the user out here if the token is expired? + $csrf_namespace->authtoken = sha1(uniqid(rand(), 1)); + $csrf_namespace->setExpirationSeconds(168 * 60 * 60); + } + + //Here we are closing the session for writing because otherwise no requests + //in this session will be handled in parallel. This gives a major boost to the perceived performance + //of the application (page load times are more consistent, no lock contention). + session_write_close(); + //Zend_Session::writeClose(true); + } + + /** + * Ideally, globals should be written to a single js file once + * from a php init function. This will save us from having to + * reinitialize them every request + */ + private function _initTranslationGlobals() + { + $view = $this->_bootstrap->getResource('view'); + $view->headScript()->appendScript("var PRODUCT_NAME = '" . PRODUCT_NAME . "';"); + $view->headScript()->appendScript("var USER_MANUAL_URL = '" . USER_MANUAL_URL . "';"); + $view->headScript()->appendScript("var COMPANY_NAME = '" . COMPANY_NAME . "';"); + } + + protected function _initHeadLink() + { + $CC_CONFIG = Config::getConfig(); + + $view = $this->_bootstrap->getResource('view'); + + $baseUrl = Application_Common_OsPath::getBaseDir(); + + $view->headLink(array('rel' => 'icon', 'href' => $baseUrl . 'favicon.ico?' . $CC_CONFIG['airtime_version'], 'type' => 'image/x-icon'), 'PREPEND') + ->appendStylesheet($baseUrl . 'css/bootstrap.css?' . $CC_CONFIG['airtime_version']) + ->appendStylesheet($baseUrl . 'css/redmond/jquery-ui-1.8.8.custom.css?' . $CC_CONFIG['airtime_version']) + ->appendStylesheet($baseUrl . 'css/pro_dropdown_3.css?' . $CC_CONFIG['airtime_version']) + ->appendStylesheet($baseUrl . 'css/qtip/jquery.qtip.min.css?' . $CC_CONFIG['airtime_version']) + ->appendStylesheet($baseUrl . 'css/styles.css?' . $CC_CONFIG['airtime_version']) + ->appendStylesheet($baseUrl . 'css/masterpanel.css?' . $CC_CONFIG['airtime_version']) + ->appendStylesheet($baseUrl . 'css/tipsy/jquery.tipsy.css?' . $CC_CONFIG['airtime_version']); + } + + protected function _initHeadScript() + { + if (!Zend_Session::isStarted()) { + return; + } + + $CC_CONFIG = Config::getConfig(); + + $view = $this->_bootstrap->getResource('view'); + + $baseUrl = Application_Common_OsPath::getBaseDir(); + + $view->headScript()->appendFile($baseUrl . 'js/libs/jquery-1.8.3.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/libs/jquery-ui-1.8.24.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/bootstrap/bootstrap.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/libs/underscore-min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + + // ->appendFile($baseUrl . 'js/libs/jquery.stickyPanel.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/qtip/jquery.qtip.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/jplayer/jquery.jplayer.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/sprintf/sprintf-0.7-beta1.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/cookie/jquery.cookie.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/i18n/jquery.i18n.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'locale/general-translation-table?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'locale/datatables-translation-table?' . $CC_CONFIG['airtime_version'], 'text/javascript') + + ->appendScript("$.i18n.setDictionary(general_dict)") + ->appendScript("var baseUrl='$baseUrl'"); + + //These timezones are needed to adjust javascript Date objects on the client to make sense to the user's set timezone + //or the server's set timezone. + $serverTimeZone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); + $now = new DateTime("now", $serverTimeZone); + $offset = $now->format("Z") * -1; + $view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds"); + + if (class_exists("Zend_Auth", false) && Zend_Auth::getInstance()->hasIdentity()) { + $userTimeZone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); + $now = new DateTime("now", $userTimeZone); + $offset = $now->format("Z") * -1; + $view->headScript()->appendScript("var userTimezoneOffset = {$offset}; //in seconds"); + } + + //scripts for now playing bar + $view->headScript()->appendFile($baseUrl . 'js/airtime/airtime_bootstrap.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/airtime/dashboard/helperfunctions.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/airtime/dashboard/dashboard.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/airtime/dashboard/versiontooltip.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/tipsy/jquery.tipsy.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + + ->appendFile($baseUrl . 'js/airtime/common/common.js?' . $CC_CONFIG['airtime_version'], 'text/javascript') + ->appendFile($baseUrl . 'js/airtime/common/audioplaytest.js?' . $CC_CONFIG['airtime_version'], 'text/javascript'); + + $user = Application_Model_User::getCurrentUser(); + if (!is_null($user)) { + $userType = $user->getType(); + } else { + $userType = ""; + } + + $view->headScript()->appendScript("var userType = '$userType';"); + if (array_key_exists('REQUEST_URI', $_SERVER) //Doesn't exist for unit tests + && strpos($_SERVER['REQUEST_URI'], 'Dashboard/stream-player') === false + && strpos($_SERVER['REQUEST_URI'], 'audiopreview') === false + && $_SERVER['REQUEST_URI'] != "/") { + $plan_level = strval(Application_Model_Preference::GetPlanLevel()); + // Since the Hobbyist plan doesn't come with Live Chat support, don't enable it + if (Application_Model_Preference::GetLiveChatEnabled() && $plan_level !== 'hobbyist') { + $client_id = strval(Application_Model_Preference::GetClientId()); + $station_url = $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; + $view->headScript()->appendScript("var livechat_client_id = '$client_id';\n" . + "var livechat_plan_type = '$plan_level';\n" . + "var livechat_station_url = 'http://$station_url';"); + $view->headScript()->appendFile($baseUrl . 'js/airtime/common/livechat.js?' . $CC_CONFIG['airtime_version'], 'text/javascript'); + } + } + + /* + if (isset($CC_CONFIG['demo']) && $CC_CONFIG['demo'] == 1) { + $view->headScript()->appendFile($baseUrl.'js/libs/google-analytics.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + }*/ + } + + protected function _initViewHelpers() + { + $view = $this->_bootstrap->getResource('view'); + $view->addHelperPath(APPLICATION_PATH . 'views/helpers', 'Airtime_View_Helper'); + $view->assign('suspended', (Application_Model_Preference::getProvisioningStatus() == PROVISIONING_STATUS_SUSPENDED)); + } + + protected function _initTitle() + { + $view = $this->_bootstrap->getResource('view'); + $view->headTitle(Application_Model_Preference::GetHeadTitle()); + } +} \ No newline at end of file diff --git a/airtime_mvc/application/forms/BillingClient.php b/airtime_mvc/application/forms/BillingClient.php index 891cd4a95..7d604cad2 100644 --- a/airtime_mvc/application/forms/BillingClient.php +++ b/airtime_mvc/application/forms/BillingClient.php @@ -188,6 +188,10 @@ class Application_Form_BillingClient extends Zend_Form $passwordVerify->addValidator($notEmptyValidator); $this->addElement($passwordVerify); + $this->addElement('hash', 'csrf', array( + 'salt' => 'unique' + )); + $submit = new Zend_Form_Element_Submit("submit"); $submit->setIgnore(true) ->setLabel(_("Save")); diff --git a/airtime_mvc/application/forms/BillingUpgradeDowngrade.php b/airtime_mvc/application/forms/BillingUpgradeDowngrade.php index ecf9b4f50..5ff4ff4de 100644 --- a/airtime_mvc/application/forms/BillingUpgradeDowngrade.php +++ b/airtime_mvc/application/forms/BillingUpgradeDowngrade.php @@ -8,6 +8,10 @@ class Application_Form_BillingUpgradeDowngrade extends Zend_Form $csrf_element->setValue($csrf_namespace->authtoken)->setRequired('true')->removeDecorator('HtmlTag')->removeDecorator('Label'); $this->addElement($csrf_element); + $this->addElement('hash', 'csrf', array( + 'salt' => 'unique' + )); + $productPrices = array(); $productTypes = array(); list($productPrices, $productTypes) = Billing::getProductPricesAndTypes(); diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index c6e6fff49..cd439ddbe 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -10,7 +10,7 @@ class Application_Model_Preference { //pass in true so the check is made with the autoloader //we need this check because saas calls this function from outside Zend - if (!class_exists("Zend_Auth", true) || !Zend_Auth::getInstance()->hasIdentity()) { + if (!Zend_Session::isStarted() || !class_exists("Zend_Auth", true) || !Zend_Auth::getInstance()->hasIdentity()) { $userId = null; } else { $auth = Zend_Auth::getInstance(); @@ -142,10 +142,14 @@ class Application_Model_Preference try { - $userId = self::getUserId(); - - if ($isUserValue && is_null($userId)) - throw new Exception("User id can't be null for a user preference."); + $userId = null; + if ($isUserValue) { + //This is nested in here because so we can still use getValue() when the session hasn't started yet. + $userId = self::getUserId(); + if (is_null($userId)) { + throw new Exception("User id can't be null for a user preference."); + } + } // If the value is already cached, return it $res = $cache->fetch($key, $isUserValue, $userId); @@ -192,7 +196,7 @@ class Application_Model_Preference } catch (Exception $e) { header('HTTP/1.0 503 Service Unavailable'); - Logging::info("Could not connect to database: ".$e->getMessage()); + Logging::info("Could not connect to database: ".$e); exit; } }