def get(self, username_from_url=None):
        username_from_url = username_from_url.decode('utf8')
        username_normalized = username_from_url.lower()

        if username_normalized:
            logging.info('Query for username: '******'usernameNormalized': username_normalized,
                    'usernameAvailable': False,
                }
                logging.info('Username taken: ' + repr(user_obj))

            else:
                response_dict = {
                    'usernameNormalized': username_normalized,
                    'usernameAvailable': True,
                }
                logging.info('Username is available: ' + username_normalized)

            http_helpers.set_http_ok_json_response(self.response, response_dict)

        else:
            err_status = 'ErrorUsernameRequired'
            # log this error for further analysis
            status_reporting.log_call_stack_and_traceback(logging.error, extra_info = err_status)
            http_helpers.set_http_error_json_response(self.response, err_status)
예제 #2
0
    def _get_current_presence_state_from_database(self):

        try:
            client_id = self.key.id()

            # Make sure that the value stored in the database was recently written, and if not then
            # the user is considered to be offline
            if (self.last_db_write + datetime.timedelta(
                    seconds=DATABASE_PRESENCE_UPDATE_INTERVAL_TIMEOUT_SECONDS)
                    < datetime.datetime.utcnow()):

                logging.debug(
                    'Client %s is set to PRESENCE_OFFLINE due to timeout (database check)'
                    % client_id)
                return 'PRESENCE_OFFLINE'

            else:
                logging.info(
                    'Presence state of %s  retrieved from database for client %s'
                    % (self.most_recent_presence_state_stored_in_db,
                       self.key.id()))
                return self.most_recent_presence_state_stored_in_db

        except:
            err_message = '_get_current_presence_state_from_database returning PRESENCE_OFFLINE due to internal error'
            status_reporting.log_call_stack_and_traceback(
                logging.error, extra_info=err_message)
            return 'PRESENCE_OFFLINE'
def get_jwt_token_payload_and_token_session_obj(authorization_header):

    token_payload = None
    token_session_obj = None

    if authorization_header:
        (bearer_txt, split_char, token_string) = authorization_header.partition(' ')

        try:
            token_payload = jwt.decode(token_string, constants.secret_key)

            token_session_id = token_payload['jti']
            token_session_obj = TokenSessionModel.get_by_id(token_session_id)

            if token_session_obj:
                # This token has been found in the database
                if token_session_obj.token_expiration_datetime < datetime.datetime.utcnow():
                    # This token has expired. Set it to none
                    token_session_obj = None
            else:
                raise LookupError('Session not found in the database: %s' % token_payload)

        except:
            # For some reason this token has triggered an error - may require more investigation
            extra_info = "token_string: %s" % token_string
            status_reporting.log_call_stack_and_traceback(logging.warning, extra_info=extra_info)


    return token_payload, token_session_obj
예제 #4
0
def get_jwt_token_payload_and_token_session_obj(authorization_header):

    token_payload = None
    token_session_obj = None

    if authorization_header:
        (bearer_txt, split_char,
         token_string) = authorization_header.partition(' ')

        try:
            token_payload = jwt.decode(token_string, constants.secret_key)

            token_session_id = token_payload['jti']
            token_session_obj = TokenSessionModel.get_by_id(token_session_id)

            if token_session_obj:
                # This token has been found in the database
                if token_session_obj.token_expiration_datetime < datetime.datetime.utcnow(
                ):
                    # This token has expired. Set it to none
                    token_session_obj = None
            else:
                raise LookupError('Session not found in the database: %s' %
                                  token_payload)

        except:
            # For some reason this token has triggered an error - may require more investigation
            extra_info = "token_string: %s" % token_string
            status_reporting.log_call_stack_and_traceback(
                logging.warning, extra_info=extra_info)

    return token_payload, token_session_obj
