about summary refs log tree commit diff
path: root/wqflask/wqflask
diff options
context:
space:
mode:
authorzsloan2018-03-29 15:46:45 +0000
committerzsloan2018-03-29 15:46:45 +0000
commitfef4b723d0e9d9d0b5f40bd51c6a2cd31410285b (patch)
tree22268c6f4db65f637070319a497de24b82843f0f /wqflask/wqflask
parent6ff7df2360d1a6d0461980c938809165982583e8 (diff)
parentb215b5fe5c6d13f0ed445106230e1e38db71c918 (diff)
downloadgenenetwork2-fef4b723d0e9d9d0b5f40bd51c6a2cd31410285b.tar.gz
Resolved conflict in views.py
Diffstat (limited to 'wqflask/wqflask')
-rw-r--r--wqflask/wqflask/__init__.py4
-rw-r--r--wqflask/wqflask/correlation/show_corr_results.py42
-rw-r--r--wqflask/wqflask/gsearch.py6
-rw-r--r--wqflask/wqflask/marker_regression/marker_regression.py2
-rw-r--r--wqflask/wqflask/search_results.py23
-rw-r--r--wqflask/wqflask/templates/new_security/login_user.html49
-rw-r--r--wqflask/wqflask/user_manager.py317
-rw-r--r--wqflask/wqflask/views.py71
8 files changed, 339 insertions, 175 deletions
diff --git a/wqflask/wqflask/__init__.py b/wqflask/wqflask/__init__.py
index 2188ce17..bc8e9900 100644
--- a/wqflask/wqflask/__init__.py
+++ b/wqflask/wqflask/__init__.py
@@ -13,8 +13,8 @@ logging.basicConfig(level=logging.INFO)
 app = Flask(__name__)
 
 app.config.from_object('cfg.default_settings')   # Get the defaults from cfg.default_settings
