diff --git a/airtime_mvc/application/controllers/LoginController.php b/airtime_mvc/application/controllers/LoginController.php index 1be71d965..fa6ea36bb 100644 --- a/airtime_mvc/application/controllers/LoginController.php +++ b/airtime_mvc/application/controllers/LoginController.php @@ -61,6 +61,7 @@ class LoginController extends Zend_Controller_Action $result = $auth->authenticate($authAdapter); if ($result->isValid()) { + Zend_Session::regenerateId(); //all info about this user from the login table omit only the password $userInfo = $authAdapter->getResultRowObject(null, 'password'); @@ -81,6 +82,7 @@ class LoginController extends Zend_Controller_Action $auth = Zend_Auth::getInstance(); $result = $auth->authenticate($authAdapter); if ($result->isValid()) { + Zend_Session::regenerateId(); //set the user locale in case user changed it in when logging in Application_Model_Preference::SetUserLocale($locale); diff --git a/airtime_mvc/application/modules/rest/controllers/MediaController.php b/airtime_mvc/application/modules/rest/controllers/MediaController.php index dd0497745..1483de816 100644 --- a/airtime_mvc/application/modules/rest/controllers/MediaController.php +++ b/airtime_mvc/application/modules/rest/controllers/MediaController.php @@ -129,6 +129,15 @@ class Rest_MediaController extends Zend_Rest_Controller public function postAction() { + /* If the user presents a valid API key, we don't check CSRF tokens. + CSRF tokens are only used for session based authentication. + */ + if(!$this->verifyAPIKey()){ + if(!$this->verifyCSRFToken($this->_getParam('csrf_token'))){ + return; + } + } + if (!$this->verifyAuth(true, true)) { return; @@ -294,6 +303,21 @@ class Rest_MediaController extends Zend_Rest_Controller } return $id; } + + private function verifyCSRFToken($token){ + $current_namespace = new Zend_Session_Namespace('csrf_namespace'); + $observed_csrf_token = $token; + $expected_csrf_token = $current_namespace->authtoken; + + if($observed_csrf_token == $expected_csrf_token){ + return true; + }else{ + $resp = $this->getResponse(); + $resp->setHttpResponseCode(401); + $resp->appendBody("ERROR: Token Missmatch."); + return false; + } + } private function verifyAuth($checkApiKey, $checkSession) { diff --git a/airtime_mvc/application/views/scripts/form/edit-user.phtml b/airtime_mvc/application/views/scripts/form/edit-user.phtml index 7ffaaecbb..a36e1aaf9 100644 --- a/airtime_mvc/application/views/scripts/form/edit-user.phtml +++ b/airtime_mvc/application/views/scripts/form/edit-user.phtml @@ -166,10 +166,7 @@ - element->getElement('csrf') ?> - - diff --git a/airtime_mvc/application/views/scripts/plupload/index.phtml b/airtime_mvc/application/views/scripts/plupload/index.phtml index a5866f933..94d5b3264 100644 --- a/airtime_mvc/application/views/scripts/plupload/index.phtml +++ b/airtime_mvc/application/views/scripts/plupload/index.phtml @@ -11,8 +11,8 @@ } ?>
quotaLimitReached) { ?> class="hidden" > - form->getElement('csrf') ?> -
+ form->getElement('csrf') ?> +
diff --git a/python_apps/airtime_analyzer/airtime_analyzer/filemover_analyzer.py b/python_apps/airtime_analyzer/airtime_analyzer/filemover_analyzer.py index de296e092..79c288124 100644 --- a/python_apps/airtime_analyzer/airtime_analyzer/filemover_analyzer.py +++ b/python_apps/airtime_analyzer/airtime_analyzer/filemover_analyzer.py @@ -42,14 +42,16 @@ class FileMoverAnalyzer(Analyzer): # TODO: Also, handle the case where the move fails and write some code # to possibly move the file to problem_files. - max_dir_len = 32 - max_file_len = 32 + max_dir_len = 48 + max_file_len = 48 final_file_path = import_directory + orig_file_basename, orig_file_extension = os.path.splitext(original_filename) if metadata.has_key("artist_name"): final_file_path += "/" + metadata["artist_name"][0:max_dir_len] # truncating with array slicing if metadata.has_key("album_title"): - final_file_path += "/" + metadata["album_title"][0:max_dir_len] - final_file_path += "/" + original_filename[0:max_file_len] + final_file_path += "/" + metadata["album_title"][0:max_dir_len] + # Note that orig_file_extension includes the "." already + final_file_path += "/" + orig_file_basename[0:max_file_len] + orig_file_extension #Ensure any redundant slashes are stripped final_file_path = os.path.normpath(final_file_path) diff --git a/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py b/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py index 97613e81f..9b890321c 100644 --- a/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py +++ b/python_apps/airtime_analyzer/airtime_analyzer/message_listener.py @@ -120,8 +120,13 @@ class MessageListener: def disconnect_from_messaging_server(self): '''Stop consuming RabbitMQ messages and disconnect''' - self._channel.stop_consuming() - self._connection.close() + # If you try to close a connection that's already closed, you're going to have a bad time. + # We're breaking EAFP because this can be called multiple times depending on exception + # handling flow here. + if not self._channel.is_closed and not self._channel.is_closing: + self._channel.stop_consuming() + if not self._connection.is_closed and not self._connection.is_closing: + self._connection.close() def graceful_shutdown(self, signum, frame): '''Disconnect and break out of the message listening loop''' diff --git a/python_apps/airtime_analyzer/airtime_analyzer/status_reporter.py b/python_apps/airtime_analyzer/airtime_analyzer/status_reporter.py index 7dcfa5d44..ebf9a12d5 100644 --- a/python_apps/airtime_analyzer/airtime_analyzer/status_reporter.py +++ b/python_apps/airtime_analyzer/airtime_analyzer/status_reporter.py @@ -38,9 +38,9 @@ def process_http_requests(ipc_queue, http_retry_queue_path): # retried later: retry_queue = collections.deque() shutdown = False - - # Unpickle retry_queue from disk so that we won't have lost any uploads - # if airtime_analyzer is shut down while the web server is down or unreachable, + + # Unpickle retry_queue from disk so that we won't have lost any uploads + # if airtime_analyzer is shut down while the web server is down or unreachable, # and there were failed HTTP requests pending, waiting to be retried. try: with open(http_retry_queue_path, 'rb') as pickle_file: @@ -57,33 +57,42 @@ def process_http_requests(ipc_queue, http_retry_queue_path): logging.error("Failed to unpickle %s. Continuing..." % http_retry_queue_path) pass - - while not shutdown: + while True: try: - request = ipc_queue.get(block=True, timeout=5) - if isinstance(request, str) and request == "shutdown": # Bit of a cheat - shutdown = True - break - if not isinstance(request, PicklableHttpRequest): - raise TypeError("request must be a PicklableHttpRequest. Was of type " + type(request).__name__) - except Queue.Empty: - request = None - - # If there's no new HTTP request we need to execute, let's check our "retry - # queue" and see if there's any failed HTTP requests we can retry: - if request: - send_http_request(request, retry_queue) - else: - # Using a for loop instead of while so we only iterate over all the requests once! - for i in range(len(retry_queue)): - request = retry_queue.popleft() - send_http_request(request, retry_queue) + while not shutdown: + try: + request = ipc_queue.get(block=True, timeout=5) + if isinstance(request, str) and request == "shutdown": # Bit of a cheat + shutdown = True + break + if not isinstance(request, PicklableHttpRequest): + raise TypeError("request must be a PicklableHttpRequest. Was of type " + type(request).__name__) + except Queue.Empty: + request = None + + # If there's no new HTTP request we need to execute, let's check our "retry + # queue" and see if there's any failed HTTP requests we can retry: + if request: + send_http_request(request, retry_queue) + else: + # Using a for loop instead of while so we only iterate over all the requests once! + for i in range(len(retry_queue)): + request = retry_queue.popleft() + send_http_request(request, retry_queue) + + logging.info("Shutting down status_reporter") + # Pickle retry_queue to disk so that we don't lose uploads if we're shut down while + # while the web server is down or unreachable. + with open(http_retry_queue_path, 'wb') as pickle_file: + pickle.dump(retry_queue, pickle_file) + except Exception as e: # Terrible top-level exception handler to prevent the thread from dying, just in case. + if shutdown: + return + logging.exception("Unhandled exception in StatusReporter") + logging.exception(e) + logging.info("Restarting StatusReporter thread") + time.sleep(2) # Throttle it - logging.info("Shutting down status_reporter") - # Pickle retry_queue to disk so that we don't lose uploads if we're shut down while - # while the web server is down or unreachable. - with open(http_retry_queue_path, 'wb') as pickle_file: - pickle.dump(retry_queue, pickle_file) def send_http_request(picklable_request, retry_queue): if not isinstance(picklable_request, PicklableHttpRequest): @@ -134,11 +143,11 @@ def is_web_server_broken(url): test_req = requests.get(url) test_req.raise_for_status() except Exception as e: - return true + return True else: # The request worked fine, so the web server and Airtime are still up. - return false - return false + return False + return False def alert_hung_request(): diff --git a/python_apps/airtime_analyzer/install/upstart/airtime_analyzer.conf b/python_apps/airtime_analyzer/install/upstart/airtime_analyzer.conf index 696c8b9a9..eeeb45797 100644 --- a/python_apps/airtime_analyzer/install/upstart/airtime_analyzer.conf +++ b/python_apps/airtime_analyzer/install/upstart/airtime_analyzer.conf @@ -9,14 +9,16 @@ respawn setuid www-data setgid www-data -expect fork +#expect fork env LANG='en_US.UTF-8' env LC_ALL='en_US.UTF-8' -script - airtime_analyzer -end script +#script +# airtime_analyzer +#end script + +exec airtime_analyzer