From a584b264d66dd4acd7d19e2cc15e7cbccbe20ac0 Mon Sep 17 00:00:00 2001 From: Tony Situ Date: Sun, 5 Nov 2017 16:46:47 -0800 Subject: [PATCH 1/3] Inital commit for api-key-framework --- ocflib/api/__init__.py | 0 ocflib/api/keys.py | 56 ++++++++++++++++++++++++++++++++++++++++++ ocflib/api/keys.sql | 6 +++++ 3 files changed, 62 insertions(+) create mode 100644 ocflib/api/__init__.py create mode 100644 ocflib/api/keys.py create mode 100644 ocflib/api/keys.sql diff --git a/ocflib/api/__init__.py b/ocflib/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ocflib/api/keys.py b/ocflib/api/keys.py new file mode 100644 index 00000000..7cb1b4e2 --- /dev/null +++ b/ocflib/api/keys.py @@ -0,0 +1,56 @@ +from binascii import hexlify +from os import urandom + +import pymsql + + +"""Constants +Changes here should be reflected in the corresponding keys.sql file if +necessary +""" +KEY_LENGTH = 32 + + +def _generate_key(length=KEY_LENGTH): + """Return a random `length` string to be used as a key + """ + return hexlify(urandom(length)).decode() + + +def add_key(cursor, user): + """Add a key for a user into the database + + Args: + cursor (pymysql.cursors.Cursor): database cursor + user (str): user to add the key for + + Returns: + None + """ + return + + +def get_key(cursor, user): + """Get the key corresponding to the user in the db + + Args: + cursor (pymysql.cursors.Cursor): database cursor + user (str): user to get the key for + + Returns: + (str) the user's key + """ + return + + +def get_connection(user, password, db='keys', **kwargs): + """Return a connection to MySQL.""" + return pymysql.connect( + user=user, + password=password, + db=db, + host='mysql.ocf.berkeley.edu', + cursorclass=pymysql.cursors.DictCursor, + charset='utf8mb4', + **dict({'autocommit': True}, **kwargs) + ) diff --git a/ocflib/api/keys.sql b/ocflib/api/keys.sql new file mode 100644 index 00000000..a62a2407 --- /dev/null +++ b/ocflib/api/keys.sql @@ -0,0 +1,6 @@ +-- length of key defined by KEY_LENGTH in keys.py +CREATE TABLE IF NOT EXISTS `keys` ( + `key` varchar(32) NOT NULL, + `user` varchar(255) NOT NULL + PRIMARY KEY(`key`) +) From 1aea65de556a3cb96d1906dad0f6a71abb3b20ea Mon Sep 17 00:00:00 2001 From: TONY SITU Date: Sun, 5 Nov 2017 17:37:24 -0800 Subject: [PATCH 2/3] Fleshing out get_key and add_key methods with SQL queries --- ocflib/api/keys.py | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/ocflib/api/keys.py b/ocflib/api/keys.py index 7cb1b4e2..f9779e40 100644 --- a/ocflib/api/keys.py +++ b/ocflib/api/keys.py @@ -27,21 +27,39 @@ def add_key(cursor, user): Returns: None """ - return + key = _generate_key() + cursor.execute( + 'INSERT INTO `keys`' + '(`key`, `user`)' + 'VALUES (%s, %s, %s)', + (key, user) + ) def get_key(cursor, user): - """Get the key corresponding to the user in the db - - Args: - cursor (pymysql.cursors.Cursor): database cursor - user (str): user to get the key for + """Get the key corresponding to the user in the db - Returns: - (str) the user's key - """ - return + Args: + cursor (pymysql.cursors.Cursor): database cursor + user (str): user to get the key for + Returns: + (str) the user's key + """ + cursor.execute( + 'SELECT `key`' + 'FROM `keys`' + 'WHERE `user` LIKE %s', + (user,), + ) + query_result = cursor.fetchone() + try: + return query_result['key'] + except (KeyError, TypeError): + # query_result is None or doesn't have a key + return "" + + return cursor.fetchone()['key'] def get_connection(user, password, db='keys', **kwargs): """Return a connection to MySQL.""" From 3e770f87a91a227a2c67de4a49e1fa1f9aba9b7e Mon Sep 17 00:00:00 2001 From: Tony Situ Date: Mon, 6 Nov 2017 15:56:43 -0800 Subject: [PATCH 3/3] added function for key existence check, and collision checking --- ocflib/api/keys.py | 92 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 21 deletions(-) diff --git a/ocflib/api/keys.py b/ocflib/api/keys.py index f9779e40..996affc3 100644 --- a/ocflib/api/keys.py +++ b/ocflib/api/keys.py @@ -17,49 +17,99 @@ def _generate_key(length=KEY_LENGTH): return hexlify(urandom(length)).decode() -def add_key(cursor, user): - """Add a key for a user into the database +def get_key(cursor, user): + """Get the key corresponding to the user in the db Args: cursor (pymysql.cursors.Cursor): database cursor - user (str): user to add the key for + user (str): user to get the key for Returns: - None + (str) the user's key, or None if there is no matching row """ - key = _generate_key() cursor.execute( - 'INSERT INTO `keys`' - '(`key`, `user`)' - 'VALUES (%s, %s, %s)', - (key, user) + 'SELECT `key`' + 'FROM `keys`' + 'WHERE `user` LIKE %s', + (user,), ) + query_result = cursor.fetchone() + try: + return query_result['key'] + except (KeyError, TypeError): + # No matching row + return None -def get_key(cursor, user): - """Get the key corresponding to the user in the db +def get_user(cursor, key): + """Get the user corresponding to the key in the db Args: cursor (pymysql.cursors.Cursor): database cursor - user (str): user to get the key for + key (str): key that corresponds to the user Returns: - (str) the user's key + (str) user that owns the key or None if there is no matching row """ cursor.execute( - 'SELECT `key`' + 'SELECT `user`' 'FROM `keys`' - 'WHERE `user` LIKE %s', - (user,), + 'WHERE `key` LIKE %s', + (key,), ) query_result = cursor.fetchone() + + try: - return query_result['key'] + return query_result['user'] except (KeyError, TypeError): - # query_result is None or doesn't have a key - return "" - - return cursor.fetchone()['key'] + # No matching row + return None + +def key_exists(cursor, key): + """Checks if the key currently exists in the db + + Args: + cursor (pymysql.cursors.Cursor): database cursor + key (str): key to check for + + Returns: + (bool) whether or not the key exists + """ + cursor.execute( + 'SELECT 1' + 'FROM `keys`' + 'WHERE `key` LIKE %s', + (key,), + ) + query_result = cursor.fetchone() + + return query_result is not None + + +def add_key(cursor, user): + """Add a key for a user into the database + + Args: + cursor (pymysql.cursors.Cursor): database cursor + user (str): user to add the key for + + Returns: + None + """ + key = _generate_key() + + # Re-generate key in case of collision + while key_exists(key): + key = _generate_key() + + cursor.execute( + 'INSERT INTO `keys`' + '(`key`, `user`)' + 'VALUES (%s, %s, %s)', + (key, user) + ) + def get_connection(user, password, db='keys', **kwargs): """Return a connection to MySQL."""