-app.config.from_envvar('WQFLASK_SETTINGS')       # See http://flask.pocoo.org/docs/config/#configuring-from-files
-# Note we also use WQFLASK_OVERRIDES
+app.config.from_envvar('GN2_SETTINGS')       # See http://flask.pocoo.org/docs/config/#configuring-from-files
+# Note no longer use the badly named WQFLASK_OVERRIDES (nyi)
 
 app.jinja_env.globals.update(
     undefined = jinja2.StrictUndefined,
diff --git a/wqflask/wqflask/correlation/show_corr_results.py b/wqflask/wqflask/correlation/show_corr_results.py
index 12f76b7d..73072423 100644
--- a/wqflask/wqflask/correlation/show_corr_results.py
+++ b/wqflask/wqflask/correlation/show_corr_results.py
@@ -52,6 +52,7 @@ import utility.webqtlUtil #this is for parallel computing only.
 from wqflask.correlation import correlation_functions
 from utility.benchmark import Bench
 import utility.webqtlUtil
+from utility.type_checking import is_float, is_int, is_str, get_float, get_int, get_string
 from wqflask import user_manager
 
 from MySQLdb import escape_string as escape
@@ -77,47 +78,6 @@ def print_mem(stage=""):
     mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
     #print("{}: {}".format(stage, mem/1024))
 
-def is_float(value):
-    try:
-        float(value)
-        return True
-    except:
-        return False
-
-def is_int(value):
-    try:
-        int(value)
-        return True
-    except:
-        return False
-
-def is_str(value):
-    if value is None:
-        return False
-    try:
-        str(value)
-        return True
-    except:
-        return False
-
-def get_float(vars,name,default=None):
-    if name in vars:
-        if is_float(vars[name]):
-            return float(vars[name])
-    return None
-
-def get_int(vars,name,default=None):
-    if name in vars:
-        if is_int(vars[name]):
-            return float(vars[name])
-    return default
-
-def get_string(vars,name,default=None):
-    if name in vars:
-        if not vars[name] is None:
-            return str(vars[name])
-    return default
-
 class AuthException(Exception):
     pass
 
diff --git a/wqflask/wqflask/gsearch.py b/wqflask/wqflask/gsearch.py
index e33e04e1..fe1e17d2 100644
--- a/wqflask/wqflask/gsearch.py
+++ b/wqflask/wqflask/gsearch.py
@@ -5,6 +5,7 @@ from base.data_set import create_dataset
 from base.trait import GeneralTrait
 from db import webqtlDatabaseFunction
 
+from utility.type_checking import is_float, is_int, is_str, get_float, get_int, get_string
 from utility.benchmark import Bench
 
 from utility.logger import getLogger
@@ -13,8 +14,13 @@ logger = getLogger(__name__)
 class GSearch(object):
 
     def __init__(self, kw):
+        assert('type' in kw)
+        assert('terms' in kw)
+
         self.type = kw['type']
         self.terms = kw['terms']
+        assert(is_str(self.type))
+
         if self.type == "gene":
             sql = """
                 SELECT
diff --git a/wqflask/wqflask/marker_regression/marker_regression.py b/wqflask/wqflask/marker_regression/marker_regression.py
index db4f8f46..3ec61e55 100644
--- a/wqflask/wqflask/marker_regression/marker_regression.py
+++ b/wqflask/wqflask/marker_regression/marker_regression.py
@@ -114,7 +114,7 @@ class MarkerRegression(object):
         self.num_perm = 0
         self.perm_output = []
         self.bootstrap_results = []
-        self.covariates = start_vars['covariates']
+        self.covariates = start_vars['covariates'] if "covariates" in start_vars else None
 
         #ZS: This is passed to GN1 code for single chr mapping
         self.selected_chr = -1
diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py
index 59e100d8..53c96591 100644
--- a/wqflask/wqflask/search_results.py
+++ b/wqflask/wqflask/search_results.py
@@ -10,6 +10,7 @@ import time
 import math
 import datetime
 import collections
+import re
 
 from pprint import pformat as pf
 
@@ -25,6 +26,7 @@ from db import webqtlDatabaseFunction
 from flask import render_template
 
 from utility import formatting
+from utility.type_checking import is_float, is_int, is_str, get_float, get_int, get_string
 
 from utility.logger import getLogger
 logger = getLogger(__name__ )
@@ -64,14 +66,26 @@ views.py).
         else:
             self.and_or = "and"
             self.search_terms = kw['search_terms_and']
-        self.search_term_exists = True
+        search = self.search_terms
+        # check for dodgy search terms
+        rx = re.compile(r'.*\W(href|http|sql|select|update)\W.*',re.IGNORECASE)
+        if rx.match(search):
+            logger.info("Regex failed search")
+            self.search_term_exists = False
+            return
+        else:
+            self.search_term_exists = True
+
         self.results = []
-        if kw['type'] == "Phenotypes":     # split datatype on type field
+        type = kw.get('type')
+        if type == "Phenotypes":     # split datatype on type field
             dataset_type = "Publish"
-        elif kw['type'] == "Genotypes":
+        elif type == "Genotypes":
             dataset_type = "Geno"
         else:
             dataset_type = "ProbeSet"      # ProbeSet is default
+
+        assert(is_str(kw.get('dataset')))
         self.dataset = create_dataset(kw['dataset'], dataset_type)
         logger.debug("search_terms:", self.search_terms)
         self.search()
@@ -145,6 +159,7 @@ statement and executes
                         else:
                             combined_where_clause += "OR"
                 else:
+                    logger.debug("Search failed 1")
                     self.search_term_exists = False
             if self.search_term_exists:
                 combined_where_clause = "(" + combined_where_clause + ")"
@@ -155,6 +170,7 @@ statement and executes
         else:
             logger.debug("len(search_terms)<=1")
             if self.search_terms == []:
+                logger.debug("Search failed 2")
                 self.search_term_exists = False
             else:
                 for a_search in self.search_terms:
@@ -162,6 +178,7 @@ statement and executes
                     if the_search != None:
                         self.results.extend(the_search.run())
                     else:
+                        logger.debug("Search failed 3")
                         self.search_term_exists = False
 
         if self.search_term_exists:
diff --git a/wqflask/wqflask/templates/new_security/login_user.html b/wqflask/wqflask/templates/new_security/login_user.html
index b9f49a61..949760b6 100644
--- a/wqflask/wqflask/templates/new_security/login_user.html
+++ b/wqflask/wqflask/templates/new_security/login_user.html
@@ -15,15 +15,39 @@
 
             <h4>Don't have an account?</h4>
 
-
-            <a href="/n/register" class="btn btn-primary modalize">Create a new account</a>
-
-
-            <hr />
+            {% if es_server: %}
+	    <a href="/n/register" class="btn btn-primary modalize">Create a new account</a>
+	    {% else: %}
+	    <div class="alert alert-warning">
+	      <p>You cannot create an account at this moment.<br />
+		Please try again later.</p>
+	    </div>
+	    {% endif %}
+
+	    <hr />
+	    <h4>Login with external services</h4>
+
+	    {% if external_login: %}
+	    <div>
+	      {% if external_login["github"]: %}
+	      <a href="{{external_login['github']}}" title="Login with GitHub" class="btn btn-info btn-group">Login with Github</a>
+	      {% endif %}
+
+	      {% if external_login["orcid"]: %}
+	      <a href="{{external_login['orcid']}}" title="Login with ORCID" class="btn btn-info btn-group">Login with ORCID</a>
+	      {% endif %}
+	    </div>
+	    {% else: %}
+	    <div class="alert alert-warning">
+	      <p>You cannot login with external services at this time.<br />
+		Please try again later.</p>
+	    </div>
+	    {% endif %}
+	    <hr />
 
             <h4>Already have an account? Sign in here.</h4>
 
-
+	    {% if es_server: %}
             <form class="form-horizontal" action="/n/login" method="POST" name="login_user_form" id="loginUserForm">
                  <fieldset>
                     <div class="form-group">
@@ -61,6 +85,19 @@
               </fieldset>
 
             </form>
+	    {% else: %}
+	    <div class="alert alert-warning">
+	      <p>You cannot login at this moment using your GeneNetwork account (the authentication service is down).<br />
+		Please try again later.</p>
+	    </div>
+	    {% endif %}
+            {% if not es_server and not external_login: %}
+            <hr>
+	    <div class="alert alert-warning">
+               Note: it is safe to use GeneNetwork without a login. Login is only required for keeping track of
+            collections and getting access to some types of restricted data.
+            </div>
+            {% endif %}
         </div>
     </div>
 
diff --git a/wqflask/wqflask/user_manager.py b/wqflask/wqflask/user_manager.py
index f7fcd2d0..c55649f3 100644
--- a/wqflask/wqflask/user_manager.py
+++ b/wqflask/wqflask/user_manager.py
@@ -54,6 +54,9 @@ logger = getLogger(__name__)
 
 from base.data_set import create_datasets_list
 
+import requests
+from utility.elasticsearch_tools import get_elasticsearch_connection, get_user_by_unique_column, save_user
+
 THREE_DAYS = 60 * 60 * 24 * 3
 #THREE_DAYS = 45
 
@@ -119,7 +122,8 @@ class AnonUser(object):
             return collections
 
     def import_traits_to_user(self):
-        collections_list = json.loads(Redis.get(self.key))
+        result = Redis.get(self.key)
+        collections_list = json.loads(result if result else "[]")
         for collection in collections_list:
             uc = model.UserCollection()
             uc.name = collection['name']
@@ -268,16 +272,24 @@ class RegisterUser(object):
         self.thank_you_mode = False
         self.errors = []
         self.user = Bunch()
+        es = kw.get('es_connection', None)
+
+        if not es:
+            self.errors.append("Missing connection object")
 
-        self.user.email_address = kw.get('email_address', '').strip()
+        self.user.email_address = kw.get('email_address', '').encode("utf-8").strip()
         if not (5 <= len(self.user.email_address) <= 50):
             self.errors.append('Email Address needs to be between 5 and 50 characters.')
+        else:
+            email_exists = get_user_by_unique_column(es, "email_address", self.user.email_address)
+            if email_exists:
+                self.errors.append('User already exists with that email')
 
-        self.user.full_name = kw.get('full_name', '').strip()
+        self.user.full_name = kw.get('full_name', '').encode("utf-8").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()
+        self.user.organization = kw.get('organization', '').encode("utf-8").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.')
 
@@ -294,28 +306,11 @@ class RegisterUser(object):
         logger.debug("No errors!")
 
         set_password(password, self.user)
+        self.user.user_id = str(uuid.uuid4())
+        self.user.confirmed = 1
 
         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)
-
-        try:
-            db_session.commit()
-        except sqlalchemy.exc.IntegrityError:
-            # This exception is thrown if the email address is already in the database
-            # To do: Perhaps put a link to sign in using an existing account here
-            self.errors.append("An account with this email address already exists. "
-                               "Click the button above to sign in using an existing account.")
-            return
-
-        logger.debug("Adding verification email to queue")
-        #self.send_email_verification()
-        VerificationEmail(self.new_user)
-        logger.debug("Added verification email to queue")
-
-        self.thank_you_mode = True
-
+        save_user(es, self.user.__dict__, self.user.user_id)
 
 def set_password(password, user):
     pwfields = Bunch()
@@ -361,7 +356,7 @@ class VerificationEmail(object):
         verification_code = str(uuid.uuid4())
         key = self.key_prefix + ":" + verification_code
 
-        data = json.dumps(dict(id=user.id,
+        data = json.dumps(dict(id=user.user_id,
                                timestamp=timestamp())
                           )
 
@@ -378,23 +373,33 @@ class ForgotPasswordEmail(VerificationEmail):
     template_name = "email/forgot_password.txt"
     key_prefix = "forgot_password_code"
     subject = "GeneNetwork password reset"
+    fromaddr = "no-reply@genenetwork.org"
 
-    def __init__(self, user):
+    def __init__(self, toaddr):
+        from email.MIMEMultipart import MIMEMultipart
+        from email.MIMEText import MIMEText
         verification_code = str(uuid.uuid4())
         key = self.key_prefix + ":" + verification_code
 
-        data = json.dumps(dict(id=user.id,
-                               timestamp=timestamp())
-                          )
+        data = {
+            "verification_code": verification_code,
+            "email_address": toaddr,
+            "timestamp": timestamp()
+        }
+        es_save_data(es, self.key_prefix, "local", data, verification_code)
 
-        Redis.set(key, data)
-        #two_days = 60 * 60 * 24 * 2
-        Redis.expire(key, THREE_DAYS)
-        to = user.email_address
         subject = self.subject
-        body = render_template(self.template_name,
-                               verification_code = verification_code)
-        send_email(to, subject, body)
+        body = render_template(
+            self.template_name,
+            verification_code = verification_code)
+
+        msg = MIMEMultipart()
+        msg["To"] = toaddr
+        msg["Subject"] = self.subject
+        msg["From"] = self.fromaddr
+        msg.attach(MIMEText(body, "plain"))
+
+        send_email(toaddr, msg.as_string())
 
 
 class Password(object):
@@ -429,38 +434,61 @@ def verify_email():
     response.set_cookie(UserSession.cookie_name, session_id_signed)
     return response
 
-@app.route("/n/password_reset")
+@app.route("/n/password_reset", methods=['GET'])
 def password_reset():
     logger.debug("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)
+    # user_encode = DecodeUser(ForgotPasswordEmail.key_prefix).reencode_standalone()
+    verification_code = request.args.get('code')
+    hmac = request.args.get('hm')
+    if verification_code:
+        code_details = get_item_by_unique_column(
+            es
+            , "verification_code"
+            , verification_code
+            , ForgotPasswordEmail.key_prefix
+            , "local")
+        if code_details:
+            user_details = get_user_by_unique_column(
+                es
+                , "email_address"
+                , code_details["email_address"])
+            if user_details:
+                return render_template(
+                    "new_security/password_reset.html", user_encode=user_details["user_id"])
+            else:
+                flash("Invalid code: User no longer exists!", "error")
+        else:
+            flash("Invalid code: Password reset code does not exist or might have expired!", "error")
+        return redirect(url_for("login"))#render_template("new_security/login_user.html", error=error)
 
 @app.route("/n/password_reset_step2", methods=('POST',))
 def password_reset_step2():
+    from utility.elasticsearch_tools import es
     logger.debug("in password_reset request.url is:", request.url)
 
     errors = []
+    user_id = request.form['user_encode']
 
-    user_encode = request.form['user_encode']
-    verification_code, separator, hmac = user_encode.partition(':')
-
-    hmac_verified = actual_hmac_creation(verification_code)
     logger.debug("locals are:", locals())
 
 
-    assert hmac == hmac_verified, "Someone has been naughty"
-
-    user = DecodeUser.actual_get_user(ForgotPasswordEmail.key_prefix, verification_code)
-    logger.debug("user is:", user)
-
+    user = Bunch()
     password = request.form['password']
-
     set_password(password, user)
-    db_session.commit()
+
+    es = get_elasticsearch_connection()
+    es.update(
+        index = "users"
+        , doc_type = "local"
+        , id = user_id
+        , body = {
+            "doc": {
+                "password": user.__dict__.get("password")
+            }
+        })
 
     flash("Password changed successfully. You can now sign in.", "alert-info")
     response = make_response(redirect(url_for('login')))
@@ -492,8 +520,85 @@ class DecodeUser(object):
 @app.route("/n/login", methods=('GET', 'POST'))
 def login():
     lu = LoginUser()
-    return lu.standard_login()
+    login_type = request.args.get("type")
+    if login_type:
+        uid = request.args.get("uid")
+        return lu.oauth2_login(login_type, uid)
+    else:
+        return lu.standard_login()
+
+@app.route("/n/login/github_oauth2", methods=('GET', 'POST'))
+def github_oauth2():
+    from utility.tools import GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
+    code = request.args.get("code")
+    data = {
+        "client_id": GITHUB_CLIENT_ID,
+        "client_secret": GITHUB_CLIENT_SECRET,
+        "code": code
+    }
+    result = requests.post("https://github.com/login/oauth/access_token", json=data)
+    result_dict = {arr[0]:arr[1] for arr in [tok.split("=") for tok in [token.encode("utf-8") for token in result.text.split("&")]]}
+
+    github_user = get_github_user_details(result_dict["access_token"])
+    es = get_elasticsearch_connection()
+    user_details = get_user_by_unique_column(es, "github_id", github_user["id"])
+    if user_details == None:
+        user_details = {
+            "user_id": str(uuid.uuid4())
+            , "name": github_user["name"].encode("utf-8")
+            , "github_id": github_user["id"]
+            , "user_url": github_user["html_url"].encode("utf-8")
+            , "login_type": "github"
+            , "organization": ""
+            , "active": 1
+            , "confirmed": 1
+        }
+        save_user(es, user_details, user_details["user_id"])
+    url = "/n/login?type=github&uid="+user_details["user_id"]
+    return redirect(url)
+
+@app.route("/n/login/orcid_oauth2", methods=('GET', 'POST'))
+def orcid_oauth2():
+    from uuid import uuid4
+    from utility.tools import ORCID_CLIENT_ID, ORCID_CLIENT_SECRET, ORCID_TOKEN_URL, ORCID_AUTH_URL
+    code = request.args.get("code")
+    error = request.args.get("error")
+    url = "/n/login"
+    if code:
+        data = {
+            "client_id": ORCID_CLIENT_ID
+            , "client_secret": ORCID_CLIENT_SECRET
+            , "grant_type": "authorization_code"
+            , "code": code
+        }
+        result = requests.post(ORCID_TOKEN_URL, data=data)
+        result_dict = json.loads(result.text.encode("utf-8"))
+
+        es = get_elasticsearch_connection()
+        user_details = get_user_by_unique_column(es, "orcid", result_dict["orcid"])
+        if user_details == None:
+            user_details = {
+                "user_id": str(uuid4())
+                , "name": result_dict["name"]
+                , "orcid": result_dict["orcid"]
+                , "user_url": "%s/%s" % (
+                    "/".join(ORCID_AUTH_URL.split("/")[:-2]),
+                    result_dict["orcid"])
+                , "login_type": "orcid"
+                , "organization": ""
+                , "active": 1
+                , "confirmed": 1
+            }
+            save_user(es, user_details, user_details["user_id"])
+        url = "/n/login?type=orcid&uid="+user_details["user_id"]
+    else:
+        flash("There was an error getting code from ORCID")
+    return redirect(url)
 
+def get_github_user_details(access_token):
+    from utility.tools import GITHUB_API_URL
+    result = requests.get(GITHUB_API_URL, params={"access_token":access_token})
+    return result.json()
 
 class LoginUser(object):
     remember_time = 60 * 60 * 24 * 30 # One month in seconds
@@ -501,27 +606,55 @@ class LoginUser(object):
     def __init__(self):
         self.remember_me = False
 
+    def oauth2_login(self, login_type, user_id):
+        """Login via an OAuth2 provider"""
+        es = get_elasticsearch_connection()
+        user_details = get_user_by_unique_column(es, "user_id", user_id)
+        if user_details:
+            user = model.User()
+            user.id = user_details["user_id"]
+            user.full_name = user_details["name"]
+            user.login_type = user_details["login_type"]
+            return self.actual_login(user)
+        else:
+            flash("Error logging in via OAuth2")
+            return make_response(redirect(url_for('login')))
+
     def standard_login(self):
         """Login through the normal form"""
         params = request.form if request.form else request.args
         logger.debug("in login params are:", params)
+        es = get_elasticsearch_connection()
         if not params:
-            return render_template("new_security/login_user.html")
+            from utility.tools import GITHUB_AUTH_URL, GITHUB_CLIENT_ID, ORCID_AUTH_URL, ORCID_CLIENT_ID
+            external_login = {}
+            if GITHUB_AUTH_URL and GITHUB_CLIENT_ID != 'UNKNOWN':
+                external_login["github"] = GITHUB_AUTH_URL
+            if ORCID_AUTH_URL and ORCID_CLIENT_ID != 'UNKNOWN':
+                external_login["orcid"] = ORCID_AUTH_URL
+            assert(es is not None)
+            return render_template(
+                "new_security/login_user.html"
+                , external_login=external_login
+                , es_server=es.ping())
         else:
-            try:
-                user = model.User.query.filter_by(email_address=params['email_address']).one()
-            except sqlalchemy.orm.exc.NoResultFound:
-                logger.debug("No account exists for that email address")
-                valid = False
-                user = None
-            else:
+            user_details = get_user_by_unique_column(es, "email_address", params["email_address"])
+            user = None
+            valid = None
+            if user_details:
+                user = model.User();
+                for key in user_details:
+                    user.__dict__[key] = user_details[key]
+                valid = False;
+
                 submitted_password = params['password']
                 pwfields = Struct(json.loads(user.password))
-                encrypted = Password(submitted_password,
-                                              pwfields.salt,
-                                              pwfields.iterations,
-                                              pwfields.keylength,
-                                              pwfields.hashfunc)
+                encrypted = Password(
+                    submitted_password,
+                    pwfields.salt,
+                    pwfields.iterations,
+                    pwfields.keylength,
+                    pwfields.hashfunc)
                 logger.debug("\n\nComparing:\n{}\n{}\n".format(encrypted.password, pwfields.password))
                 valid = pbkdf2.safe_str_cmp(encrypted.password, pwfields.password)
                 logger.debug("valid is:", valid)
@@ -549,7 +682,7 @@ class LoginUser(object):
         else:
             if user:
                 self.unsuccessful_login(user)
-            flash("Invalid email-address or password. Please try again.", "alert-error")
+            flash("Invalid email-address or password. Please try again.", "alert-danger")
             response = make_response(redirect(url_for('login')))
 
             return response
@@ -558,7 +691,6 @@ class LoginUser(object):
         """The meat of the logging in process"""
         session_id_signed = self.successful_login(user, assumed_by)
         flash("Thank you for logging in {}.".format(user.full_name), "alert-success")
-        print("IMPORT1:", import_collections)
         response = make_response(redirect(url_for('index_page', import_collections=import_collections)))
         if self.remember_me:
             max_age = self.remember_time
@@ -589,8 +721,6 @@ class LoginUser(object):
         else:
             expire_time = THREE_DAYS
         Redis.expire(key, expire_time)
-        db_session.add(login_rec)
-        db_session.commit()
         return session_id_signed
 
     def unsuccessful_login(self, user):
@@ -618,13 +748,16 @@ def forgot_password():
 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)
+    user_details = get_user_by_unique_column(es, "email_address", email_address)
+    if user_details:
+        ForgotPasswordEmail(user_details["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)
 
@@ -691,16 +824,18 @@ def register():
 
 
     params = request.form if request.form else request.args
+    params = params.to_dict(flat=True)
+    es = get_elasticsearch_connection()
+    params["es_connection"] = es
 
     if params:
         logger.debug("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)
+        if len(errors) == 0:
+            flash("Registration successful. You may login with your new account", "alert-info")
+            return redirect(url_for("login"))
 
     return render_template("new_security/register_user.html", values=params, errors=errors)
 
@@ -771,13 +906,21 @@ 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 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 send_email(toaddr, msg, fromaddr="no-reply@genenetwork.org"):
+    from smtplib import SMTP
+    from utility.tools import SMTP_CONNECT, SMTP_USERNAME, SMTP_PASSWORD
+    server = SMTP(SMTP_CONNECT)
+    server.starttls()
+    server.login(SMTP_USERNAME, SMTP_PASSWORD)
+    server.sendmail(fromaddr, toaddr, msg)
+    server.quit()
 
 
 
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 998e5302..a65924d8 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -123,7 +123,7 @@ def handle_bad_request(e):
 @app.route("/")
 def index_page():
     logger.info("Sending index_page")
-    logger.debug(request.url)
+    logger.info(request.url)
     params = request.args
     if 'import_collections' in params:
         import_collections = params['import_collections']
@@ -141,7 +141,7 @@ def index_page():
 def tmp_page(img_path):
     logger.info("In tmp_page")
     logger.info("img_path:", img_path)
-    logger.debug(request.url)
+    logger.info(request.url)
     initial_start_vars = request.form
     logger.info("initial_start_vars:", initial_start_vars)
     imgfile = open(GENERATED_IMAGE_DIR + img_path, 'rb')
@@ -174,7 +174,7 @@ def twitter(filename):
 @app.route("/search", methods=('GET',))
 def search_page():
     logger.info("in search_page")
-    logger.debug(request.url)
+    logger.info(request.url)
     if 'info_database' in request.args:
         logger.info("Going to sharing_info_page")
         template_vars = sharing_info_page()
@@ -199,21 +199,22 @@ def search_page():
         logger.info("request.args is", request.args)
         the_search = search_results.SearchResultPage(request.args)
         result = the_search.__dict__
+        valid_search = result['search_term_exists']
 
         logger.debugf("result", result)
 
-        if USE_REDIS:
+        if USE_REDIS and valid_search:
             Redis.set(key, pickle.dumps(result, pickle.HIGHEST_PROTOCOL))
             Redis.expire(key, 60*60)
 
-        if result['search_term_exists']:
+        if valid_search:
             return render_template("search_result_page.html", **result)
         else:
             return render_template("search_error.html")
 
 @app.route("/gsearch", methods=('GET',))
 def gsearchact():
-    logger.debug(request.url)
+    logger.info(request.url)
     result = gsearch.GSearch(request.args).__dict__
     type = request.args['type']
     if type == "gene":
@@ -224,7 +225,7 @@ def gsearchact():
 @app.route("/gsearch_updating", methods=('POST',))
 def gsearch_updating():
     logger.info("REQUEST ARGS:", request.values)
-    logger.debug(request.url)
+    logger.info(request.url)
     result = update_search_results.GSearch(request.args).__dict__
     return result['results']
     # type = request.args['type']
@@ -235,31 +236,31 @@ def gsearch_updating():
 
 @app.route("/docedit")
 def docedit():
-    logger.debug(request.url)
+    logger.info(request.url)
     doc = docs.Docs(request.args['entry'])
     return render_template("docedit.html", **doc.__dict__)
 
 @app.route('/generated/<filename>')
 def generated_file(filename):
-    logger.debug(request.url)
+    logger.info(request.url)
     return send_from_directory(GENERATED_IMAGE_DIR,filename)
 
 @app.route("/help")
 def help():
-    logger.debug(request.url)
+    logger.info(request.url)
     doc = docs.Docs("help")
     return render_template("docs.html", **doc.__dict__)
 
 @app.route("/wgcna_setup", methods=('POST',))
 def wcgna_setup():
     logger.info("In wgcna, request.form is:", request.form)             # We are going to get additional user input for the analysis
-    logger.debug(request.url)
+    logger.info(request.url)
     return render_template("wgcna_setup.html", **request.form)          # Display them using the template
 
 @app.route("/wgcna_results", methods=('POST',))
 def wcgna_results():
     logger.info("In wgcna, request.form is:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     wgcna = wgcna_analysis.WGCNA()                                # Start R, load the package and pointers and create the analysis
     wgcnaA = wgcna.run_analysis(request.form)                     # Start the analysis, a wgcnaA object should be a separate long running thread
     result = wgcna.process_results(wgcnaA)                        # After the analysis is finished store the result
@@ -268,13 +269,13 @@ def wcgna_results():
 @app.route("/ctl_setup", methods=('POST',))
 def ctl_setup():
     logger.info("In ctl, request.form is:", request.form)             # We are going to get additional user input for the analysis
-    logger.debug(request.url)
+    logger.info(request.url)
     return render_template("ctl_setup.html", **request.form)          # Display them using the template
 
 @app.route("/ctl_results", methods=('POST',))
 def ctl_results():
     logger.info("In ctl, request.form is:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     ctl = ctl_analysis.CTL()                                  # Start R, load the package and pointers and create the analysis
     ctlA = ctl.run_analysis(request.form)                     # Start the analysis, a ctlA object should be a separate long running thread
     result = ctl.process_results(ctlA)                        # After the analysis is finished store the result
@@ -313,13 +314,13 @@ def environments():
 
 @app.route("/submit_trait")
 def submit_trait_form():
-    logger.debug(request.url)
+    logger.info(request.url)
     species_and_groups = get_species_groups()
     return render_template("submit_trait.html", **{'species_and_groups' : species_and_groups, 'gn_server_url' : GN_SERVER_URL, 'version' : GN_VERSION})
 
 @app.route("/create_temp_trait", methods=('POST',))
 def create_temp_trait():
-    logger.debug(request.url)
+    logger.info(request.url)
     print("REQUEST.FORM:", request.form)
     #template_vars = submit_trait.SubmitTrait(request.form)
 
@@ -332,7 +333,7 @@ def export_trait_excel():
     """Excel file consisting of the sample data from the trait data and analysis page"""
     logger.info("In export_trait_excel")
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     sample_data = export_trait_data.export_sample_table(request.form)
 
     logger.info("sample_data - type: %s -- size: %s" % (type(sample_data), len(sample_data)))
@@ -358,7 +359,7 @@ def export_trait_csv():
     """CSV file consisting of the sample data from the trait data and analysis page"""
     logger.info("In export_trait_csv")
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     sample_data = export_trait_data.export_sample_table(request.form)
 
     logger.info("sample_data - type: %s -- size: %s" % (type(sample_data), len(sample_data)))
@@ -379,7 +380,7 @@ def export_traits_csv():
     """CSV file consisting of the traits from the search result page"""
     logger.info("In export_traits_csv")
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     csv_data = export_traits.export_search_results_csv(request.form)
 
     return Response(csv_data,
@@ -389,7 +390,7 @@ def export_traits_csv():
 @app.route('/export_perm_data', methods=('POST',))
 def export_perm_data():
     """CSV file consisting of the permutation data for the mapping results"""
-    logger.debug(request.url)
+    logger.info(request.url)
     num_perm = float(request.form['num_perm'])
     perm_data = json.loads(request.form['perm_results'])
 
@@ -412,7 +413,7 @@ def export_perm_data():
 
 @app.route("/show_temp_trait", methods=('POST',))
 def show_temp_trait_page():
-    logger.debug(request.url)
+    logger.info(request.url)
     template_vars = show_trait.ShowTrait(request.form)
     #logger.info("js_data before dump:", template_vars.js_data)
     template_vars.js_data = json.dumps(template_vars.js_data,
@@ -427,7 +428,7 @@ def show_temp_trait_page():
 
 @app.route("/show_trait")
 def show_trait_page():
-    logger.debug(request.url)
+    logger.info(request.url)
     template_vars = show_trait.ShowTrait(request.args)
     #logger.info("js_data before dump:", template_vars.js_data)
     template_vars.js_data = json.dumps(template_vars.js_data,
@@ -443,7 +444,7 @@ def show_trait_page():
 @app.route("/heatmap", methods=('POST',))
 def heatmap_page():
     logger.info("In heatmap, request.form is:", pf(request.form))
-    logger.debug(request.url)
+    logger.info(request.url)
 
     start_vars = request.form
     temp_uuid = uuid.uuid4()
@@ -493,7 +494,7 @@ def mapping_results_container_page():
 
 @app.route("/loading", methods=('POST',))
 def loading_page():
-    logger.debug(request.url)
+    logger.info(request.url)
     initial_start_vars = request.form
     logger.debug("Marker regression called with initial_start_vars:", initial_start_vars.items())
     #temp_uuid = initial_start_vars['temp_uuid']
@@ -552,7 +553,7 @@ def loading_page():
 def marker_regression_page():
     initial_start_vars = request.form
     logger.debug("Marker regression called with initial_start_vars:", initial_start_vars.items())
-    logger.debug(request.url)
+    logger.info(request.url)
     temp_uuid = initial_start_vars['temp_uuid']
     wanted = (
         'trait_id',
@@ -678,7 +679,7 @@ def marker_regression_page():
 @app.route("/export_mapping_results", methods = ('POST',))
 def export_mapping_results():
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     file_path = request.form.get("results_path")
     results_csv = open(file_path, "r").read()
     response = Response(results_csv,
@@ -691,7 +692,7 @@ def export_mapping_results():
 @app.route("/export", methods = ('POST',))
 def export():
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     svg_xml = request.form.get("data", "Invalid data")
     filename = request.form.get("filename", "manhattan_plot_snp")
     response = Response(svg_xml, mimetype="image/svg+xml")
@@ -702,7 +703,7 @@ def export():
 def export_pdf():
     import cairosvg
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     svg_xml = request.form.get("data", "Invalid data")
     logger.info("svg_xml:", svg_xml)
     filename = request.form.get("filename", "interval_map_pdf")
@@ -715,7 +716,7 @@ def export_pdf():
 @app.route("/network_graph", methods=('POST',))
 def network_graph_page():
     logger.info("In network_graph, request.form is:", pf(request.form))
-    logger.debug(request.url)
+    logger.info(request.url)
     start_vars = request.form
     traits = [trait.strip() for trait in start_vars['trait_list'].split(',')]
     if traits[0] != "":
@@ -731,7 +732,7 @@ def network_graph_page():
 @app.route("/corr_compute", methods=('POST',))
 def corr_compute_page():
     logger.info("In corr_compute, request.form is:", pf(request.form))
-    logger.debug(request.url)
+    logger.info(request.url)
     #fd = webqtlFormData.webqtlFormData(request.form)
     template_vars = show_corr_results.CorrelationResults(request.form)
     return render_template("correlation_page.html", **template_vars.__dict__)
@@ -739,7 +740,7 @@ def corr_compute_page():
 @app.route("/corr_matrix", methods=('POST',))
 def corr_matrix_page():
     logger.info("In corr_matrix, request.form is:", pf(request.form))
-    logger.debug(request.url)
+    logger.info(request.url)
 
     start_vars = request.form
     traits = [trait.strip() for trait in start_vars['trait_list'].split(',')]
@@ -755,7 +756,7 @@ def corr_matrix_page():
 
 @app.route("/corr_scatter_plot")
 def corr_scatter_plot_page():
-    logger.debug(request.url)
+    logger.info(request.url)
     template_vars = corr_scatter_plot.CorrScatterPlot(request.args)
     template_vars.js_data = json.dumps(template_vars.js_data,
                                        default=json_default_handler,
@@ -764,7 +765,7 @@ def corr_scatter_plot_page():
 
 @app.route("/submit_bnw", methods=('POST',))
 def submit_bnw():
-    logger.debug(request.url)
+    logger.info(request.url)
     template_vars = get_bnw_input(request.form)
     return render_template("empty_collection.html", **{'tool':'Correlation Matrix'})
 
@@ -772,7 +773,7 @@ def submit_bnw():
 def sharing_info_page():
     """Info page displayed when the user clicks the "Info" button next to the dataset selection"""
     logger.info("In sharing_info_page")
-    logger.debug(request.url)
+    logger.info(request.url)
     fd = webqtlFormData.webqtlFormData(request.args)
     template_vars = SharingInfoPage.SharingInfoPage(fd)
     return template_vars
@@ -780,7 +781,7 @@ def sharing_info_page():
 # Take this out or secure it before putting into production
 @app.route("/get_temp_data")
 def get_temp_data():
-    logger.debug(request.url)
+    logger.info(request.url)
     temp_uuid = request.args['key']
     return flask.jsonify(temp_data.TempData(temp_uuid).get_all())