예제 #5
0
    def get(self, chat_room_name_from_url=None):
        chat_room_name_from_url = chat_room_name_from_url.decode('utf8')
        chat_room_name_normalized = chat_room_name_from_url.lower()

        if chat_room_name_normalized:
            logging.info('Query for room name: ' + chat_room_name_normalized)
            chat_room_obj = ChatRoomModel.query(ChatRoomModel.chat_room_name_normalized == chat_room_name_normalized).get()

            if chat_room_obj:
                response_dict = {
                    'chatRoomName': chat_room_name_normalized,
                    'roomIsRegistered': True,
                    'numInRoom': chat_room_obj.get_occupancy(),
                }
                logging.info('Found room: ' + repr(chat_room_obj))

            else:
                response_dict = {
                    'chatRoomName': chat_room_name_normalized,
                    'roomIsRegistered' : False,
                    'numInRoom': 0
                }
                logging.info('Room name is available: ' + chat_room_name_normalized)

            http_helpers.set_http_ok_json_response(self.response, response_dict)

        else:
            err_status = 'ErrorChatRoomNameRequired'
            # log this error for further analysis
            status_reporting.log_call_stack_and_traceback(logging.error, extra_info = err_status)
            http_helpers.set_http_error_json_response(self.response, err_status)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except:
            status_string = "serverError"
            request = None
            
            # Check if this is a method on a RequestHandler object, and if so we also write
            # out an error to the http response. 
            # Note: this code is only executed if an error occurs, and therefore we don't worry
            # about the extra "cost" of executing the code below since it should be rarely executed. 
            arg_spec = inspect.getargspec(func)
            if arg_spec.args and arg_spec.args[0] == 'self':
                # if the first parameter is 'self' then it is likely that this is a method on an object
                self = args[0]
                if hasattr(self, '__class__'):
                    # if self has an attrute '__class__' this self is likely a parameter on a method object 
                    # as opposed to a parameter on a function
                    cls = self.__class__
                    if issubclass(cls, webapp2.RequestHandler):
                        # if cls is a subclass of RequestHander, then we are pretty sure that the user is generating http on the 
                        # self.response object
                        if hasattr(self, 'response'):
                            # If self has a 'response' attribute, then write out the error_status to self.response.
                            request = self.request
                            logging.debug('executing set_http_error_json_response self.response=%s and status_string=%s' %
                                         (repr(self.response), status_string))
                            http_helpers.set_http_error_json_response(self.response, status_string, 500)  

            # Log the error to the server, along with stack trace and debugging information
            logging.debug('executing log_call_stack_and_traceback with extra_info = %s' % status_string)
            status_reporting.log_call_stack_and_traceback(logging.error, extra_info = status_string, request = request) 
예제 #7
0
    def txn_add_client_to_room(self, client_id):

        room_key = self.key
        new_client_has_been_added = False

        logging.info('Adding client %s to room %s ' % (client_id, room_key))
        chat_room_obj = room_key.get()


        if chat_room_obj:

            try:
                # If user is not already in the room, then add them
                if not client_id in chat_room_obj.room_members_client_ids:
                    chat_room_obj.room_members_client_ids.append(client_id)
                    chat_room_obj.put()
                    logging.info('Client %s has joined room %s ' % (client_id, room_key))
                    new_client_has_been_added = True
                else:
                    logging.info('Client %s was already in room %s ' % (client_id, room_key))

            # If the user cannot be added to the room, then an exception will be generated - let the client
            # know that the server had a problem.
            except:
                status_reporting.log_call_stack_and_traceback(logging.error)
                raise Exception('Error adding client %s to room %s' % (client_id, room_key))

        else:
            error_message = 'Invalid room id: %s' % room_key
            logging.error(error_message)
            raise Exception(error_message)

        return (new_client_has_been_added, chat_room_obj)
