def validate(json, args): """ Ensure the json object contains all the keys needed to satisfy its endpoint (and isnt empty) """ if not json: raise BBJParameterError( "JSON input is empty. This method requires the following " "arguments: {}".format(", ".join(args))) for arg in args: if arg not in json.keys(): raise BBJParameterError( "Required parameter {} is absent from the request. " "This method requires the following arguments: {}".format( arg, ", ".join(args)))
def thread_get(connection, thread_id, messages=True, op_only=False): """ Fetch the thread_id from the database. Formatting is be handled elsewhere. MESSAGES, if False, will omit the inclusion of a thread's messages and only get its metadata, such as title, author, etc. """ c = connection.cursor() thread = c.execute("SELECT * FROM threads WHERE thread_id = ?", (thread_id, )).fetchone() if not thread: raise BBJParameterError("Thread does not exist.") thread = schema.thread(*thread) if messages or op_only: query = "SELECT * FROM messages WHERE thread_id = ? %s" c.execute( query % ("AND post_id = 0" if op_only else "ORDER BY post_id"), (thread_id, )) # create a list where each post_id matches its list[index] thread["messages"] = [ schema.message(*values) for values in c.fetchall() ] return thread
def user_resolve(connection, name_or_id, externalize=False, return_false=True): """ Accepts a name or id and returns the full user object for it. EXTERNALIZE determines whether to strip the object of private data. RETURN_FALSE determines whether to raise an exception or just return bool False if the user doesn't exist """ user = connection.execute( """ SELECT * FROM users WHERE user_name = ? OR user_id = ? """, (name_or_id, name_or_id)).fetchone() if user: user = schema.user_internal(*user) if externalize: return user_externalize(user) return user if return_false: return False raise BBJParameterError("Requested user element ({})" " is not registered".format(name_or_id))
def do_formatting(format_spec, messages): if not format_spec: return None elif format_spec == "sequential": method = formatting.sequential_expressions else: raise BBJParameterError("invalid formatter specification") formatting.apply_formatting(messages, method) return True
def message_edit_query(connection, author, thread_id, post_id): """ Perform all the neccesary sanity checks required to edit a post and then return the requested message object without any changes. """ user = user_resolve(connection, author) thread = thread_get(connection, thread_id) try: message = thread["messages"][post_id] except IndexError: raise BBJParameterError("post_id out of bounds for requested thread") if not user["is_admin"]: if not user["user_id"] == message["author"]: raise BBJUserError( "non-admin attempt to edit another user's message") elif (time() - message["created"]) > 86400: raise BBJUserError("message is too old to edit (24hr limit)") return message
def wrapper(self, *args, **kwargs): response = None debug = app_config["debug"] try: connection = sqlite3.connect(dbname) # read in the body from the request to a string... if cherrypy.request.method == "POST": read_in = str(cherrypy.request.body.read(), "utf8") if not read_in: # the body may be empty, not all methods require input body = {} else: body = json.loads(read_in) if not isinstance(body, dict): raise BBJParameterError("Non-JSONObject input") # lowercase all of its top-level keys body = {key.lower(): value for key, value in body.items()} else: body = {} username = cherrypy.request.headers.get("User") auth = cherrypy.request.headers.get("Auth") if (username and not auth) or (auth and not username): raise BBJParameterError( "User or Auth was given without the other.") elif not username and not auth: user = db.anon else: user = db.user_resolve(connection, username) if not user: raise BBJUserError("User %s is not registered" % username) elif auth.lower() != user["auth_hash"].lower(): raise BBJException(5, "Invalid authorization key for user.") # api_methods may choose to bind a usermap into the thread_data # which will send it off with the response cherrypy.thread_data.usermap = {} value = function(self, body, connection, user) response = schema.response(value, cherrypy.thread_data.usermap) except BBJException as e: response = e.schema except json.JSONDecodeError as e: response = schema.error(0, str(e)) except Exception as e: error_id = uuid1().hex response = schema.error( 1, "Internal server error: code {} {}".format(error_id, repr(e))) with open("logs/exceptions/" + error_id, "a") as log: traceback.print_tb(e.__traceback__, file=log) log.write(repr(e)) print("logged code 1 exception " + error_id) finally: connection.close() return json.dumps(response)
def validate(keys_and_values): """ The line of defense against garbage user input. Recieves an iterable containing iterables, where [0] is a string representing the value type, and [1] is the value to compare against a set of rules for it's type. The function returns the boolean value True when everything is okay, or raises a BBJException to be handled by higher levels of the program if something is wrong (immediately stopping execution at the db level) """ for key, value in keys_and_values: if key == "user_name": if not value: raise BBJUserError("Username may not be empty.") elif contains_nonspaces(value): raise BBJUserError( "Username cannot contain whitespace characters besides spaces." ) elif not value.strip(): raise BBJUserError( "Username must contain at least one non-space character") elif len(value) > 24: raise BBJUserError("Username is too long (max 24 chars)") elif key == "auth_hash": if not value: raise BBJParameterError("auth_hash may not be empty") elif len(value) != 64: raise BBJParameterError("Client error: invalid SHA-256 hash.") elif key == "quip": if contains_nonspaces(value): raise BBJUserError( "Quip cannot contain whitespace characters besides spaces." ) elif len(value) > 120: raise BBJUserError("Quip is too long (max 120 chars)") elif key == "bio": if len(value) > 4096: raise BBJUserError("Bio is too long (max 4096 chars)") elif key == "title": if not value: raise BBJUserError("Title cannot be empty") elif contains_nonspaces(value): raise BBJUserError( "Titles cannot contain whitespace characters besides spaces." ) elif not value.strip(): raise BBJUserError( "Title must contain at least one non-space character") elif len(value) > 120: raise BBJUserError("Title is too long (max 120 chars)") elif key == "body": if not value: raise BBJUserError("Post body cannot be empty") elif key == "color": if value in range(0, 7): continue raise BBJParameterError( "Color specification out of range (int 0-6)") return True