aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--wqflask/secure_server.py7
-rwxr-xr-xwqflask/utility/__init__.py23
-rw-r--r--wqflask/wqflask/model.py12
-rw-r--r--wqflask/wqflask/templates/base.html29
-rw-r--r--wqflask/wqflask/templates/index_page.html7
-rw-r--r--wqflask/wqflask/templates/new_security/login_user.html144
-rw-r--r--wqflask/wqflask/templates/new_security/thank_you.html4
-rw-r--r--wqflask/wqflask/user_manager.py94
-rw-r--r--wqflask/wqflask/views.py43
9 files changed, 260 insertions, 103 deletions
diff --git a/wqflask/secure_server.py b/wqflask/secure_server.py
index df195bd2..a77abf7e 100644
--- a/wqflask/secure_server.py
+++ b/wqflask/secure_server.py
@@ -25,8 +25,11 @@ app.logger.addHandler(file_handler)
import logging_tree
logging_tree.printout()
-import sys
-print("At startup, path is:", sys.path)
+#import sys
+#print("At startup, path is:", sys.path)
+
+from werkzeug.contrib.fixers import ProxyFix
+app.wsgi_app = ProxyFix(app.wsgi_app)
#print("app.config is:", app.config)
diff --git a/wqflask/utility/__init__.py b/wqflask/utility/__init__.py
index d0e4a3fa..d9856eed 100755
--- a/wqflask/utility/__init__.py
+++ b/wqflask/utility/__init__.py
@@ -1,5 +1,6 @@
from pprint import pformat as pf
+# Todo: Move these out of __init__
class Bunch(object):
"""Like a dictionary but using object notation"""
@@ -10,3 +11,25 @@ class Bunch(object):
return pf(self.__dict__)
+class Struct(object):
+ '''The recursive class for building and representing objects with.
+
+ From http://stackoverflow.com/a/6573827/1175849
+
+ '''
+
+ def __init__(self, obj):
+ for k, v in obj.iteritems():
+ if isinstance(v, dict):
+ setattr(self, k, Struct(v))
+ else:
+ setattr(self, k, v)
+
+ def __getitem__(self, val):
+ return self.__dict__[val]
+
+ def __repr__(self):
+ return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for
+ (k, v) in self.__dict__.iteritems()))
+
+
diff --git a/wqflask/wqflask/model.py b/wqflask/wqflask/model.py
index 8e7a823e..5c514bde 100644
--- a/wqflask/wqflask/model.py
+++ b/wqflask/wqflask/model.py
@@ -1,7 +1,9 @@
from __future__ import print_function, division, absolute_import
import uuid
+import datetime
+from flask import request
from flask.ext.sqlalchemy import SQLAlchemy
#from flask.ext.security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin
@@ -84,9 +86,15 @@ class Login(Base):
__tablename__ = "login"
id = Column(Unicode(36), primary_key=True, default=lambda: unicode(uuid.uuid4()))
user = Column(Unicode(36), ForeignKey('user.id'))
- timestamp = Column(DateTime())
+ timestamp = Column(DateTime(), default=lambda: datetime.datetime.utcnow())
ip_address = Column(Unicode(39))
-
+ successful = Column(Boolean(), nullable=False) # False if wrong password was entered
+ session_id = Column(Text) # Set only if successfully logged in, otherwise should be blank
+
+ def __init__(self, user):
+ self.user = user.id
+ self.ip_address = request.remote_addr
+
# Setup Flask-Security
#user_datastore = SQLAlchemyUserDatastore(db, User, Role)
diff --git a/wqflask/wqflask/templates/base.html b/wqflask/wqflask/templates/base.html
index 6e7119fe..077e4705 100644
--- a/wqflask/wqflask/templates/base.html
+++ b/wqflask/wqflask/templates/base.html
@@ -26,10 +26,39 @@
</head>
+{% macro header(main, second) %}
+ <header class="jumbotron subhead" id="overview">
+ <div class="container">
+ <h1>Login</h1>
+ <p class="lead">
+ Gain access to GeneNetwork.
+ </p>
+ </div>
+ </header>
+
+ {{ flash_me() }}
+{% endmacro %}
+
+
+{% macro flash_me() -%}
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {% if messages %}
+ <br />
+ <div class="container">
+ {% for category, message in messages %}
+ <div class="alert {{ category }}">{{ message }}</div>
+ {% endfor %}
+ </div>
+ {% endif %}
+ {% endwith %}
+{% endmacro %}
+
<body data-spy="scroll" data-target=".bs-docs-sidebar">
<!-- Navbar
================================================== -->
+
<div class="navbar navbar-inverse navbar-fixed-top">
+
<div class="navbar-inner">
<div class="container">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
diff --git a/wqflask/wqflask/templates/index_page.html b/wqflask/wqflask/templates/index_page.html
index 61e34eca..98682e57 100644
--- a/wqflask/wqflask/templates/index_page.html
+++ b/wqflask/wqflask/templates/index_page.html
@@ -8,11 +8,14 @@
<header class="jumbotron subhead" id="overview">
<div class="container">
<h1>GeneNetwork</h1>
- <p class="lead">Open source bioinformatics for systems genetics</p>
+ <p class="lead">Open source bioinformatics for systems genetics</p>
</div>
</header>
<div class="container">
+
+ {{ flash_me() }}
+
<div class="row">
<div class="span3 bs-docs-sidebar">
<ul class="nav nav-list bs-docs-sidenav">
@@ -24,7 +27,7 @@
<li><a href="#websites"><i class="icon-chevron-right"></i> Affiliates and mirrors</a></li>
</ul>
</div>
-
+
<div class="span9">
<section id="quick-search">
<div class="page-header">
diff --git a/wqflask/wqflask/templates/new_security/login_user.html b/wqflask/wqflask/templates/new_security/login_user.html
index 360a6a68..f232ccbc 100644
--- a/wqflask/wqflask/templates/new_security/login_user.html
+++ b/wqflask/wqflask/templates/new_security/login_user.html
@@ -1,61 +1,83 @@
-<div class="security_box">
-
- <h4>* Don't have an account?</h4>
-
- <center>
- <a href="/n/register" class="btn btn-info modalize">Create a new account</a>
- </center>
-
- <hr />
-
- <h4>Already have an account?</h4>
-
- <h5>Sign in here</h5>
-
- <form class="form-horizontal" action="/n/login_submit"
- method="POST" name="login_user_form">
- <fieldset>
-
-
- <div class="control-group">
- <label class="control-label" for="email">Email Address</label>
- <div class="controls">
- <input id="email" class="focused" name="email" type="text" value="">
- </div>
- </div>
-
- <div class="control-group">
- <label class="control-label" for="password">Password</label>
- <div class="controls">
- <input id="password" name="password" type="password" value="">
- <br />
- <a href="{{ url_for_security('forgot_password') }}">Forgot your password?</a><br/>
- </div>
- </div>
-
-
- <div class="control-group">
- <div class="controls">
- <label class="checkbox">
- <input id="remember" name="remember" type="checkbox" value="y"> Remember me
- </label>
- </div>
-
-
- <div class="control-group">
- <div class="controls">
- <input id="next" name="next" type="hidden" value="">
-
- <input class="btn btn-primary" id="submit" name="submit" type="submit" value="Sign in">
- </div>
-
-
- </div>
- </fieldset>
-
- </form>
-</div>
-
-
-{% include "new_security/_scripts.html" %}
-<!--{% include "security/_menu.html" %}-->
+{% extends "base.html" %}
+{% block title %}Register{% endblock %}
+{% block content %}
+
+ {{ header("Login", "Gain access to GeneNetwork.") }}
+
+ <div class="container">
+ <div class="page-header">
+ <h1>Login</h1>
+ </div>
+
+
+ <div class="security_box">
+
+ <h4>Don't have an account?</h4>
+
+
+ <a href="/n/register" class="btn btn-info modalize">Create a new account</a>
+
+
+ <hr />
+
+ <h4>Already have an account?</h4>
+
+ <h5>Sign in here</h5>
+
+
+
+ <form class="form-horizontal" action="/n/login"
+ method="POST" name="login_user_form">
+ <fieldset>
+
+
+ <div class="control-group">
+ <label class="control-label" for="email_address">Email Address</label>
+ <div class="controls">
+ <input id="email_address" class="focused" name="email_address" type="text" value="">
+ </div>
+ </div>
+
+ <div class="control-group">
+ <label class="control-label" for="password">Password</label>
+ <div class="controls">
+ <input id="password" name="password" type="password" value="">
+ <br />
+ <a href="url_for_security('forgot_password')">Forgot your password?</a><br/>
+ </div>
+ </div>
+
+
+ <div class="control-group">
+ <div class="controls">
+ <label class="checkbox">
+ <input id="remember" name="remember" type="checkbox" value="y"> Remember me
+ </label>
+ </div>
+
+
+ <div class="control-group">
+ <div class="controls">
+ <input id="next" name="next" type="hidden" value="">
+
+ <input class="btn btn-primary" id="submit" name="submit" type="submit" value="Sign in">
+ </div>
+
+
+ </div>
+ </fieldset>
+
+ </form>
+ </div>
+ </div>
+ </div>
+
+ {% endblock %}
+
+{% block js %}
+ <!--<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>-->
+
+ {% include "new_security/_scripts.html" %}
+
+{% endblock %}
+
diff --git a/wqflask/wqflask/templates/new_security/thank_you.html b/wqflask/wqflask/templates/new_security/thank_you.html
index 5aa11ebf..97cb7807 100644
--- a/wqflask/wqflask/templates/new_security/thank_you.html
+++ b/wqflask/wqflask/templates/new_security/thank_you.html
@@ -12,12 +12,12 @@
<div class="container">
<div class="page-header">
- <h1>All done</h1>
+ <h3>You are done registering</h3>
</div>
<p>Enjoy using the site.</p>
- <p>Go to the <a href="{{ url_for("/") }}">homepage</a></p>.
+ <p>Go to the <a href="{{ url_for("index_page") }}">homepage</a></p>.
</div>
{% endblock %}
diff --git a/wqflask/wqflask/user_manager.py b/wqflask/wqflask/user_manager.py
index b967c86f..7c1761ba 100644
--- a/wqflask/wqflask/user_manager.py
+++ b/wqflask/wqflask/user_manager.py
@@ -15,6 +15,7 @@ import time
import uuid
import hashlib
import hmac
+import base64
import simplejson as json
@@ -22,7 +23,7 @@ from redis import StrictRedis
Redis = StrictRedis()
-from flask import Flask, g, render_template, url_for, request
+from flask import Flask, g, render_template, url_for, request, make_response, redirect, flash
from wqflask import app
@@ -35,7 +36,7 @@ from wqflask.database import db_session
from wqflask import model
-from utility import Bunch
+from utility import Bunch, Struct
@@ -112,7 +113,9 @@ class RegisterUser(object):
db_session.add(self.new_user)
db_session.commit()
+ print("Adding verification email to queue")
self.send_email_verification()
+ print("Added verification email to queue")
self.thank_you_mode = True
@@ -122,9 +125,10 @@ class RegisterUser(object):
pwfields.algorithm = "pbkdf2"
pwfields.hashfunc = "sha256"
- hashfunc = getattr(hashlib, pwfields.hashfunc)
+ #hashfunc = getattr(hashlib, pwfields.hashfunc)
- pwfields.salt = os.urandom(32)
+ # Encoding it to base64 makes storing it in json much easier
+ pwfields.salt = base64.b64encode(os.urandom(32))
# https://forums.lastpass.com/viewtopic.php?t=84104
pwfields.iterations = 100000
@@ -136,18 +140,18 @@ class RegisterUser(object):
print("pwfields:", vars(pwfields))
print("locals:", locals())
+
+ enc_password = Password(password,
+ pwfields.salt,
+ pwfields.iterations,
+ pwfields.keylength,
+ pwfields.hashfunc)
- # On our computer it takes around 1.4 seconds
- start_time = time.time()
- pwfields.password = pbkdf2.pbkdf2_hex(password, pwfields.salt, pwfields.iterations, pwfields.keylength, hashfunc)
- pwfields.encrypt_time = round(time.time() - start_time, 3)
-
- print("Creating password took:", pwfields.encrypt_time)
+ pwfields.password = enc_password.password
+ pwfields.encrypt_time = enc_password.encrypt_time
self.user.password = json.dumps(pwfields.__dict__,
- sort_keys=True,
- # See http://stackoverflow.com/a/12312896
- encoding="latin-1"
+ sort_keys=True,
)
def send_email_verification(self):
@@ -166,7 +170,21 @@ class RegisterUser(object):
body = render_template("email/verification.txt",
verification_code = verification_code)
send_email(to, subject, body)
-
+
+class Password(object):
+ def __init__(self, unencrypted_password, salt, iterations, keylength, hashfunc):
+ print("in Password __init__ locals are:", locals())
+ hashfunc = getattr(hashlib, hashfunc)
+ print("hashfunc is:", hashfunc)
+ # On our computer it takes around 1.4 seconds in 2013
+ start_time = time.time()
+ salt = base64.b64decode(salt)
+ print("now salt is:", salt)
+ self.password = pbkdf2.pbkdf2_hex(str(unencrypted_password),
+ salt, iterations, keylength, hashfunc)
+ self.encrypt_time = round(time.time() - start_time, 3)
+ print("Creating password took:", self.encrypt_time)
+
def basic_info():
return dict(timestamp = timestamp(),
@@ -184,8 +202,54 @@ def verify_email():
user.confirmed = json.dumps(basic_info(), sort_keys=True)
db_session.commit()
-
+def login():
+ params = request.form if request.form else request.args
+ print("in login params are:", params)
+ if not params:
+ return render_template("new_security/login_user.html")
+ else:
+ user = model.User.query.filter_by(email_address=params['email_address']).one()
+ submitted_password = params['password']
+ pwfields = Struct(json.loads(user.password))
+ encrypted = Password(submitted_password,
+ pwfields.salt,
+ pwfields.iterations,
+ pwfields.keylength,
+ pwfields.hashfunc)
+ print("\n\nComparing:\n{}\n{}\n".format(encrypted.password, pwfields.password))
+ valid = pbkdf2.safe_str_cmp(encrypted.password, pwfields.password)
+ print("valid is:", valid)
+
+ login_rec = model.Login(user)
+
+
+ if valid:
+ login_rec.successful = True
+ login_rec.session_id = str(uuid.uuid4())
+ #session_id = "session_id:{}".format(login_rec.session_id)
+ session_id_signature = actual_hmac_creation(login_rec.session_id)
+ session_id_signed = login_rec.session_id + ":" + session_id_signature
+
+ session = dict(login_time = time.time(),
+ user_id = user.id,
+ user_email_address = user.email_address)
+
+ flash("Thank you for logging in.", "alert-success")
+
+ Redis.hmset("session_id:" + login_rec.session_id, session)
+
+ response = make_response(redirect('/'))
+ response.set_cookie('session_id', session_id_signed)
+ else:
+ login_rec.successful = False
+ flash("Invalid email-address or password. Please try again.", "alert-error")
+ response = make_response(redirect(url_for('login')))
+ db_session.add(login_rec)
+ db_session.commit()
+ return response
+ def logout():
+ pass
################################# Sign and unsign #####################################
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index fe91e014..deccf459 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -267,20 +267,15 @@ def sharing_info_page():
template_vars = SharingInfoPage.SharingInfoPage(fd)
return template_vars
-# Take this out or secure it before g[umlfoing into production
+# Take this out or secure it before putting into production
@app.route("/get_temp_data")
def get_temp_data():
temp_uuid = request.args['key']
return flask.jsonify(temp_data.TempData(temp_uuid).get_all())
-#@app.route("/thank_you")
-#def thank_you():
-# return render_template("security/thank_you.html")
-@app.route("/manage/verify")
-def verify():
- user_manager.verify_email()
- return render_template("new_security/verified.html")
+###################################################################################################
+
@app.route("/manage/users")
def manage_users():
@@ -298,16 +293,18 @@ def manage_groups():
return render_template("admin/group_manager.html", **template_vars.__dict__)
-
-
@app.route("/n/register", methods=('GET', 'POST'))
-def new_register():
+def register():
params = None
errors = None
- if request.form:
- params = request.form
- else:
- params = request.args
+
+ #if request.form:
+ # params = request.form
+ #else:
+ # params = request.args
+
+ params = request.form if request.form else request.args
+
if params:
print("Attempting to register the user...")
result = user_manager.RegisterUser(params)
@@ -315,7 +312,7 @@ def new_register():
if result.thank_you_mode:
assert not errors, "Errors while in thank you mode? That seems wrong..."
- return render_template("new_security/thank_you.html")
+ return render_template("new_security/registered.html")
return render_template("new_security/register_user.html", values=params, errors=errors)
@@ -333,9 +330,17 @@ def new_register():
# #return redirect(url_for('new_register', errors=errors), code=307)
-@app.route("/n/login")
-def new_login():
- return render_template("new_security/login_user.html")
+@app.route("/n/login", methods=('GET', 'POST'))
+def login():
+ return user_manager.login()
+
+@app.route("/manage/verify")
+def verify():
+ user_manager.verify_email()
+ return render_template("new_security/verified.html")
+
+
+##########################################################################
def json_default_handler(obj):
'''Based on http://stackoverflow.com/a/2680060/1175849'''