예제 #8
0
    def post(self):
        try:
            room_dict = json.loads(self.request.body)

            # Need to get the URL encoded data from utf8. Note that json encoded data appears to already be decoded.

            # Convert camel case keys to underscore (standard python) keys
            room_dict = utils.convert_dict(room_dict, utils.camel_to_underscore)

            chat_room_name_as_written = room_dict['chat_room_name_as_written']
            chat_room_name_normalized = chat_room_name_as_written.lower()
            room_dict['chat_room_name_normalized'] = chat_room_name_normalized


            # Make sure that the room name is valid before continuing.
            # These errors should be rare since these values are
            # already formatted to be within bounds and characters checked by the client-side javascript.
            # Only if the user has somehow bypassed the javascript checks should we receive values that don't
            # conform to the constraints that we have placed.
            if not constants.valid_chat_room_name_regex_compiled.match(chat_room_name_as_written):
                raise Exception('Room name regexp did not match')
            if len(chat_room_name_as_written) > constants.room_max_chars or len(chat_room_name_as_written) < constants.room_min_chars:
                raise Exception('Room name length of %s is out of range' % len(chat_room_name_as_written))

            response_dict = {}
            user_id = int(room_dict['user_id'])

            assert self.session.user_id == user_id

            # If this is a new room, then the room_creator_user_key will be stored in the room
            # object as the "creator" of the room
            room_creator_user_key = ndb.Key('UserModel', user_id)
            chat_room_obj = ChatRoomModel.create_or_get_room(chat_room_name_normalized, room_dict,
                                                            room_creator_user_key)

            response_dict['chatRoomNameNormalized'] = chat_room_name_normalized
            response_dict['chatRoomId'] = chat_room_obj.key.id()
            response_dict['statusString'] = 'roomJoined'

            http_helpers.set_http_ok_json_response(self.response, response_dict)

        except:
            # Unknown exception - provide feedback to the user to indicate that the room was not created or entered into
            err_status = 'ErrorUnableToCreateOrEnterRoom'
            # log this error for further analysis
            status_reporting.log_call_stack_and_traceback(logging.error, extra_info = err_status)
            http_helpers.set_http_ok_json_response(self.response, {'statusString': err_status})
예제 #9
0
    def _get_current_presence_state_from_database(self):

        try:
            client_id = self.key.id()

            # Make sure that the value stored in the database was recently written, and if not then
            # the user is considered to be offline
            if (self.last_db_write + datetime.timedelta(seconds=DATABASE_PRESENCE_UPDATE_INTERVAL_TIMEOUT_SECONDS) <
                datetime.datetime.utcnow()):

                logging.debug('Client %s is set to PRESENCE_OFFLINE due to timeout (database check)' % client_id)
                return 'PRESENCE_OFFLINE'

            else:
                logging.info('Presence state of %s  retrieved from database for client %s' %
                             (self.most_recent_presence_state_stored_in_db, self.key.id()))
                return self.most_recent_presence_state_stored_in_db

        except:
            err_message = '_get_current_presence_state_from_database returning PRESENCE_OFFLINE due to internal error'
            status_reporting.log_call_stack_and_traceback(logging.error, extra_info = err_message)
            return 'PRESENCE_OFFLINE'
예제 #10
0
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except:
            status_string = "serverError"
            request = None

            # Check if this is a method on a RequestHandler object, and if so we also write
            # out an error to the http response.
            # Note: this code is only executed if an error occurs, and therefore we don't worry
            # about the extra "cost" of executing the code below since it should be rarely executed.
            arg_spec = inspect.getargspec(func)
            if arg_spec.args and arg_spec.args[0] == 'self':
                # if the first parameter is 'self' then it is likely that this is a method on an object
                self = args[0]
                if hasattr(self, '__class__'):
                    # if self has an attrute '__class__' this self is likely a parameter on a method object
                    # as opposed to a parameter on a function
                    cls = self.__class__
                    if issubclass(cls, webapp2.RequestHandler):
                        # if cls is a subclass of RequestHander, then we are pretty sure that the user is generating http on the
                        # self.response object
                        if hasattr(self, 'response'):
                            # If self has a 'response' attribute, then write out the error_status to self.response.
                            request = self.request
                            logging.debug(
                                'executing set_http_error_json_response self.response=%s and status_string=%s'
                                % (repr(self.response), status_string))
                            http_helpers.set_http_error_json_response(
                                self.response, status_string, 500)

            # Log the error to the server, along with stack trace and debugging information
            logging.debug(
                'executing log_call_stack_and_traceback with extra_info = %s' %
                status_string)
            status_reporting.log_call_stack_and_traceback(
                logging.error, extra_info=status_string, request=request)
예제 #11
0
def handle_500(request, response, exception):
    status_reporting.log_call_stack_and_traceback(logging.error)
    set_error_json_response_and_write_log(response, 'handle_500 - Server error occured. Path: %s' % request.path, logging.error, 500)
