Commit b9c20cea authored by Jean Michel Rouly's avatar Jean Michel Rouly
Browse files

Introduced user accounts with cookies.

parent 520bdb6f
import ldap
import site
import Cookie
import cookielib
site.addsitedir('/srv/http/wsgi')
import library
......@@ -7,7 +9,7 @@ import goconfig
def application(environ, start_response):
# Set default "empty page" text.
body = ["<p>Nothing here.</p>"]
......@@ -26,13 +28,33 @@ def application(environ, start_response):
usr = data['usr']
psw = data['pass']
body = [usr]
#body = [ data ]
# Try to talk with the LDAP server.
#ld = ldap.initialize( goconfig.ldap_domain )
#ld.simple_bind_s()
#ld.unbind_s()
success = False
if( success ):
# create a hashed cookie
cookie = library.generate_cookie(usr)
cookie_value = cookie["user"].OutputString()
hash_value = cookie["user"].value
# unregister the user, in case they're already in
library.deactivate_user( hash_value )
# register the hashed user with the SQL database
library.activate_user( hash_value )
# push the cookie to the user and redirect
status = '303 See Other'
response_headers = [('Set-Cookie', cookie_value),
('Location', '/'),
('Content-type', 'text/plain')]
start_response(status, response_headers)
return [ str(cookie) ]
else:
body = ["<p>Error: invalid username or password.</p>"]
# Read and store in memory the header and footer sections
# of the page display.
......
......@@ -26,10 +26,13 @@ sql_usr = "go-user"
sql_pasw = "georgemasonsrct"
# sql_db: The SQL database or schema name to which to connect.
sql_db = "srctgo"
sql_db = "go"
# sql_table: The SQL table storing the URL listing.
sql_table = "urls"
# sql_url_table: The SQL table storing the URL listing.
sql_url_table = "urls"
# sql_usr_table: The SQL table storing the active users.
sql_usr_table = "usrs"
#ldap_domain: The location of the LDAP database to connect to.
ldap_domain = "ldap://ldap.gmu.edu"
......@@ -47,3 +50,5 @@ ldap_domain = "ldap://ldap.gmu.edu"
# a "short url" or url identifier.
min_url_len = 5
# hash_salt: This is the private salt used to salt cookie hashing.
hash_salt = "salty"
......@@ -20,11 +20,6 @@ def application(environ, start_response):
## sends the user back here, the user should remain logged in and
## have no issues travelling back and forth.
## IDEAS FOR LOGGING-IN USERS:
## store active users in a database somewhere
## look for an existing framework to integrate with
## cookies / sessions (a la PHP)
# Construct the default body, along with its header/footer wrapper.
body = []
f = open(goconfig.doc_root + "/site_data/top.part", "r")
......@@ -68,7 +63,7 @@ def application(environ, start_response):
"""
#body.append( url_form )
if( library.user_logged_in() ):
if( library.user_logged_in( environ ) ):
body.append( url_form )
else:
body.append( login_form )
......
import random
import math
import site
import MySQLdb
import Cookie
import cookielib
import hashlib
site.addsitedir('/srv/http/wsgi')
import goconfig
# Determine if a user is appropriately validated through LDAP.
def user_logged_in():
def user_logged_in( environ ):
cookie = Cookie.SimpleCookie()
# if the environment contains a cookie, check it out
if environ.has_key('HTTP_COOKIE'):
if cookie.has_key('user'):
# load the cookie we found
cookie.load(environ['HTTP_COOKIE']);
user_hash = cookie['user'].value
# see if it's in the database
mdb,cursor = connect_to_mysql()
cursor.execute("""SELECT count(*) FROM `%s` WHERE `user_hash`='%s';""" %
(goconfig.sql_usr_table, user_hash) )
((num_rows,),) = cursor.fetchall()
mdb.commit()
mdb.close()
return num_rows > 0
return False
# Log in a user by placing a cookie on their machine and entering
# the related hash in a SQL database.
def generate_cookie( user ):
hashed_value = hashlib.sha512( user + goconfig.hash_salt ).hexdigest()
cookie = Cookie.SimpleCookie()
cookie["user"] = hashed_value
cookie["user"]["expires"] = ""
cookie["user"]["path"] = "/"
return cookie
# Register the user in the table of active users.
def activate_user( hash_value ):
mdb,cursor = connect_to_mysql()
cursor.execute( """INSERT INTO `%s` (user_hash) VALUES ('%s');""" %
(goconfig.sql_usr_table, hash_value) )
mdb.commit()
mdb.close()
# Unregister the user in the table of active users.
def deactivate_user( hash_value ):
mdb, cursor = connect_to_mysql()
cursor.execute( """DELETE FROM `%s` WHERE `user_hash`='%s';""" %
(goconfig.sql_usr_table, hash_value) )
mdb.commit()
mdb.close()
# Connect to a mySQL database and return a pointer to that database.
def connect_to_mysql():
# Connect to and choose the database.
mdb = MySQLdb.connect(
goconfig.sql_domain,
goconfig.sql_usr,
goconfig.sql_pasw,
goconfig.sql_db )
cursor = mdb.cursor()
# If we need to create the urls table, then construct it.
cursor.execute("""CREATE TABLE IF NOT EXISTS %s(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
longurl VARCHAR(100),
shorturl VARCHAR(100),
clicks INT(10))""" % goconfig.sql_url_table)
cursor.execute("""CREATE TABLE IF NOT EXISTS %s(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
user_hash VARCHAR(500))""" % goconfig.sql_usr_table)
return mdb, cursor
# Given a dictionary and a set of relevant entries, this procedure
# removes all irrelevant entries, effectively trimming the noise level
def trim_noise( dictionary, relevant_keys ):
......@@ -43,13 +124,13 @@ def parse_post_data( post_data ):
# Generate a random short url from a list of possible characters
# and the minimum allowed length.
def generate_short_url( long_url, min_len ):
def generate_short_url( long_url ):
decimal = 10
encoding = 62
# determine the range of possible values (set by min_len)
min_val = encoding ** (min_len - 1)
max_val = (encoding ** min_len) - 1
# determine the range of possible values (set by goconfig.min_url_len)
min_val = encoding ** (goconfig.min_url_len - 1)
max_val = (encoding ** goconfig.min_url_len) - 1
# generate the short url (some val between min and max)
value = random.randint( min_val, max_val )
......@@ -69,11 +150,21 @@ def generate_short_url( long_url, min_len ):
# This function should return true if the specified short_url
# already exists in the mySQL database. This prevents overlapping.
def short_url_exists( cursor, table, short_url ):
def short_url_exists( short_url ):
mdb, cursor = connect_to_mysql()
output = cursor.execute(
""" SELECT * from """ + table +
""" SELECT * from """ + goconfig.sql_url_table +
""" WHERE shorturl = %s """, (short_url))
output = True if output > 0 else False
mdb.commit()
mdb.close()
return output
# Inserts a short-url, long-url pairing into the database.
def register_url( longurl, shorturl ):
mdb, cursor = connect_to_mysql()
cursor.execute("""INSERT INTO `%s`(`id`, `longurl`, `shorturl`, `clicks`) VALUES
(NULL, '%s', '%s', '0')""" % (goconfig.sql_url_table, longurl, shorturl))
mdb.commit()
mdb.close()
......@@ -26,23 +26,11 @@ def application(environ, start_response):
try:
# Connect to and choose the database.
mdb = MySQLdb.connect(
goconfig.sql_domain,
goconfig.sql_usr,
goconfig.sql_pasw,
goconfig.sql_db )
cursor = mdb.cursor()
# If we need to create the urls table, then construct it.
cursor.execute("""CREATE TABLE IF NOT EXISTS %s(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
longurl VARCHAR(100),
shorturl VARCHAR(100))""" % goconfig.sql_table)
mdb,cursor = library.connect_to_mysql()
# Query the database for the short_url value.
query = cursor.execute(
""" SELECT * from """ + goconfig.sql_table +
""" SELECT * from """ + goconfig.sql_url_table +
""" WHERE shorturl = %s """, (target))
# If at least one row has been found, then grab its short_url.
......@@ -72,12 +60,9 @@ def application(environ, start_response):
return [response]
else:
response = "here we go!"
status = '303 See other'
response_headers = [('Location', url),
('Content-Length', str(len(response)))]
response_headers = [('Location', url)]
start_response(status, response_headers)
return [response]
return ['Redirecting to url . . .']
......@@ -38,7 +38,7 @@ def application(environ, start_response):
# If the page was requested via POST, that means the URL-input
# form was submitted. Scan over the input data, parse it, validate
# it, and then finally connect to the DB and store it. Then output.
while environ["REQUEST_METHOD"] == "POST" and library.user_logged_in():
if environ["REQUEST_METHOD"] == "POST" and library.user_logged_in( environ ):
# Grab user data, cut off non-relevant fields.
data = environ['wsgi.input']
......@@ -51,95 +51,60 @@ def application(environ, start_response):
long_url = urllib.unquote( long_url )
short_url = urllib.unquote( short_url )
# Try to connect to the database and store this pairing
# in the appropriate DB and Table.
try:
# Connect to and choose the database.
mdb = MySQLdb.connect(
goconfig.sql_domain,
goconfig.sql_usr,
goconfig.sql_pasw,
goconfig.sql_db )
cursor = mdb.cursor()
# If we need to create the urls table, then construct it.
cursor.execute("""CREATE TABLE IF NOT EXISTS %s(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
longurl VARCHAR(100),
shorturl VARCHAR(100))""" % goconfig.sql_table)
# If no custom short url identifier was entered, then
# generate a randomized value from the library.
if len( short_url ) == 0:
short_url = library.generate_short_url( long_url, goconfig.min_url_len )
while library.short_url_exists( cursor, goconfig.sql_table, short_url ):
short_url = library.generate_short_url( long_url, goconfig.min_url_len )
# Check each possible error case and set flags accordingly.
# 1) Check that long_url is a valid URL
# 2) Check that short_url exceeds the minimum length
# 3) Check that short_url only contains the appropriate characters
# 4) Check that short_url doesn't already exist in the database
if re.match(url_regex, long_url) == None:
INV_LU = True # long url validity
if len( short_url ) < goconfig.min_url_len:
SU_TS = True # short url length
if re.match(short_regex, short_url) == None:
INV_SU = True # short url validity
if library.short_url_exists( cursor, goconfig.sql_table, short_url ):
SU_EX = True # short url uniqueness
# Reset body of output to prepare for possible error/success msg.
body = []
# Depending on the values of error flags, display an appropriate
# message to the user.
if INV_LU:
body.append("<p>You entered an invalid long url!</p>")
if INV_SU:
body.append("<p>The identifier can contain only letters and numbers.</p>")
if SU_TS:
body.append("<p>The identifier must be at least " + str(goconfig.min_url_len) + " characters.</p>")
if SU_EX:
body.append("<p>The identifier already exists in the database!</p>")
# If none of the error flags have been thrown, then
# append the success messages.
# ie. Display the long and short URLs.
if (not INV_LU) and (not INV_SU) and (not SU_TS) and (not SU_EX):
body.append(
'<p><em>Original URL:</em> <a href="%s">%s</a></p>' %
(long_url, long_url))
body.append(
'<p><em>Shortened URL:</em> <a href="/%s">%s</a></p>' %
(short_url, short_url))
else:
body.append(
'<input type="submit" value="BACK" ' +
'onclick="history.back()" />')
break;
# If execution has proceeded this far, then all the error
# flags must be down, and we are connected successfully to the
# SQL database. If this is the case, then move on to
# If no custom short url identifier was entered, then
# generate a randomized value from the library.
if len( short_url ) == 0:
short_url = library.generate_short_url( long_url )
while library.short_url_exists( short_url ):
short_url = library.generate_short_url( long_url )
# Check each possible error case and set flags accordingly.
# 1) Check that long_url is a valid URL
# 2) Check that short_url exceeds the minimum length
# 3) Check that short_url only contains the appropriate characters
# 4) Check that short_url doesn't already exist in the database
if re.match(url_regex, long_url) == None:
INV_LU = True # long url validity
if len( short_url ) < goconfig.min_url_len:
SU_TS = True # short url length
if re.match(short_regex, short_url) == None:
INV_SU = True # short url validity
if library.short_url_exists( short_url ):
SU_EX = True # short url uniqueness
# Reset body of output to prepare for possible error/success msg.
body = []
# Depending on the values of error flags, display an appropriate
# message to the user.
if INV_LU:
body.append("<p>You entered an invalid long url!</p>")
if INV_SU:
body.append("<p>The identifier can contain only letters and numbers.</p>")
if SU_TS:
body.append("<p>The identifier must be at least " +
str(goconfig.min_url_len) + " characters.</p>")
if SU_EX:
body.append("<p>The identifier already exists in the database!</p>")
# If none of the error flags have been thrown, then
# append the success messages.
# ie. Display the long and short URLs.
if not (INV_LU or INV_SU or SU_TS or SU_EX):
# insert the longurl-shorturl pairing in the database.
cursor.execute(
"""INSERT INTO """ + goconfig.sql_table + """ (id, longurl, shorturl) """ +
"""VALUES (NULL, %s, %s)""", (long_url, short_url))
# Commit changes and close connection.
mdb.commit()
mdb.close()
library.register_url( long_url, short_url )
except MySQLdb.OperationalError:
body.append( "<p>Error in mySQL database.</p>" )
finally:
break
break # failsafe in case we get to the end somehow!
body.append(
'<p><em>Original URL:</em> <a href="%s">%s</a></p>' %
(long_url, long_url))
body.append(
'<p><em>Shortened URL:</em> <a href="/%s">%s</a></p>' %
(short_url, short_url))
else:
body.append(
'<input type="submit" value="BACK" ' +
'onclick="history.back()" />')
# Read and store in memory the header and footer sections
# of the page display.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment