aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--wqflask/secure_server.py40
-rw-r--r--wqflask/wqflask/send_mail.py39
-rw-r--r--wqflask/wqflask/templates/email/forgot_password.txt5
-rw-r--r--wqflask/wqflask/templates/email/verification.txt3
-rw-r--r--wqflask/wqflask/templates/new_security/forgot_password.html65
-rw-r--r--wqflask/wqflask/templates/new_security/forgot_password_step2.html33
-rw-r--r--wqflask/wqflask/templates/new_security/login_user.html55
-rw-r--r--wqflask/wqflask/templates/new_security/password_reset.html80
-rw-r--r--wqflask/wqflask/templates/new_security/register_user.html56
-rw-r--r--wqflask/wqflask/templates/new_security/registered.html11
-rw-r--r--wqflask/wqflask/user_manager.py342
-rw-r--r--wqflask/wqflask/views.py64
12 files changed, 546 insertions, 247 deletions
diff --git a/wqflask/secure_server.py b/wqflask/secure_server.py
index a77abf7e..d5f1a291 100644
--- a/wqflask/secure_server.py
+++ b/wqflask/secure_server.py
@@ -1,12 +1,18 @@
from __future__ import absolute_import, division, print_function
+import time
+import sys
+
from wqflask import app
from flask import Flask, render_template
+import redis
+Redis = redis.StrictRedis()
+
# Setup mail
-from flask.ext.mail import Mail
-mail = Mail(app)
+#from flask.ext.mail import Mail
+#mail = Mail(app)
from wqflask.model import *
@@ -33,8 +39,38 @@ app.wsgi_app = ProxyFix(app.wsgi_app)
#print("app.config is:", app.config)
+
+
+def check_send_mail_running():
+ """Ensure send_mail.py is running before we start the site
+
+ It would be really easy to accidentally run the site
+ without our mail program running
+ This will make sure our mail program is running...or at least recently run...
+
+ """
+ error_msg = "Make sure your are running send_mail.py"
+ send_mail_ping = Redis.get("send_mail:ping")
+ print("send_mail_ping is:", send_mail_ping)
+ if not send_mail_ping:
+ sys.exit(error_msg)
+
+ last_ping = time.time() - float(send_mail_ping)
+ if not (0 < last_ping < 100):
+ sys.exit(error_msg)
+
+
+ print("send_mail.py seems to be running...")
+
+
if __name__ == '__main__':
#create_user()
+
+
+
+ check_send_mail_running()
+
+
app.run(host='0.0.0.0',
port=app.config['SERVER_PORT'],
use_debugger=False,
diff --git a/wqflask/wqflask/send_mail.py b/wqflask/wqflask/send_mail.py
index be51ad0d..bf5d0dd8 100644
--- a/wqflask/wqflask/send_mail.py
+++ b/wqflask/wqflask/send_mail.py
@@ -1,6 +1,7 @@
from __future__ import absolute_import, division, print_function
import datetime
+import time
import simplejson as json
@@ -12,34 +13,40 @@ import mailer
def timestamp():
ts = datetime.datetime.utcnow()
return ts.isoformat()
-
+
def main():
while True:
- print("Waiting for message to show up in queue...")
- msg = Redis.blpop("mail_queue")
-
- # Queue name is the first element, we want the second, which is the actual message
- msg = msg[1]
-
- print("\nGot a msg in queue at {}: {}".format(timestamp(), msg))
- # Todo: Truncate mail_processed when it gets to long
- Redis.rpush("mail_processed", msg)
- process_message(msg)
-
+ print("I'm alive!")
+
+ # Set something so we know it's running (or at least been running recently)
+ Redis.setex("send_mail:ping", 300, time.time())
+
+ msg = Redis.blpop("mail_queue", 30)
+
+ if msg:
+ # Queue name is the first element, we want the second, which is the actual message
+ msg = msg[1]
+
+ print("\n\nGot a msg in queue at {}: {}".format(timestamp(), msg))
+ # Todo: Truncate mail_processed when it gets to long
+ Redis.rpush("mail_processed", msg)
+ process_message(msg)
+
+
def process_message(msg):
msg = json.loads(msg)
-
+
message = mailer.Message()
message.From = msg['From']
message.To = msg['To']
message.Subject = msg['Subject']
message.Body = msg['Body']
-
+
sender = mailer.Mailer('localhost')
sender.send(message)
print("Sent message at {}: {}\n".format(timestamp(), msg))
-
+
if __name__ == '__main__':
- main() \ No newline at end of file
+ main()
diff --git a/wqflask/wqflask/templates/email/forgot_password.txt b/wqflask/wqflask/templates/email/forgot_password.txt
new file mode 100644
index 00000000..e7d1389b
--- /dev/null
+++ b/wqflask/wqflask/templates/email/forgot_password.txt
@@ -0,0 +1,5 @@
+Sorry to hear you lost your GeneNetwork password.
+
+To reset your password please click the following link, or cut and paste it into your browser window:
+
+{{ url_for_hmac("password_reset", code = verification_code, _external=True )}}
diff --git a/wqflask/wqflask/templates/email/verification.txt b/wqflask/wqflask/templates/email/verification.txt
index 29229c68..76149a3a 100644
--- a/wqflask/wqflask/templates/email/verification.txt
+++ b/wqflask/wqflask/templates/email/verification.txt
@@ -4,5 +4,4 @@ We need to verify your email address.
To do that please click the following link, or cut and paste it into your browser window:
-{{ url_for_hmac("verify", code = verification_code, _external=True )}}
-
+{{ url_for_hmac("verify_email", code = verification_code, _external=True )}}
diff --git a/wqflask/wqflask/templates/new_security/forgot_password.html b/wqflask/wqflask/templates/new_security/forgot_password.html
new file mode 100644
index 00000000..39e51f96
--- /dev/null
+++ b/wqflask/wqflask/templates/new_security/forgot_password.html
@@ -0,0 +1,65 @@
+{% extends "base.html" %}
+{% block title %}Forgot Password{% endblock %}
+{% block content %}
+
+ {{ header("Forgot Password", "Easily reset your password.") }}
+
+ <div class="container">
+ <div class="page-header">
+ <h1>Password Reset</h1>
+ </div>
+
+
+ <div class="security_box">
+
+ <h4>Enter your email address</h4>
+
+ <h5>And we'll send you a link to reset your password</h5>
+
+
+
+ <form class="form-horizontal" action="/n/forgot_password_submit"
+ 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">
+ <div class="controls">
+ <input id="next" name="next" type="hidden" value="">
+
+ <input class="btn btn-primary" id="submit" name="submit" type="submit" value="Send link">
+ </div>
+ </div>
+
+ </fieldset>
+
+ <br />
+
+ <div class="well">
+ <h5>Has your email address changed?</h5>
+
+ If you no longer use the email address connected to your account, you can contact us for assistance.
+
+ </div>
+
+ </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/forgot_password_step2.html b/wqflask/wqflask/templates/new_security/forgot_password_step2.html
new file mode 100644
index 00000000..1295e589
--- /dev/null
+++ b/wqflask/wqflask/templates/new_security/forgot_password_step2.html
@@ -0,0 +1,33 @@
+{% extends "base.html" %}
+{% block title %}Register{% endblock %}
+{% block content %}
+ <header class="jumbotron subhead" id="overview">
+ <div class="container">
+ <h1>Password Reset</h1>
+ <p class="lead">
+ Check your email.
+ </p>
+ </div>
+ </header>
+
+ <div class="container">
+ <div class="page-header">
+ <h3>One last step</h3>
+ </div>
+
+ <p>You will receive an email with the subject <strong>"{{ subject }}"</strong>.</p>
+
+ <p>You must click the link in the email to reset the password.</p>
+
+ <p>If you don't see the email, check your spam folder.</p>
+ </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" %}
+ <script type="text/javascript" src="/static/new/js_external/zxcvbn/zxcvbn-async.js"></script>
+ <script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/login_user.html b/wqflask/wqflask/templates/new_security/login_user.html
index f232ccbc..4e308c75 100644
--- a/wqflask/wqflask/templates/new_security/login_user.html
+++ b/wqflask/wqflask/templates/new_security/login_user.html
@@ -2,82 +2,81 @@
{% block title %}Register{% endblock %}
{% block content %}
- {{ header("Login", "Gain access to GeneNetwork.") }}
+ {{ header("Sign in", "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/>
+ <a href="/n/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 %}
+{% 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/password_reset.html b/wqflask/wqflask/templates/new_security/password_reset.html
new file mode 100644
index 00000000..cda1e477
--- /dev/null
+++ b/wqflask/wqflask/templates/new_security/password_reset.html
@@ -0,0 +1,80 @@
+{% extends "base.html" %}
+{% block title %}Register{% endblock %}
+{% block content %}
+
+ {{ header("Password Reset", "Create a new password.") }}
+
+
+ <div class="container">
+ <div class="page-header">
+ <h1>Password reset</h1>
+ </div>
+
+ <div class="security_box">
+
+
+ <h4>Enter your new password</h4>
+
+
+ {% if errors %}
+ <div class="alert alert-error">
+ <strong>Please note:</strong>
+ <ul>
+ {% for error in errors %}
+ <li>{{error}}</li>
+ {% endfor %}
+ </ul>
+ </div>
+ {% endif %}
+
+ <form class="form-horizontal" action="/n/password_reset_step2" data-validate="parsley"
+ method="POST" name="login_user_form">
+
+ <fieldset>
+
+ <input class="hidden" name="user_encode" value="{{ user_encode }}">
+
+
+ <div class="control-group">
+ <label class="control-label" for="password">Password</label>
+ <div class="controls">
+ <input id="password" name="password" type="password" value=""
+ data-trigger="change" data-required="true" data-minlength="6">
+ </div>
+ </div>
+
+ <div class="control-group" style="display: none" id="password_alert">
+ <div class="controls"">
+ <span id="password_strength" class="alert"></span>
+ </div>
+ </div>
+
+ <div class="control-group">
+ <label class="control-label" for="password_confirm">Confirm Password</label>
+ <div class="controls">
+ <input id="password" name="password_confirm" type="password" value=""
+ data-trigger="change" data-required="true" data-equalto="#password">
+ </div>
+ </div>
+
+ <div class="control-group">
+ <div class="controls"">
+ <input class="btn btn-primary" id="submit" name="submit" type="submit" value="Reset password">
+ </div>
+ </div>
+
+ </fieldset>
+
+ </form>
+ </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" %}
+ <script type="text/javascript" src="/static/new/js_external/zxcvbn/zxcvbn-async.js"></script>
+ <script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/register_user.html b/wqflask/wqflask/templates/new_security/register_user.html
index 2a02e7ca..998d2a7b 100644
--- a/wqflask/wqflask/templates/new_security/register_user.html
+++ b/wqflask/wqflask/templates/new_security/register_user.html
@@ -1,36 +1,31 @@
{% extends "base.html" %}
{% block title %}Register{% endblock %}
{% block content %}
- <header class="jumbotron subhead" id="overview">
- <div class="container">
- <h1>Register</h1>
- <p class="lead">
- It's easy and fast to make an account.
- </p>
- </div>
- </header>
+
+ {{ header("Register", "It's fast and easy to make an account.") }}
+
<div class="container">
<div class="page-header">
<h1>Registration</h1>
</div>
-
+
<div class="security_box">
<h4>Already have an account?</h4>
-
-
+
+
<a href="/n/login"
class="btn btn-info modalize">Sign in using existing account</a>
-
-
+
+
<hr />
-
+
<h4>Don't have an account?</h4>
-
+
<h5>Register here</h5>
-
+
{% if errors %}
- <div class="alert alert-error">
+ <div class="alert alert-error">
<strong>Please note:</strong>
<ul>
{% for error in errors %}
@@ -39,11 +34,11 @@
</ul>
</div>
{% endif %}
-
+
<form class="form-horizontal" action="/n/register" data-validate="parsley"
method="POST" name="login_user_form">
<fieldset>
-
+
<div class="control-group">
<label class="control-label" for="email_address">Email Address</label>
<div class="controls">
@@ -51,7 +46,7 @@
data-trigger="change" data-required="true" data-type="email" data-maxlength="50">
</div>
</div>
-
+
<div class="control-group">
<label class="control-label" for="full_name">Full Name</label>
<div class="controls">
@@ -59,28 +54,28 @@
data-trigger="change" data-required="true" data-minlength="5" data-maxlength="50">
</div>
</div>
-
+
<div class="control-group">
<label class="control-label" for="organization">Organization</label>
<div class="controls">
<input id="organization" name="organization" type="text" value="{{values.organization}}" data-minlength="3" data-maxlength="50">
</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=""
data-trigger="change" data-required="true" data-minlength="6">
</div>
- </div>
-
+ </div>
+
<div class="control-group" style="display: none" id="password_alert">
<div class="controls"">
<span id="password_strength" class="alert"></span>
</div>
</div>
-
+
<div class="control-group">
<label class="control-label" for="password_confirm">Confirm Password</label>
<div class="controls">
@@ -88,26 +83,25 @@
data-trigger="change" data-required="true" data-equalto="#password">
</div>
</div>
-
+
<div class="control-group">
<div class="controls"">
<input class="btn btn-primary" id="submit" name="submit" type="submit" value="Create account">
</div>
</div>
-
+
</fieldset>
-
+
</form>
</div>
</div>
{% endblock %}
-{% block js %}
+{% block js %}
<!--<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>-->
-
+
{% include "new_security/_scripts.html" %}
<script type="text/javascript" src="/static/new/js_external/zxcvbn/zxcvbn-async.js"></script>
<script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
{% endblock %}
-
diff --git a/wqflask/wqflask/templates/new_security/registered.html b/wqflask/wqflask/templates/new_security/registered.html
index 391a6044..49dc961f 100644
--- a/wqflask/wqflask/templates/new_security/registered.html
+++ b/wqflask/wqflask/templates/new_security/registered.html
@@ -15,20 +15,19 @@
<h3>One last step</h3>
</div>
- <p>You will receive an email with the subject <strong>"GeneNetwork email verification"</strong>.</p>
-
+ <p>You will receive an email with the subject <strong>"{{ subject }}"</strong>.</p>
+
<p>You must click the link in the email to complete registration.</p>
-
+
<p>If you don't see the email, check your spam folder.</p>
</div>
{% endblock %}
-{% block js %}
+{% block js %}
<!--<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>-->
-
+
{% include "new_security/_scripts.html" %}
<script type="text/javascript" src="/static/new/js_external/zxcvbn/zxcvbn-async.js"></script>
<script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
{% endblock %}
-
diff --git a/wqflask/wqflask/user_manager.py b/wqflask/wqflask/user_manager.py
index a2dff7f2..766f49df 100644
--- a/wqflask/wqflask/user_manager.py
+++ b/wqflask/wqflask/user_manager.py
@@ -17,10 +17,15 @@ import hashlib
import hmac
import base64
+import urlparse
+
import simplejson as json
-from redis import StrictRedis
-Redis = StrictRedis()
+from sqlalchemy import orm
+
+#from redis import StrictRedis
+import redis
+Redis = redis.StrictRedis()
from flask import (Flask, g, render_template, url_for, request, make_response,
@@ -53,7 +58,7 @@ def timestamp():
class UserSession(object):
cookie_name = 'session_id'
-
+
def __init__(self):
cookie = request.cookies.get(self.cookie_name)
if not cookie:
@@ -70,13 +75,13 @@ class UserSession(object):
self.record = Redis.hgetall(self.redis_key)
print("record is:", self.record)
self.logged_in = True
-
-
+
+
def delete_session(self):
# And more importantly delete the redis record
Redis.delete(self.cookie_name)
print("At end of delete_session")
-
+
@app.before_request
def before_request():
g.user_session = UserSession()
@@ -105,22 +110,22 @@ class UserManager(object):
print(" ID:", dataset.id)
print(" Confidential:", dataset.check_confidentiality())
#print(" ---> self.datasets:", self.datasets)
-
+
class RegisterUser(object):
def __init__(self, kw):
self.thank_you_mode = False
self.errors = []
self.user = Bunch()
-
+
self.user.email_address = kw.get('email_address', '').strip()
if not (5 <= len(self.user.email_address) <= 50):
self.errors.append('Email Address needs to be between 5 and 50 characters.')
-
+
self.user.full_name = kw.get('full_name', '').strip()
if not (5 <= len(self.user.full_name) <= 50):
self.errors.append('Full Name needs to be between 5 and 50 characters.')
-
+
self.user.organization = kw.get('organization', '').strip()
if self.user.organization and not (5 <= len(self.user.organization) <= 50):
self.errors.append('Organization needs to be empty or between 5 and 50 characters.')
@@ -128,81 +133,94 @@ class RegisterUser(object):
password = str(kw.get('password', ''))
if not (6 <= len(password)):
self.errors.append('Password needs to be at least 6 characters.')
-
+
if kw.get('password_confirm') != password:
self.errors.append("Passwords don't match.")
-
+
if self.errors:
return
-
+
print("No errors!")
-
- self.set_password(password)
-
+
+ set_password(password, user)
+
self.user.registration_info = json.dumps(basic_info(), sort_keys=True)
-
+
self.new_user = model.User(**self.user.__dict__)
db_session.add(self.new_user)
db_session.commit()
-
+
print("Adding verification email to queue")
- self.send_email_verification()
+ #self.send_email_verification()
+ VerificationEmail(self.new_user)
print("Added verification email to queue")
-
+
self.thank_you_mode = True
-
-
- def set_password(self, password):
- pwfields = Bunch()
-
- pwfields.algorithm = "pbkdf2"
- pwfields.hashfunc = "sha256"
- #hashfunc = getattr(hashlib, pwfields.hashfunc)
-
- # 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
- pwfields.keylength = 32
-
- pwfields.created_ts = timestamp()
- # One more check on password length
- assert len(password) >= 6, "Password shouldn't be so short here"
-
- print("pwfields:", vars(pwfields))
- print("locals:", locals())
-
- enc_password = Password(password,
- pwfields.salt,
- pwfields.iterations,
- pwfields.keylength,
- pwfields.hashfunc)
-
- pwfields.password = enc_password.password
- pwfields.encrypt_time = enc_password.encrypt_time
-
- self.user.password = json.dumps(pwfields.__dict__,
- sort_keys=True,
- )
-
- def send_email_verification(self):
+
+
+def set_password(password, user):
+ pwfields = Bunch()
+
+ pwfields.algorithm = "pbkdf2"
+ pwfields.hashfunc = "sha256"
+ #hashfunc = getattr(hashlib, pwfields.hashfunc)
+
+ # 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
+ pwfields.keylength = 32
+
+ pwfields.created_ts = timestamp()
+ # One more check on password length
+ assert len(password) >= 6, "Password shouldn't be so short here"
+
+ print("pwfields:", vars(pwfields))
+ print("locals:", locals())
+
+ enc_password = Password(password,
+ pwfields.salt,
+ pwfields.iterations,
+ pwfields.keylength,
+ pwfields.hashfunc)
+
+ pwfields.password = enc_password.password
+ pwfields.encrypt_time = enc_password.encrypt_time
+
+ user.password = json.dumps(pwfields.__dict__,
+ sort_keys=True,
+ )
+
+
+class VerificationEmail(object):
+ template_name = "email/verification.txt"
+ key_prefix = "verification_code"
+ subject = "GeneNetwork email verification"
+
+ def __init__(self, user):
verification_code = str(uuid.uuid4())
- key = "verification_code:" + verification_code
-
- data = json.dumps(dict(id=self.new_user.id,
+ key = self.key_prefix + ":" + verification_code
+
+ data = json.dumps(dict(id=user.id,
timestamp=timestamp())
)
-
+
Redis.set(key, data)
two_days = 60 * 60 * 24 * 2
- Redis.expire(key, two_days)
- to = self.user.email_address
- subject = "GeneNetwork email verification"
- body = render_template("email/verification.txt",
+ Redis.expire(key, two_days)
+ to = user.email_address
+ subject = self.subject
+ body = render_template(self.template_name,
verification_code = verification_code)
send_email(to, subject, body)
+class ForgotPasswordEmail(VerificationEmail):
+ template_name = "email/forgot_password.txt"
+ key_prefix = "forgot_password_code"
+ subject = "GeneNetwork password reset"
+
+
class Password(object):
def __init__(self, unencrypted_password, salt, iterations, keylength, hashfunc):
hashfunc = getattr(hashlib, hashfunc)
@@ -215,24 +233,80 @@ class Password(object):
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(),
ip_address = request.remote_addr,
user_agent = request.headers.get('User-Agent'))
+@app.route("/manage/verify_email")
def verify_email():
- print("in verify_email request.url is:", request.url)
- verify_url_hmac(request.url)
- verification_code = request.args['code']
- data = Redis.get("verification_code:" + verification_code)
- data = json.loads(data)
- print("data is:", data)
- user = model.User.query.get(data['id'])
+ user = DecodeUser(VerificationEmail.key_prefix).user
user.confirmed = json.dumps(basic_info(), sort_keys=True)
db_session.commit()
-
+
+@app.route("/n/password_reset")
+def password_reset():
+ print("in password_reset request.url is:", request.url)
+
+ # We do this mainly just to assert that it's in proper form for displaying next page
+ # Really not necessary but doesn't hurt
+ user_encode = DecodeUser(ForgotPasswordEmail.key_prefix).reencode_standalone()
+
+ return render_template("new_security/password_reset.html", user_encode=user_encode)
+
+@app.route("/n/password_reset_step2", methods=('POST',))
+def password_reset_step2():
+ print("in password_reset request.url is:", request.url)
+
+ errors = []
+
+ user_encode = request.form['user_encode']
+ verification_code, separator, hmac = user_encode.partition(':')
+
+ hmac_verified = actual_hmac_creation(verification_code)
+ print("locals are:", locals())
+
+
+ assert hmac == hmac_verified, "Someone has been naughty"
+
+ user = DecodeUser.actual_get_user(ForgotPasswordEmail.key_prefix, verification_code)
+ print("user is:", user)
+
+ password = request.form['password']
+
+ set_password(password, user)
+ db_session.commit()
+
+ flash("Password changed successfully. You can now sign in.", "alert-info")
+ response = make_response(redirect(url_for('login')))
+
+ return response
+
+class DecodeUser(object):
+
+ def __init__(self, code_prefix):
+ verify_url_hmac(request.url)
+
+ #params = urlparse.parse_qs(url)
+
+ self.verification_code = request.args['code']
+ self.user = self.actual_get_user(code_prefix, self.verification_code)
+
+ def reencode_standalone(self):
+ hmac = actual_hmac_creation(self.verification_code)
+ return self.verification_code + ":" + hmac
+
+ @staticmethod
+ def actual_get_user(code_prefix, verification_code):
+ data = Redis.get(code_prefix + ":" + verification_code)
+ print("in get_coded_user, data is:", data)
+ data = json.loads(data)
+ print("data is:", data)
+ return model.User.query.get(data['id'])
+
+@app.route("/n/login", methods=('GET', 'POST'))
def login():
params = request.form if request.form else request.args
print("in login params are:", params)
@@ -250,10 +324,10 @@ def login():
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())
@@ -261,17 +335,17 @@ def login():
session_id_signature = actual_hmac_creation(login_rec.session_id)
session_id_signed = login_rec.session_id + ":" + session_id_signature
print("session_id_signed:", session_id_signed)
-
+
session = dict(login_time = time.time(),
user_id = user.id,
user_email_address = user.email_address)
-
- flash("Thank you for logging in.", "alert-success")
-
+
+ flash("Thank you for logging in {}.".format(user.full_name), "alert-success")
+
key = "session_id:" + login_rec.session_id
print("Key when signing:", key)
Redis.hmset(key, session)
-
+
response = make_response(redirect(url_for('index_page')))
response.set_cookie(UserSession.cookie_name, session_id_signed)
else:
@@ -281,8 +355,8 @@ def login():
db_session.add(login_rec)
db_session.commit()
return response
-
-@app.route("/n/logout")
+
+@app.route("/n/logout")
def logout():
print("Logging out...")
UserSession().delete_session()
@@ -291,15 +365,89 @@ def logout():
# Delete the cookie
response.set_cookie(UserSession.cookie_name, '', expires=0)
return response
-
-
+
+
+@app.route("/n/forgot_password")
+def forgot_password():
+ return render_template("new_security/forgot_password.html")
+
+@app.route("/n/forgot_password_submit", methods=('POST',))
+def forgot_password_submit():
+ params = request.form
+ email_address = params['email_address']
+ try:
+ user = model.User.query.filter_by(email_address=email_address).one()
+ except orm.exc.NoResultFound:
+ flash("Couldn't find a user associated with the email address {}. Sorry.".format(
+ email_address))
+ return redirect(url_for("login"))
+ ForgotPasswordEmail(user)
+ return render_template("new_security/forgot_password_step2.html",
+ subject=ForgotPasswordEmail.subject)
+
+
+
+@app.route("/manage/users")
+def manage_users():
+ template_vars = UsersManager()
+ return render_template("admin/user_manager.html", **template_vars.__dict__)
+
+@app.route("/manage/user")
+def manage_user():
+ template_vars = UserManager(request.args)
+ return render_template("admin/ind_user_manager.html", **template_vars.__dict__)
+
+@app.route("/manage/groups")
+def manage_groups():
+ template_vars = GroupsManager(request.args)
+ return render_template("admin/group_manager.html", **template_vars.__dict__)
+
+
+@app.route("/n/register", methods=('GET', 'POST'))
+def register():
+ params = None
+ errors = None
+
+ #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 = RegisterUser(params)
+ errors = result.errors
+
+ if result.thank_you_mode:
+ assert not errors, "Errors while in thank you mode? That seems wrong..."
+ return render_template("new_security/registered.html",
+ subject=VerificationEmail.subject)
+
+ return render_template("new_security/register_user.html", values=params, errors=errors)
+
+
+
+
+#@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")
+
+
+
################################# Sign and unsign #####################################
def url_for_hmac(endpoint, **values):
"""Like url_for but adds an hmac at the end to insure the url hasn't been tampered with"""
url = url_for(endpoint, **values)
-
+
hm = actual_hmac_creation(url)
if '?' in url:
combiner = "&"
@@ -323,10 +471,10 @@ def verify_url_hmac(url):
hm = actual_hmac_creation(url)
assert hm == hmac, "Unexpected url (stage 3)"
-
+
def actual_hmac_creation(stringy):
"""Helper function to create the actual hmac"""
-
+
secret = app.config['SECRET_HMAC_CODE']
hmaced = hmac.new(secret, stringy, hashlib.sha1)
@@ -339,14 +487,14 @@ def actual_hmac_creation(stringy):
app.jinja_env.globals.update(url_for_hmac=url_for_hmac)
#######################################################################################
-
+
def send_email(to, subject, body):
msg = json.dumps(dict(From="no-reply@genenetwork.org",
To=to,
Subject=subject,
Body=body))
Redis.rpush("mail_queue", msg)
-
+
#def combined_salt(user_salt):
# """Combine the master salt with the user salt...we use two seperate salts so that if the database is compromised, the
@@ -358,7 +506,7 @@ def send_email(to, subject, body):
# for x, y in user_salt, secret_salt:
# combined = combined + x + y
# return combined
-
+
class GroupsManager(object):
@@ -374,9 +522,5 @@ class RolesManager(object):
#class Password(object):
# """To generate a master password: dd if=/dev/urandom bs=32 count=1 > master_salt"""
-#
-# master_salt =
-
-
-
-
+#
+# master_salt =
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index deccf459..a060ba3c 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -51,7 +51,7 @@ from wqflask import user_manager
@app.before_request
def connect_db():
g.db = sqlalchemy.create_engine(app.config['DB_URI'])
-
+
#@app.before_request
#def trace_it():
# from wqflask import tracer
@@ -277,68 +277,6 @@ def get_temp_data():
###################################################################################################
-@app.route("/manage/users")
-def manage_users():
- template_vars = user_manager.UsersManager()
- return render_template("admin/user_manager.html", **template_vars.__dict__)
-
-@app.route("/manage/user")
-def manage_user():
- template_vars = user_manager.UserManager(request.args)
- return render_template("admin/ind_user_manager.html", **template_vars.__dict__)
-
-@app.route("/manage/groups")
-def manage_groups():
- template_vars = user_manager.GroupsManager(request.args)
- return render_template("admin/group_manager.html", **template_vars.__dict__)
-
-
-@app.route("/n/register", methods=('GET', 'POST'))
-def register():
- params = None
- errors = None
-
- #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)
- errors = result.errors
-
- if result.thank_you_mode:
- assert not errors, "Errors while in thank you mode? That seems wrong..."
- return render_template("new_security/registered.html")
-
- return render_template("new_security/register_user.html", values=params, errors=errors)
-
-#@app.route("/n/register_submit", methods=('POST',))
-#def register_submit():
-# print("request.args are: ", request.args)
-# result = user_manager.RegisterUser(request.form)
-# if result.errors:
-# print("Redirecting")
-# # 307 preserves the post on the redirect (maybe)
-# errors = result.errors
-# #errors = json.dumps(errors)
-# print("request.args are: ", request.args)
-# return render_template("new_security/register_user.html", errors=errors, values=request.form)
-# #return redirect(url_for('new_register', errors=errors), code=307)
-
-
-@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")
-
##########################################################################