예제 #12
0
def handle_404(request, response, exception):
    status_reporting.log_call_stack_and_traceback(logging.error)
    set_error_json_response_and_write_log(response,  'handle_404 - Page not found: %s' % request.path, logging.error, 404)
예제 #13
0
def set_error_json_response_and_write_log(response, status_string, logging_function, http_status_code, request=None):
    status_reporting.log_call_stack_and_traceback(logging_function, extra_info = status_string, request=request)
    set_http_error_json_response(response, status_string , http_status_code)
예제 #14
0
    def dispatch(self):

        error_key = 'unknownError'
        error_message = 'Unknown Error'

        try:
            authorization_header = self.request.headers.environ.get('HTTP_AUTHORIZATION')

            # Get the "now" before the session, so that in the case that the session is not expired,
            # "now" is always <= session expiry. If we were to get "now" after getting the token
            # then this inequality might not be valid in rare cases where the token has just expired
            # within the past few milliseconds.
            now = datetime.datetime.now()

            token_payload, token_session_obj = token_sessions.get_jwt_token_payload_and_token_session_obj(authorization_header)

            if token_payload and token_session_obj:
                # This session is still valid. Continue processing.
                self.session.user_id = token_payload['userId']
                self.session.username_as_written = token_payload['usernameAsWritten']
                logging.info('******** SESSION user_id: %s username: %s ********' % (self.session.user_id, self.session.username_as_written))

                if self.verify_client:
                    logging.info('*** Verifying client information')
                    if self.request.method == 'POST':
                        # We expect all POSTs to contain a json object that contains a clientId
                        post_body_json = json.loads(self.request.body)
                        self.session.post_body_json = post_body_json

                        client_id = None
                        if 'clientId' in post_body_json:
                            client_id = post_body_json['clientId']

                            assert self.session.user_id == int(client_id.split('|')[0])

                            client_obj = ndb.Key('ClientModel', client_id).get()
                            self.session.client_obj = client_obj
                        else:
                            client_obj = None

                        if not client_obj:
                            error_key = 'invalidClientId'
                            error_message = 'Client object not found for clientId %s. Request denied. ' % client_id
                            raise Exception(error_message)


                user_id = self.session.user_id
                user_obj = users.UserModel.get_by_id(user_id)
                self.session.user_obj = user_obj

                if not user_obj:
                    error_key = 'invalidUserAuthToken'
                    error_message = 'User object not found for userId %s. Request denied. ' % user_id
                    raise Exception(error_message)


                session_expiry = token_session_obj.token_expiration_datetime

                # The session should not already be expired
                assert(session_expiry >= now)
                if (session_expiry - now <
                        datetime.timedelta(seconds = constants.seconds_before_expiration_to_refresh_token)):
                    # The session will expire soon. However, because the client is still connected to the server
                    # we grant them an extension on their session.
                    token_session_obj.token_expiration_datetime = user_obj.get_token_expiration_datetime()
                    logging.info('**** Session expiration date is being reset to %s' % token_session_obj.token_expiration_datetime)
                    token_session_obj.put()

                    if not user_obj.registered_user_bool:
                        # only update the token_expiration_datetime on the userobject in the case that this is a non-registered
                        # user. For registered users, this value is irrelevant and should be set to None, as the
                        # user object will never expire (however, note that tokens for registered users will still
                        # have expiry dates).
                        user_obj.txn_update_expiration_datetime(token_session_obj.token_expiration_datetime)


                # Dispatch the request.
                webapp2.RequestHandler.dispatch(self)

            else:
                error_key = 'invalidUserAuthToken'
                error_message = 'Invalid or expired token with payload %s. Request denied. Header: %s' % \
                    (token_payload, authorization_header)
                raise Exception(error_message)

        except:
            # This client/user has been denied access. 
            # Send unauthorized 401 code as an error response.
            status_reporting.log_call_stack_and_traceback(logging.warn, extra_info = error_message)
            http_helpers.set_http_json_response(self.response, {error_key: error_message}, 401)
