SAAS-1085: Optimization - Don't start sessions unless we actually need them.

This commit is contained in:
Albert Santoni 2015-09-25 10:41:51 -04:00
parent a86e3ed4a8
commit c03e9cbe9a
13 changed files with 334 additions and 197 deletions

View file

@ -35,6 +35,7 @@ require_once __DIR__.'/services/CeleryService.php';
require_once __DIR__.'/services/SoundcloudService.php'; require_once __DIR__.'/services/SoundcloudService.php';
require_once __DIR__.'/forms/helpers/ValidationTypes.php'; require_once __DIR__.'/forms/helpers/ValidationTypes.php';
require_once __DIR__.'/forms/helpers/CustomDecorators.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/RabbitMqPlugin.php';
require_once __DIR__.'/controllers/plugins/Maintenance.php'; require_once __DIR__.'/controllers/plugins/Maintenance.php';
require_once __DIR__.'/controllers/plugins/ConversionTracking.php'; require_once __DIR__.'/controllers/plugins/ConversionTracking.php';
@ -52,20 +53,21 @@ if (array_key_exists("REQUEST_URI", $_SERVER) && (stripos($_SERVER["REQUEST_URI"
die(); die();
} }
Zend_Session::setOptions(array('strict' => true));
Config::setAirtimeVersion(); Config::setAirtimeVersion();
require_once (CONFIG_PATH . 'navigation.php'); require_once (CONFIG_PATH . 'navigation.php');
Zend_Validate::setDefaultNamespaces("Zend"); Zend_Validate::setDefaultNamespaces("Zend");
Application_Model_Auth::pinSessionToClient(Zend_Auth::getInstance());
$front = Zend_Controller_Front::getInstance(); $front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new RabbitMqPlugin()); $front->registerPlugin(new RabbitMqPlugin());
$front->registerPlugin(new Zend_Controller_Plugin_ConversionTracking()); $front->registerPlugin(new Zend_Controller_Plugin_ConversionTracking());
$front->throwExceptions(false); $front->throwExceptions(false);
//localization configuration
Application_Model_Locale::configureLocalization();
/* The bootstrap class should only be used to initialize actions that return a view. /* The bootstrap class should only be used to initialize actions that return a view.
Actions that return JSON will not use the bootstrap class! */ 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'); $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() { protected function _initTasks() {
/* We need to wrap this here so that we aren't checking when we're running the unit test suite /* 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() protected function _initZFDebug()
{ {
@ -293,6 +139,7 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{ {
$front = Zend_Controller_Front::getInstance(); $front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new Zend_Controller_Plugin_Maintenance()); $front->registerPlugin(new Zend_Controller_Plugin_Maintenance());
$front->registerPlugin(new PageLayoutInitPlugin($this));
} }
} }

View file

@ -13,16 +13,11 @@ class SecurityHelper {
return $arr; return $arr;
} }
public static function verifyAjaxCSRFToken($observedToken) { public static function verifyCSRFToken($observedToken) {
$current_namespace = new Zend_Session_Namespace('csrf_namespace'); $current_namespace = new Zend_Session_Namespace('csrf_namespace');
$observed_csrf_token = $observedToken; $observed_csrf_token = $observedToken;
$expected_csrf_token = $current_namespace->authtoken; $expected_csrf_token = $current_namespace->authtoken;
if ($observed_csrf_token == $expected_csrf_token){ return ($observed_csrf_token == $expected_csrf_token);
return true;
}else{
return false;
}
} }
} }

View file

@ -114,6 +114,9 @@ final class TaskManager {
* otherwise false * otherwise false
*/ */
private function _isUserSessionRequest() { private function _isUserSessionRequest() {
if (!Zend_Session::isStarted()) {
return false;
}
$auth = Zend_Auth::getInstance(); $auth = Zend_Auth::getInstance();
$data = $auth->getStorage()->read(); $data = $auth->getStorage()->read();
return !empty($data); return !empty($data);

View file

@ -11,6 +11,8 @@ class ApiController extends Zend_Controller_Action
public function init() public function init()
{ {
//Ignore API key and session authentication for these APIs:
$ignoreAuth = array("live-info", $ignoreAuth = array("live-info",
"live-info-v2", "live-info-v2",
"week-info", "week-info",
@ -25,6 +27,11 @@ class ApiController extends Zend_Controller_Action
"show-logo" "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(); $params = $this->getRequest()->getParams();
if (!in_array($params['action'], $ignoreAuth)) { if (!in_array($params['action'], $ignoreAuth)) {
$this->checkAuth(); $this->checkAuth();
@ -73,13 +80,23 @@ class ApiController extends Zend_Controller_Action
$CC_CONFIG = Config::getConfig(); $CC_CONFIG = Config::getConfig();
$api_key = $this->_getParam('api_key'); $api_key = $this->_getParam('api_key');
if (!in_array($api_key, $CC_CONFIG["apiKey"]) && if (in_array($api_key, $CC_CONFIG["apiKey"])) {
is_null(Zend_Auth::getInstance()->getStorage()->read())) { return true;
header('HTTP/1.0 401 Unauthorized');
print _('You are not allowed to access this resource.');
exit;
} }
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() public function versionAction()

View file

@ -25,7 +25,10 @@ class BillingController extends Zend_Controller_Action {
$baseUrl = Application_Common_OsPath::getBaseDir(); $baseUrl = Application_Common_OsPath::getBaseDir();
$this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']);
Billing::ensureClientIdIsValid(); Billing::ensureClientIdIsValid();
//Zend's CSRF token element requires the session to be open for writing
session_start();
$request = $this->getRequest(); $request = $this->getRequest();
$form = new Application_Form_BillingUpgradeDowngrade(); $form = new Application_Form_BillingUpgradeDowngrade();
@ -219,7 +222,10 @@ class BillingController extends Zend_Controller_Action {
$CC_CONFIG = Config::getConfig(); $CC_CONFIG = Config::getConfig();
$baseUrl = Application_Common_OsPath::getBaseDir(); $baseUrl = Application_Common_OsPath::getBaseDir();
$this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']); $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(); $request = $this->getRequest();
$form = new Application_Form_BillingClient(); $form = new Application_Form_BillingClient();
Billing::ensureClientIdIsValid(); Billing::ensureClientIdIsValid();

View file

@ -8,8 +8,14 @@ class LoginController extends Zend_Controller_Action
public function init() public function init()
{ {
//Open the session for writing, because we close it for writing by default in Bootstrap.php as an optimization. $CC_CONFIG = Config::getConfig();
session_start(); $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() 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. //Enable AJAX requests from www.airtime.pro for the sign-in process.
CORSHelper::enableATProCrossOriginRequests($request, $response); CORSHelper::enableATProCrossOriginRequests($request, $response);
Application_Model_Locale::configureLocalization($request->getcookie('airtime_locale', $stationLocale)); Application_Model_Locale::configureLocalization($request->getcookie('airtime_locale', $stationLocale));
$auth = Zend_Auth::getInstance();
if (Zend_Session::isStarted()) {
if ($auth->hasIdentity()) { //Open the session for writing, because we close it for writing by default in Bootstrap.php as an optimization.
$this->_redirect('Showbuilder'); session_start();
$auth = Zend_Auth::getInstance();
$auth->getStorage();
if ($auth->hasIdentity()) {
$this->_redirect('Showbuilder');
}
} }
//uses separate layout without a navigation. //uses separate layout without a navigation.
@ -43,6 +56,10 @@ class LoginController extends Zend_Controller_Action
$message = _("Please enter your username and password."); $message = _("Please enter your username and password.");
if ($request->isPost()) { 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. // if the post contains recaptcha field, which means form had recaptcha field.
// Hence add the element for validation. // Hence add the element for validation.
if (array_key_exists('recaptcha_response_field', $request->getPost())) { if (array_key_exists('recaptcha_response_field', $request->getPost())) {
@ -117,6 +134,9 @@ class LoginController extends Zend_Controller_Action
public function logoutAction() 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 = Zend_Auth::getInstance();
$auth->clearIdentity(); $auth->clearIdentity();
// Unset all session variables relating to CSRF prevention on logout // Unset all session variables relating to CSRF prevention on logout

View file

@ -134,7 +134,7 @@ class PreferenceController extends Zend_Controller_Action
// Remove reliance on .phtml files to render requests // Remove reliance on .phtml files to render requests
$this->_helper->viewRenderer->setNoRender(true); $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'); Logging::error(__FILE__ . ': Invalid CSRF token');
$this->_helper->json->sendJson(array("jsonrpc" => "2.0", "valid" => false, "error" => "CSRF token did not match.")); $this->_helper->json->sendJson(array("jsonrpc" => "2.0", "valid" => false, "error" => "CSRF token did not match."));
return; return;
@ -486,7 +486,7 @@ class PreferenceController extends Zend_Controller_Action
$this->view->layout()->disableLayout(); $this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true); $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'); Logging::error(__FILE__ . ': Invalid CSRF token');
$this->_helper->json->sendJson(array("jsonrpc" => "2.0", "valid" => false, "error" => "CSRF token did not match.")); $this->_helper->json->sendJson(array("jsonrpc" => "2.0", "valid" => false, "error" => "CSRF token did not match."));
return; return;

View file

@ -109,9 +109,10 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
public function preDispatch(Zend_Controller_Request_Abstract $request) public function preDispatch(Zend_Controller_Request_Abstract $request)
{ {
$controller = strtolower($request->getControllerName()); $controller = strtolower($request->getControllerName());
Application_Model_Auth::pinSessionToClient(Zend_Auth::getInstance());
if (in_array($controller, array( if (in_array($controller, array(
"index",
"login",
"api", "api",
"auth", "auth",
"error", "error",
@ -123,7 +124,10 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
))) )))
{ {
$this->setRoleName("G"); $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, // If we don't have an identity and we're making a RESTful request,
// we need to do API key verification // 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. } else { //We have a session/identity.
// If we have an identity and we're making a RESTful request, // If we have an identity and we're making a RESTful request,
// we need to check the CSRF token // we need to check the CSRF token
if ($_SERVER['REQUEST_METHOD'] != "GET" && $request->getModuleName() == "rest") { 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) { private function verifyCSRFToken($token) {
$current_namespace = new Zend_Session_Namespace('csrf_namespace'); SecurityHelper::verifyCSRFToken($token);
$observed_csrf_token = $token;
$expected_csrf_token = $current_namespace->authtoken;
return ($observed_csrf_token == $expected_csrf_token);
} }
private function verifyAPIKey() { private function verifyAPIKey() {

View file

@ -4,6 +4,10 @@ class Zend_Controller_Plugin_ConversionTracking extends Zend_Controller_Plugin_A
{ {
public function preDispatch(Zend_Controller_Request_Abstract $request) 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 user is a super admin and old plan level is set to trial....
if (Application_Common_GoogleAnalytics::didPaidConversionOccur($request)) if (Application_Common_GoogleAnalytics::didPaidConversionOccur($request))
{ {

View file

@ -0,0 +1,232 @@
<?php
/** Our standard page layout initialization has to be done via a plugin
* because some of it requires session variables, and some of the routes
* run without a session (like API calls). This is an optimization because
* starting the session adds a fair amount of overhead.
*/
class PageLayoutInitPlugin extends Zend_Controller_Plugin_Abstract
{
protected $_bootstrap = null;
public function __construct($boostrap) {
$this->_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());
}
}

View file

@ -188,6 +188,10 @@ class Application_Form_BillingClient extends Zend_Form
$passwordVerify->addValidator($notEmptyValidator); $passwordVerify->addValidator($notEmptyValidator);
$this->addElement($passwordVerify); $this->addElement($passwordVerify);
$this->addElement('hash', 'csrf', array(
'salt' => 'unique'
));
$submit = new Zend_Form_Element_Submit("submit"); $submit = new Zend_Form_Element_Submit("submit");
$submit->setIgnore(true) $submit->setIgnore(true)
->setLabel(_("Save")); ->setLabel(_("Save"));

View file

@ -8,6 +8,10 @@ class Application_Form_BillingUpgradeDowngrade extends Zend_Form
$csrf_element->setValue($csrf_namespace->authtoken)->setRequired('true')->removeDecorator('HtmlTag')->removeDecorator('Label'); $csrf_element->setValue($csrf_namespace->authtoken)->setRequired('true')->removeDecorator('HtmlTag')->removeDecorator('Label');
$this->addElement($csrf_element); $this->addElement($csrf_element);
$this->addElement('hash', 'csrf', array(
'salt' => 'unique'
));
$productPrices = array(); $productPrices = array();
$productTypes = array(); $productTypes = array();
list($productPrices, $productTypes) = Billing::getProductPricesAndTypes(); list($productPrices, $productTypes) = Billing::getProductPricesAndTypes();

View file

@ -10,7 +10,7 @@ class Application_Model_Preference
{ {
//pass in true so the check is made with the autoloader //pass in true so the check is made with the autoloader
//we need this check because saas calls this function from outside Zend //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; $userId = null;
} else { } else {
$auth = Zend_Auth::getInstance(); $auth = Zend_Auth::getInstance();
@ -142,10 +142,14 @@ class Application_Model_Preference
try { try {
$userId = self::getUserId(); $userId = null;
if ($isUserValue) {
if ($isUserValue && is_null($userId)) //This is nested in here because so we can still use getValue() when the session hasn't started yet.
throw new Exception("User id can't be null for a user preference."); $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 // If the value is already cached, return it
$res = $cache->fetch($key, $isUserValue, $userId); $res = $cache->fetch($key, $isUserValue, $userId);
@ -192,7 +196,7 @@ class Application_Model_Preference
} }
catch (Exception $e) { catch (Exception $e) {
header('HTTP/1.0 503 Service Unavailable'); 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; exit;
} }
} }