def get_current_user(self): """Set current user, authenticated via HTTP basic auth""" auth_header = self.request.headers.get('Authorization') if auth_header is None or not auth_header.startswith('Basic '): return None auth_decoded = base64.decodestring(auth_header[6:]) if not auth_decoded.count(':'): return None login, password = auth_decoded.split(':', 2) if not login or not password: return None try: encrypted_password = self.mongo.user.find_one({'_id': login}, {'password': 1})['password'] except: return None if encrypted_password and Password.verify(password, encrypted_password): return login else: return None
def do_POST(self): # Get the parameters of the POST request params = query.Parameters(self) print "" print Storyboard.SEPARATOR2 if Storyboard.ENABLE_PASSWORD: print( "* INFO: trngsrv: Request POST parameters: [not shown because password use is enabled]" ) else: print( "* INFO: trngsrv: Request POST parameters: {}".format(params)) # Get the values of the parameters for given keys user_id = params.get(query.Parameters.USER) password = params.get(query.Parameters.PASSWORD) action = params.get(query.Parameters.ACTION) language = params.get(query.Parameters.LANG) instance_count = params.get(query.Parameters.COUNT) ttype = params.get(query.Parameters.TYPE) scenario = params.get(query.Parameters.SCENARIO) level = params.get(query.Parameters.LEVEL) range_id = params.get(query.Parameters.RANGE_ID) if DEBUG: print Storyboard.SEPARATOR1 print "POST PARAMETERS:" print Storyboard.SEPARATOR1 print "USER: %s" % (user_id) if password: print "PASSWORD: ******" print "ACTION: %s" % (action) print "LANGUAGE: %s" % (language) print "COUNT: %s" % (instance_count) print "TYPE: %s" % (ttype) print "SCENARIO: %s" % (scenario) print "LEVEL: %s" % (level) print "RANGE_ID: %s" % (range_id) print Storyboard.SEPARATOR1 ## Verify user information # Get user information from YAML file # Note: Only reading data that is (potentially) modified externally => # no need for synchronization user_info = userinfo.UserInfo() if not user_info.parse_YAML_file(DATABASE_DIR + USERS_FILE): self.respond_error(Storyboard.USER_SETTINGS_LOADING_ERROR) return if DEBUG: user_info.pretty_print() # Check that user id is valid if not user_id: self.respond_error(Storyboard.USER_ID_MISSING_ERROR) return user_obj = user_info.get_user(user_id) if not user_obj: self.respond_error(Storyboard.USER_ID_INVALID_ERROR) return # Check password (if enabled) if Storyboard.ENABLE_PASSWORD: # Check whether password exists in database for current user if not user_obj.password: self.respond_error( Storyboard.USER_PASSWORD_NOT_IN_DATABASE_ERROR) return # If a password was provided, verify that it matches the encrypted one from the database if password: if not Password.verify(password, user_obj.password): self.respond_error( Storyboard.USER_ID_PASSWORD_INVALID_ERROR) return else: self.respond_error(Storyboard.USER_PASSWORD_MISSING_ERROR) return ## Verify action information # Check that action is valid if not action: self.respond_error(Storyboard.ACTION_MISSING_ERROR) return if action not in self.VALID_ACTIONS: self.respond_error(Storyboard.ACTION_INVALID_ERROR) return # Create training database content information object training_info = trnginfo.TrainingInfo() # Check that language is valid if not language: self.respond_error(Storyboard.LANGUAGE_MISSING_ERROR) return if language not in self.VALID_LANGUAGES: self.respond_error(Storyboard.LANGUAGE_INVALID_ERROR) return # Select the scenario information file based on the # requested language if language == query.Parameters.JA: training_settings_file = DATABASE_DIR + SCENARIOS_FILE_JA else: training_settings_file = DATABASE_DIR + SCENARIOS_FILE_EN if DEBUG: print "* DEBUG: trngsrv: Read training settings from '%s'..." % ( training_settings_file) # Note: Only reading data that is (potentially) modified externally => # no need for synchronization result = training_info.parse_YAML_file(training_settings_file) if not result: self.respond_error(Storyboard.TRAINING_SETTINGS_LOADING_ERROR) return if DEBUG: training_info.pretty_print() # If we reached this point, it means processing was successful # => act according to each action #################################################################### # Fetch content action # Note: Only reading data that is (potentially) modified externally => # no need for synchronization if action == query.Parameters.FETCH_CONTENT: # Convert the training info to the external JSON # representation that will be provided to the client response_data = training_info.get_JSON_representation() #################################################################### # Create training action # Note: Requires synchronized access to active sessions list elif action == query.Parameters.CREATE_TRAINING: if not instance_count: self.respond_error(Storyboard.INSTANCE_COUNT_MISSING_ERROR) return try: instance_count_value = int(instance_count) except ValueError as error: self.respond_error(Storyboard.INSTANCE_COUNT_INVALID_ERROR) return if not ttype: self.respond_error(Storyboard.TRAINING_TYPE_MISSING_ERROR) return if not scenario: self.respond_error(Storyboard.SCENARIO_NAME_MISSING_ERROR) return if not level: self.respond_error(Storyboard.LEVEL_NAME_MISSING_ERROR) return # Synchronize access to active sessions list and related variables self.lock_active_sessions.acquire() try: cyber_range_id = self.generate_cyber_range_id( self.pending_sessions) if cyber_range_id: # Convert to string for internal representation cyber_range_id = str(cyber_range_id) self.pending_sessions.append(cyber_range_id) print "* INFO: trngsrv: Allocated session with ID #%s." % ( cyber_range_id) else: self.respond_error(Storyboard.SESSION_ALLOCATION_ERROR) return finally: self.lock_active_sessions.release() ######################################## # Handle content upload content_file_name = training_info.get_content_name(scenario, level) if content_file_name == None: self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.CONTENT_IDENTIFICATION_ERROR) return content_file_name = DATABASE_DIR + content_file_name if DEBUG: print "* DEBUG: trngsrv: Training content file: %s" % ( content_file_name) # Open the content file try: content_file = open(content_file_name, "r") content_file_content = content_file.read() content_file.close() except IOError as error: print "* ERROR: trngsrv: File error: %s." % (error) self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.CONTENT_LOADING_ERROR) return try: # Note: creating a dictionary as below does not # preserve the order of the parameters, but this has # no negative influence in our implementation query_tuples = { query.Parameters.USER: user_id, query.Parameters.ACTION: query.Parameters.UPLOAD_CONTENT, query.Parameters.DESCRIPTION_FILE: content_file_content, query.Parameters.RANGE_ID: cyber_range_id } query_params = urllib.urlencode(query_tuples) print "* INFO: trngsrv: Send upload request to content server %s." % ( CONTENT_SERVER_URL) if DEBUG: print "* DEBUG: trngsrv: POST parameters: %s" % ( query_params) data_stream = urllib.urlopen(CONTENT_SERVER_URL, query_params) data = data_stream.read() if DEBUG: print "* DEBUG: trngsrv: Content server response body: %s" % ( data) (status, activity_id) = query.Response.parse_server_response(data) if DEBUG: print( "* DEBUG: trngsrv: Response status: {}".format(status)) print("* DEBUG: trngsrv: Response activity_id: {}".format( activity_id)) if status == Storyboard.SERVER_STATUS_SUCCESS: # Store activity id in session description pass else: print "* ERROR: trngsrv: Content upload error." self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.CONTENT_UPLOAD_ERROR) return # Save the response data contsrv_response = data except IOError as error: print "* ERROR: trngsrv: URL error: %s." % (error) self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.CONTENT_SERVER_ERROR) return ######################################## # Handle instantiation spec_file_name = training_info.get_specification_name( scenario, level) if spec_file_name == None: self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.TEMPLATE_IDENTIFICATION_ERROR) return spec_file_name = DATABASE_DIR + spec_file_name if DEBUG: print "* DEBUG: trngsrv: Scenario specification file: %s" % ( spec_file_name) # Open the specification file (template) try: spec_file = open(spec_file_name, "r") spec_file_content = spec_file.read() spec_file.close() except IOError as error: print "* ERROR: trngsrv: File error: %s." % (error) self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.TEMPLATE_LOADING_ERROR) return # Do instantiation try: # Replace variables in the specification file spec_file_content = user_obj.replace_variables( spec_file_content, cyber_range_id, instance_count_value) # Note: creating a dictionary as below does not # preserve the order of the parameters, but this has # no negative influence in our implementation query_tuples = { query.Parameters.USER: user_id, query.Parameters.ACTION: query.Parameters.INSTANTIATE_RANGE, query.Parameters.DESCRIPTION_FILE: spec_file_content, query.Parameters.RANGE_ID: cyber_range_id } query_params = urllib.urlencode(query_tuples) print "* INFO: trngsrv: Send instantiate request to instantiation server %s." % ( INSTANTIATION_SERVER_URL) if DEBUG: print "* DEBUG: trngsrv: POST parameters: %s" % ( query_params) data_stream = urllib.urlopen(INSTANTIATION_SERVER_URL, query_params) data = data_stream.read() if DEBUG: print "* DEBUG: trngsrv: Instantiation server response body: %s" % ( data) # Remove pending session self.removePendingSession(cyber_range_id) (status, message) = query.Response.parse_server_response(data) if DEBUG: print "* DEBUG: trngsrv: Response status:", status print "* DEBUG: trngsrv: Response message:", message if status == Storyboard.SERVER_STATUS_SUCCESS: session_name = "Training Session #%s" % (cyber_range_id) crt_time = time.asctime() print "* INFO: trngsrv: Instantiation successful => save training session: %s (time: %s)." % ( session_name, crt_time) # Synchronize access to active sessions list self.lock_active_sessions.acquire() try: # Read session info session_info = sessinfo.SessionInfo() session_info.parse_YAML_file(ACTIVE_SESSIONS_FILE) # Add new session and save to file # Scenarios and levels should be given as arrays, # so we convert values to arrays when passing arguments session_info.add_session(session_name, cyber_range_id, user_id, crt_time, ttype, [scenario], [level], language, instance_count, activity_id) session_info.write_YAML_file(ACTIVE_SESSIONS_FILE) finally: self.lock_active_sessions.release() else: print "* ERROR: trngsrv: Range instantiation error." self.respond_error(Storyboard.INSTANTIATION_ERROR) return # Save the response data instsrv_response = data if DEBUG: print "* DEBUG: trngsrv: contsrv response: %s" % ( contsrv_response) print "* DEBUG: trngsrv: instsrv response: %s" % ( instsrv_response) # Prepare the response as a message # TODO: Should create a function to handle this if message: response_data = '[{{"{0}": "{1}"}}]'.format( Storyboard.SERVER_MESSAGE_KEY, message) else: response_data = None except IOError as error: print "* ERROR: trngsrv: URL error: %s." % (error) self.removePendingSession(cyber_range_id) self.respond_error(Storyboard.INSTANTIATION_SERVER_ERROR) return #################################################################### # Retrieve saved training configurations action # Note: Requires synchronized access to saved configurations list elif action == query.Parameters.GET_CONFIGURATIONS: self.lock_saved_configurations.acquire() try: # Read session info session_info = sessinfo.SessionInfo() session_info.parse_YAML_file(SAVED_CONFIGURATIONS_FILE) # TODO: Catch error in operation above finally: self.lock_saved_configurations.release() # Convert the training session info to the external JSON # representation that will be provided to the client response_data = session_info.get_JSON_representation(user_id) #################################################################### # Retrieve active training sessions action # Note: Requires synchronized access to active sessions list elif action == query.Parameters.GET_SESSIONS: self.lock_active_sessions.acquire() try: # Read session info session_info = sessinfo.SessionInfo() session_info.parse_YAML_file(ACTIVE_SESSIONS_FILE) # TODO: Catch error in operation above finally: self.lock_active_sessions.release() # Convert the training session info to the external JSON # representation that will be provided to the client response_data = session_info.get_JSON_representation(user_id) #################################################################### # End training action # Note: Requires synchronized access to active sessions list elif action == query.Parameters.END_TRAINING: if not range_id: print "* ERROR: trngsrv: %s." % ( Storyboard.SESSION_ID_MISSING_ERROR) self.respond_error(Storyboard.SESSION_ID_MISSING_ERROR) return activity_id = None self.lock_active_sessions.acquire() try: activity_id = self.check_range_id_exists(range_id, user_id) if not activity_id: print "* ERROR: trngsrv: Session with ID " + range_id + " doesn't exist for user " + user_id error_msg = Storyboard.SESSION_ID_INVALID_ERROR + ": " + range_id self.respond_error(error_msg) return finally: self.lock_active_sessions.release() ######################################## # Handle content removal try: # Note: Creating a dictionary as below does not # preserve the order of the parameters, but this has # no negative influence in our implementation query_tuples = { query.Parameters.USER: user_id, query.Parameters.ACTION: query.Parameters.REMOVE_CONTENT, query.Parameters.RANGE_ID: range_id, query.Parameters.ACTIVITY_ID: activity_id } query_params = urllib.urlencode(query_tuples) print "* INFO: trngsrv: Send removal request to content server %s." % ( CONTENT_SERVER_URL) print "* INFO: trngsrv: POST parameters: %s" % (query_params) data_stream = urllib.urlopen(CONTENT_SERVER_URL, query_params) data = data_stream.read() if DEBUG: print "* DEBUG: trngsrv: Content server response body: %s" % ( data) # We don't parse the response since the content server does not provide # a uniformly formatted one; instead we only check for SUCCESS key if Storyboard.SERVER_STATUS_SUCCESS in data: # Nothing to do on success as there is no additional info provided pass else: print "* ERROR: trngsrv: Content removal error." self.respond_error(Storyboard.CONTENT_REMOVAL_ERROR) return # Save the response data contsrv_response = data except IOError as error: print "* ERROR: trngsrv: URL error: %s." % (error) self.respond_error(Storyboard.CONTENT_SERVER_ERROR) return ######################################## # Handle range destruction try: # Note: creating a dictionary as below does not # preserve the order of the parameters, but this has # no negative influence in our implementation query_tuples = { query.Parameters.USER: user_id, query.Parameters.ACTION: query.Parameters.DESTROY_RANGE, query.Parameters.RANGE_ID: range_id } query_params = urllib.urlencode(query_tuples) print "* INFO: trngsrv: Send destroy request to instantiation server %s." % ( INSTANTIATION_SERVER_URL) print "* INFO: trngsrv: POST parameters: %s" % (query_params) data_stream = urllib.urlopen(INSTANTIATION_SERVER_URL, query_params) data = data_stream.read() if DEBUG: print "* DEBUG: trngsrv: Instantiation server response body: %s" % ( data) (status, message) = query.Response.parse_server_response(data) if status == Storyboard.SERVER_STATUS_SUCCESS: self.lock_active_sessions.acquire() try: # Read session info session_info = sessinfo.SessionInfo() session_info.parse_YAML_file(ACTIVE_SESSIONS_FILE) # Remove session and save to file if not session_info.remove_session(range_id, user_id): print "* ERROR: Cannot remove training session %s." % ( range_id) self.respond_error( Storyboard.SESSION_INFO_CONSISTENCY_ERROR) return else: session_info.write_YAML_file(ACTIVE_SESSIONS_FILE) finally: self.lock_active_sessions.release() else: print "* ERROR: trngsrv: Range destruction error: %s." % ( message) self.respond_error(Storyboard.DESTRUCTION_ERROR) return instsrv_response = data if DEBUG: print "* DEBUG: trngsrv: contsrv response: %s" % ( contsrv_response) print "* DEBUG: trngsrv: instsrv response: %s" % ( instsrv_response) # Prepare the response: no data needs to be returned, # hence we set the content to None response_data = None except IOError as error: print "* ERROR: trngsrv: URL error: %s." % (error) self.respond_error(Storyboard.INSTANTIATION_SERVER_ERROR) return #################################################################### # Catch unknown actions else: print "* WARNING: trngsrv: Unknown action: %s." % (action) # Emulate communication delay if EMULATE_DELAY: sleep_time = random.randint(2, 5) print "* INFO: trngsrv: Simulate communication by sleeping %d s." % ( sleep_time) time.sleep(sleep_time) # Respond to requester with SUCCESS self.respond_success(response_data)