예제 #15
0
    def dispatch(self):

        error_key = 'unknownError'
        error_message = 'Unknown Error'

        try:
            authorization_header = self.request.headers.environ.get(
                'HTTP_AUTHORIZATION')

            # Get the "now" before the session, so that in the case that the session is not expired,
            # "now" is always <= session expiry. If we were to get "now" after getting the token
            # then this inequality might not be valid in rare cases where the token has just expired
            # within the past few milliseconds.
            now = datetime.datetime.now()

            token_payload, token_session_obj = token_sessions.get_jwt_token_payload_and_token_session_obj(
                authorization_header)

            if token_payload and token_session_obj:
                # This session is still valid. Continue processing.
                self.session.user_id = token_payload['userId']
                self.session.username_as_written = token_payload[
                    'usernameAsWritten']
                logging.info(
                    '******** SESSION user_id: %s username: %s ********' %
                    (self.session.user_id, self.session.username_as_written))

                if self.verify_client:
                    logging.info('*** Verifying client information')
                    if self.request.method == 'POST':
                        # We expect all POSTs to contain a json object that contains a clientId
                        post_body_json = json.loads(self.request.body)
                        self.session.post_body_json = post_body_json

                        client_id = None
                        if 'clientId' in post_body_json:
                            client_id = post_body_json['clientId']

                            assert self.session.user_id == int(
                                client_id.split('|')[0])

                            client_obj = ndb.Key('ClientModel',
                                                 client_id).get()
                            self.session.client_obj = client_obj
                        else:
                            client_obj = None

                        if not client_obj:
                            error_key = 'invalidClientId'
                            error_message = 'Client object not found for clientId %s. Request denied. ' % client_id
                            raise Exception(error_message)

                user_id = self.session.user_id
                user_obj = users.UserModel.get_by_id(user_id)
                self.session.user_obj = user_obj

                if not user_obj:
                    error_key = 'invalidUserAuthToken'
                    error_message = 'User object not found for userId %s. Request denied. ' % user_id
                    raise Exception(error_message)

                session_expiry = token_session_obj.token_expiration_datetime

                # The session should not already be expired
                assert (session_expiry >= now)
                if (session_expiry - now < datetime.timedelta(
                        seconds=constants.
                        seconds_before_expiration_to_refresh_token)):
                    # The session will expire soon. However, because the client is still connected to the server
                    # we grant them an extension on their session.
                    token_session_obj.token_expiration_datetime = user_obj.get_token_expiration_datetime(
                    )
                    logging.info(
                        '**** Session expiration date is being reset to %s' %
                        token_session_obj.token_expiration_datetime)
                    token_session_obj.put()

                    if not user_obj.registered_user_bool:
                        # only update the token_expiration_datetime on the userobject in the case that this is a non-registered
                        # user. For registered users, this value is irrelevant and should be set to None, as the
                        # user object will never expire (however, note that tokens for registered users will still
                        # have expiry dates).
                        user_obj.txn_update_expiration_datetime(
                            token_session_obj.token_expiration_datetime)

                # Dispatch the request.
                webapp2.RequestHandler.dispatch(self)

            else:
                error_key = 'invalidUserAuthToken'
                error_message = 'Invalid or expired token with payload %s. Request denied. Header: %s' % \
                    (token_payload, authorization_header)
                raise Exception(error_message)

        except:
            # This client/user has been denied access.
            # Send unauthorized 401 code as an error response.
            status_reporting.log_call_stack_and_traceback(
                logging.warn, extra_info=error_message)
            http_helpers.set_http_json_response(self.response,
                                                {error_key: error_message},
                                                401)
예제 #16
0
def handle_500(request, response, exception):
    status_reporting.log_call_stack_and_traceback(logging.error)
    set_error_json_response_and_write_log(
        response, "handle_500 - Server error occured. Path: %s" % request.path, logging.error, 500
    )
예제 #17
0
def handle_404(request, response, exception):
    status_reporting.log_call_stack_and_traceback(logging.error)
    set_error_json_response_and_write_log(
        response, "handle_404 - Page not found: %s" % request.path, logging.error, 404
    )
예제 #18
0
def set_error_json_response_and_write_log(response, status_string, logging_function, http_status_code, request=None):
    status_reporting.log_call_stack_and_traceback(logging_function, extra_info=status_string, request=request)
    set_http_error_json_response(response, status_string, http_status_code)