diff options
author | zsloan | 2020-03-09 11:54:34 -0500 |
---|---|---|
committer | zsloan | 2020-03-09 11:54:34 -0500 |
commit | 04b32f95279602da4cfb00b4f356a04eee48af51 (patch) | |
tree | 4669f99ed75c0a39164c3b277e4b07ba4913aa63 | |
parent | 69a1bef6f0e653e0a8d84e4e13e5879e2faeea39 (diff) | |
download | genenetwork2-04b32f95279602da4cfb00b4f356a04eee48af51.tar.gz |
I think this should complete consolidating all the collections code.
-rw-r--r-- | wqflask/base/trait.py | 8 | ||||
-rw-r--r-- | wqflask/utility/hmac.py | 56 | ||||
-rw-r--r-- | wqflask/wqflask/collect.py | 247 | ||||
-rw-r--r-- | wqflask/wqflask/correlation/corr_scatter_plot.py | 9 | ||||
-rw-r--r-- | wqflask/wqflask/gsearch.py | 4 | ||||
-rw-r--r-- | wqflask/wqflask/search_results.py | 2 | ||||
-rw-r--r-- | wqflask/wqflask/templates/base.html | 4 | ||||
-rw-r--r-- | wqflask/wqflask/templates/collections/list.html | 24 | ||||
-rw-r--r-- | wqflask/wqflask/templates/collections/view.html | 9 | ||||
-rw-r--r-- | wqflask/wqflask/templates/new_security/login_user.html | 1 | ||||
-rw-r--r-- | wqflask/wqflask/templates/show_trait_mapping_tools.html | 11 | ||||
-rw-r--r-- | wqflask/wqflask/templates/show_trait_statistics.html | 6 | ||||
-rw-r--r-- | wqflask/wqflask/user_login.py | 8 | ||||
-rw-r--r-- | wqflask/wqflask/user_session.py | 551 | ||||
-rw-r--r-- | wqflask/wqflask/views.py | 2 |
15 files changed, 375 insertions, 567 deletions
diff --git a/wqflask/base/trait.py b/wqflask/base/trait.py index c9dfe780..8e779a11 100644 --- a/wqflask/base/trait.py +++ b/wqflask/base/trait.py @@ -303,7 +303,7 @@ def jsonable_table_row(trait, dataset_name, index): additive = "N/A" else: additive = "%.3f" % round(float(trait.additive), 2) - return ['<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + hmac.hmac_creation('{}:{}'.format(str(trait.name), dataset.name)) + '">', + return ['<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + hmac.data_hmac('{}:{}'.format(str(trait.name), dataset.name)) + '">', index, '<a href="/show_trait?trait_id='+str(trait.name)+'&dataset='+dataset.name+'">'+str(trait.name)+'</a>', trait.symbol, @@ -319,7 +319,7 @@ def jsonable_table_row(trait, dataset_name, index): else: additive = "%.2f" % round(float(trait.additive), 2) if trait.pubmed_id: - return ['<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + hmac.hmac_creation('{}:{}'.format(str(trait.name), dataset.name)) + '">', + return ['<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + hmac.data_hmac('{}:{}'.format(str(trait.name), dataset.name)) + '">', index, '<a href="/show_trait?trait_id='+str(trait.name)+'&dataset='+dataset.name+'">'+str(trait.name)+'</a>', trait.description_display, @@ -329,7 +329,7 @@ def jsonable_table_row(trait, dataset_name, index): trait.LRS_location_repr, additive] else: - return ['<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + hmac.hmac_creation('{}:{}'.format(str(trait.name), dataset.name)) + '">', + return ['<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + hmac.data_hmac('{}:{}'.format(str(trait.name), dataset.name)) + '">', index, '<a href="/show_trait?trait_id='+str(trait.name)+'&dataset='+dataset.name+'">'+str(trait.name)+'</a>', trait.description_display, @@ -339,7 +339,7 @@ def jsonable_table_row(trait, dataset_name, index): trait.LRS_location_repr, additive] elif dataset.type == "Geno": - return ['<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + hmac.hmac_creation('{}:{}'.format(str(trait.name), dataset.name)) + '">', + return ['<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + hmac.data_hmac('{}:{}'.format(str(trait.name), dataset.name)) + '">', index, '<a href="/show_trait?trait_id='+str(trait.name)+'&dataset='+dataset.name+'">'+str(trait.name)+'</a>', trait.location_repr] diff --git a/wqflask/utility/hmac.py b/wqflask/utility/hmac.py index 47001e54..d8a0eace 100644 --- a/wqflask/utility/hmac.py +++ b/wqflask/utility/hmac.py @@ -1,18 +1,38 @@ -from __future__ import print_function, division, absolute_import
-
-import hmac
-
-from wqflask import app
-
-def hmac_creation(stringy):
- """Helper function to create the actual hmac"""
-
- secret = app.config['SECRET_HMAC_CODE']
-
- hmaced = hmac.new(secret, stringy, hashlib.sha1)
- hm = hmaced.hexdigest()
- # ZS: Leaving the below comment here to ask Pjotr about
- # "Conventional wisdom is that you don't lose much in terms of security if you throw away up to half of the output."
- # http://www.w3.org/QA/2009/07/hmac_truncation_in_xml_signatu.html
- hm = hm[:20]
- return hm
\ No newline at end of file +from __future__ import print_function, division, absolute_import + +import hmac +import hashlib + +from wqflask import app + +def hmac_creation(stringy): + """Helper function to create the actual hmac""" + + secret = app.config['SECRET_HMAC_CODE'] + + hmaced = hmac.new(secret, stringy, hashlib.sha1) + hm = hmaced.hexdigest() + # ZS: Leaving the below comment here to ask Pjotr about + # "Conventional wisdom is that you don't lose much in terms of security if you throw away up to half of the output." + # http://www.w3.org/QA/2009/07/hmac_truncation_in_xml_signatu.html + hm = hm[:20] + return hm + +def data_hmac(stringy): + """Takes arbitray data string and appends :hmac so we know data hasn't been tampered with""" + return stringy + ":" + hmac_creation(stringy) + +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 = hmac_creation(url) + if '?' in url: + combiner = "&" + else: + combiner = "?" + return url + combiner + "hm=" + hm + +app.jinja_env.globals.update(url_for_hmac=url_for_hmac, + data_hmac=data_hmac)
\ No newline at end of file diff --git a/wqflask/wqflask/collect.py b/wqflask/wqflask/collect.py index c273af59..e5c24873 100644 --- a/wqflask/wqflask/collect.py +++ b/wqflask/wqflask/collect.py @@ -8,7 +8,6 @@ import time import uuid import hashlib -import hmac import base64 import urlparse @@ -28,9 +27,8 @@ from pprint import pformat as pf from wqflask.database import db_session from wqflask import model -from wqflask import user_manager -from utility import Bunch, Struct +from utility import Bunch, Struct, hmac from utility.formatting import numify from base import trait @@ -40,119 +38,15 @@ import logging from utility.logger import getLogger logger = getLogger(__name__) - -class AnonCollection(object): - """User is not logged in""" - def __init__(self, collection_name): - anon_user = user_manager.AnonUser() - self.key = anon_user.key - self.name = collection_name - self.id = None - self.created_timestamp = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') - self.changed_timestamp = self.created_timestamp #ZS: will be updated when changes are made - - #ZS: Find id and set it if the collection doesn't already exist - if Redis.get(self.key) == "None" or Redis.get(self.key) == None: - Redis.set(self.key, "None") #ZS: For some reason I get the error "Operation against a key holding the wrong kind of value" if I don't do this - else: - collections_list = json.loads(Redis.get(self.key)) - collection_position = 0 #ZS: Position of collection in collection_list, if it exists - collection_exists = False - for i, collection in enumerate(collections_list): - if collection['name'] == self.name: - collection_position = i - collection_exists = True - self.id = collection['id'] - break - - if self.id == None: - self.id = str(uuid.uuid4()) - - def get_members(self): - traits = [] - collections_list = json.loads(Redis.get(self.key)) - for collection in collections_list: - if collection['id'] == self.id: - traits = collection['members'] - return traits - - @property - def num_members(self): - num_members = 0 - collections_list = json.loads(Redis.get(self.key)) - for collection in collections_list: - if collection['id'] == self.id: - num_members = collection['num_members'] - return num_members - - def add_traits(self, unprocessed_traits): - #assert collection_name == "Default", "Unexpected collection name for anonymous user" - self.traits = list(process_traits(unprocessed_traits)) - existing_collections = Redis.get(self.key) - logger.debug("existing_collections:", existing_collections) - if existing_collections != None and existing_collections != "None": - collections_list = json.loads(existing_collections) - collection_position = 0 #ZS: Position of collection in collection_list, if it exists - collection_exists = False - for i, collection in enumerate(collections_list): - if collection['id'] == self.id: - collection_position = i - collection_exists = True - break - if collection_exists: - collections_list[collection_position]['members'] += list(set(self.traits) - set(collections_list[collection_position]['members'])) - collections_list[collection_position]['num_members'] = len(collections_list[collection_position]['members']) - collections_list[collection_position]['changed_timestamp'] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') - else: - collection_dict = {"id" : self.id, - "name" : self.name, - "created_timestamp" : self.created_timestamp, - "changed_timestamp" : self.changed_timestamp, - "num_members" : len(self.traits), - "members" : self.traits} - collections_list.append(collection_dict) - else: - collections_list = [] - collection_dict = {"id" : self.id, - "name" : self.name, - "created_timestamp" : self.created_timestamp, - "changed_timestamp" : self.changed_timestamp, - "num_members" : len(self.traits), - "members" : self.traits} - collections_list.append(collection_dict) - - Redis.set(self.key, json.dumps(collections_list)) - - def remove_traits(self, params): - traits_to_remove = [(":").join(trait.split(":")[:2]) for trait in params.getlist('traits[]')] - existing_collections = Redis.get(self.key) - collection_position = 0 - collections_list = json.loads(existing_collections) - for i, collection in enumerate(collections_list): - if collection['id'] == self.id: - collection_position = i - collection_exists = True - break - collections_list[collection_position]['members'] = [trait for trait in collections_list[collection_position]['members'] if trait not in traits_to_remove] - collections_list[collection_position]['num_members'] = len(collections_list[collection_position]['members']) - collections_list[collection_position]['changed_timestamp'] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') - len_now = collections_list[collection_position]['num_members'] - Redis.set(self.key, json.dumps(collections_list)) - - # We need to return something so we'll return this...maybe in the future - # we can use it to check the results - return str(len_now) - def process_traits(unprocessed_traits): if isinstance(unprocessed_traits, basestring): unprocessed_traits = unprocessed_traits.split(",") traits = set() for trait in unprocessed_traits: - #print("trait is:", trait) data, _separator, hmac = trait.rpartition(':') data = data.strip() if g.user_session.logged_in: - assert hmac==user_manager.actual_hmac_creation(data), "Data tampering?" + assert hmac == hmac.data_hmac(data), "Data tampering?" traits.add(str(data)) return traits @@ -160,8 +54,6 @@ def process_traits(unprocessed_traits): def report_change(len_before, len_now): new_length = len_now - len_before if new_length: - logger.debug("We've added {} to your collection.".format( - numify(new_length, 'new trait', 'new traits'))) flash("We've added {} to your collection.".format( numify(new_length, 'new trait', 'new traits'))) else: @@ -180,18 +72,14 @@ def store_traits_list(): @app.route("/collections/add") def collections_add(): - if g.user_session.logged_in: + + collections = g.user_session.user_collections + if len(collections) < 1: + collection_name = "Default Collection" + uc_id = g.user_session.add_collection(collection_name, set()) collections = g.user_session.user_collections - if len(collections) < 1: - collection_name = "Default Collection" - uc_id = g.user_session.add_collection(collection_name, set()) - collections = g.user_session.user_collections - else: - anon_collections = user_manager.AnonUser().get_collections() - collections = [] - for collection in anon_collections: - collections.append({'id':collection['id'], 'name':collection['name']}) + #ZS: One of these might be unnecessary if 'traits' in request.args: traits=request.args['traits'] return render_template("collections/add.html", @@ -212,19 +100,13 @@ def collections_new(): if "sign_in" in params: return redirect(url_for('login')) if "create_new" in params: - logger.debug("in create_new") collection_name = params['new_collection'] if collection_name.strip() == "": collection_name = datetime.datetime.utcnow().strftime('Collection_%b_%d_%H:%M') return create_new(collection_name) elif "add_to_existing" in params: - logger.debug("in add to existing") if 'existing_collection' not in params: - default_collection_exists = False - if g.user_session.logged_in: - collections = g.user_session.user_collections - else: - collections = user_manager.AnonUser().get_collections() + collections = g.user_session.user_collections for collection in collections: if collection["name"] == "Default Collection": collection_id = collection["id"] @@ -235,22 +117,14 @@ def collections_new(): else: collection_id = params['existing_collection'].split(":")[0] collection_name = params['existing_collection'].split(":")[1] - if g.user_session.logged_in: - if "hash" in params: - unprocessed_traits = Redis.get(params['hash']) - else: - unprocessed_traits = params['traits'] - traits = list(process_traits(unprocessed_traits)) - g.user_session.add_traits_to_collection(collection_id, traits) - return redirect(url_for('view_collection', uc_id=collection_id)) + + if "hash" in params: + unprocessed_traits = Redis.get(params['hash']) else: - ac = AnonCollection(collection_name) - if "hash" in params: - unprocessed_traits = Redis.get(params['hash']) - else: - unprocessed_traits = params['traits'] - ac.add_traits(unprocessed_traits) - return redirect(url_for('view_collection', collection_id=ac.id)) + unprocessed_traits = params['traits'] + traits = list(process_traits(unprocessed_traits)) + g.user_session.add_traits_to_collection(collection_id, traits) + return redirect(url_for('view_collection', uc_id=collection_id)) else: CauseAnError @@ -265,50 +139,30 @@ def create_new(collection_name): traits = process_traits(unprocessed_traits) - if g.user_session.logged_in: - uc_id = g.user_session.add_collection(collection_name, traits) + uc_id = g.user_session.add_collection(collection_name, traits) - return redirect(url_for('view_collection', uc_id=uc_id)) - else: - ac = AnonCollection(collection_name) - ac.changed_timestamp = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') - ac.add_traits(unprocessed_traits) - return redirect(url_for('view_collection', collection_id=ac.id)) + return redirect(url_for('view_collection', uc_id=uc_id)) @app.route("/collections/list") def list_collections(): params = request.args - if g.user_session.logged_in: - user_collections = list(g.user_session.user_collections) - #logger.debug("user_collections are:", user_collections) - return render_template("collections/list.html", - params = params, - collections = user_collections, - ) - else: - anon_collections = user_manager.AnonUser().get_collections() - #logger.debug("anon_collections are:", anon_collections) - return render_template("collections/list.html", - params = params, - collections = anon_collections) - + user_collections = list(g.user_session.user_collections) + return render_template("collections/list.html", + params = params, + collections = user_collections, + ) @app.route("/collections/remove", methods=('POST',)) def remove_traits(): params = request.form - if "uc_id" in params: - uc_id = params['uc_id'] - traits_to_remove = params.getlist('traits[]') - traits_to_remove = process_traits(traits_to_remove) - logger.debug("\n\n after processing, traits_to_remove:", traits_to_remove) - - members_now = g.user_session.remove_traits_from_collection(uc_id, traits_to_remove) - else: - collection_name = params['collection_name'] - members_now = AnonCollection(collection_name).remove_traits(params) + uc_id = params['uc_id'] + traits_to_remove = params.getlist('traits[]') + traits_to_remove = process_traits(traits_to_remove) + logger.debug("\n\n after processing, traits_to_remove:", traits_to_remove) + members_now = g.user_session.remove_traits_from_collection(uc_id, traits_to_remove) # We need to return something so we'll return this...maybe in the future # we can use it to check the results @@ -319,21 +173,13 @@ def remove_traits(): def delete_collection(): params = request.form uc_id = "" - if g.user_session.logged_in: - uc_id = params['uc_id'] - if len(uc_id.split(":")) > 1: - for this_uc_id in uc_id.split(":"): - collection_name = g.user_session.delete_collection(this_uc_id) - else: - collection_name = g.user_session.delete_collection(uc_id) + + uc_id = params['uc_id'] + if len(uc_id.split(":")) > 1: + for this_uc_id in uc_id.split(":"): + collection_name = g.user_session.delete_collection(this_uc_id) else: - if "collection_name" in params: - collection_name = params['collection_name'] - user_manager.AnonUser().delete_collection(collection_name) - else: - uc_id = params['uc_id'] - for this_collection in uc_id.split(":"): - user_manager.AnonUser().delete_collection(this_collection) + collection_name = g.user_session.delete_collection(uc_id) if uc_id != "": if len(uc_id.split(":")) > 1: @@ -350,19 +196,9 @@ def delete_collection(): def view_collection(): params = request.args - if g.user_session.logged_in and "uc_id" in params: - uc_id = params['uc_id'] - uc = (collection for collection in g.user_session.user_collections if collection["id"] == uc_id).next() - traits = uc["members"] - else: - user_collections = json.loads(Redis.get(user_manager.AnonUser().key)) - this_collection = {} - for collection in user_collections: - if collection['id'] == params['collection_id']: - this_collection = collection - break - - traits = this_collection['members'] + uc_id = params['uc_id'] + uc = (collection for collection in g.user_session.user_collections if collection["id"] == uc_id).next() + traits = uc["members"] trait_obs = [] json_version = [] @@ -381,12 +217,9 @@ def view_collection(): json_version.append(trait.jsonable(trait_ob)) - if "uc_id" in params: - collection_info = dict(trait_obs=trait_obs, - uc = uc) - else: - collection_info = dict(trait_obs=trait_obs, - collection_name=this_collection['name']) + collection_info = dict(trait_obs=trait_obs, + uc = uc) + if "json" in params: return json.dumps(json_version) else: diff --git a/wqflask/wqflask/correlation/corr_scatter_plot.py b/wqflask/wqflask/correlation/corr_scatter_plot.py index f8eca386..dfb81c54 100644 --- a/wqflask/wqflask/correlation/corr_scatter_plot.py +++ b/wqflask/wqflask/correlation/corr_scatter_plot.py @@ -71,15 +71,8 @@ class CorrScatterPlot(object): sr_intercept_coords = get_intercept_coords(srslope, srintercept, sr_range, sr_range) - #vals_3 = [] - #for sample in self.trait_3.data: - # vals_3.append(self.trait_3.data[sample].value) - self.collections_exist = "False" - if g.user_session.logged_in: - if g.user_session.num_collections > 0: - self.collections_exist = "True" - elif g.cookie_session.display_num_collections() != "": + if g.user_session.num_collections > 0: self.collections_exist = "True" self.js_data = dict( diff --git a/wqflask/wqflask/gsearch.py b/wqflask/wqflask/gsearch.py index ed3b74b8..12813e9a 100644 --- a/wqflask/wqflask/gsearch.py +++ b/wqflask/wqflask/gsearch.py @@ -70,7 +70,7 @@ class GSearch(object): this_trait['name'] = line[5] this_trait['dataset'] = line[3] this_trait['dataset_fullname'] = line[4] - this_trait['hmac'] = hmac.hmac_creation('{}:{}'.format(line[5], line[3])) + this_trait['hmac'] = hmac.data_hmac('{}:{}'.format(line[5], line[3])) this_trait['species'] = line[0] this_trait['group'] = line[1] this_trait['tissue'] = line[2] @@ -174,7 +174,7 @@ class GSearch(object): this_trait['display_name'] = this_trait['name'] this_trait['dataset'] = line[2] this_trait['dataset_fullname'] = line[3] - this_trait['hmac'] = hmac.hmac_creation('{}:{}'.format(line[4], line[2])) + this_trait['hmac'] = hmac.data_hmac('{}:{}'.format(line[4], line[2])) this_trait['species'] = line[0] this_trait['group'] = line[1] if line[9] != None and line[6] != None: diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py index 902317a4..34c647f4 100644 --- a/wqflask/wqflask/search_results.py +++ b/wqflask/wqflask/search_results.py @@ -120,7 +120,7 @@ views.py). else: trait_dict['display_name'] = this_trait.name trait_dict['dataset'] = this_trait.dataset.name - trait_dict['hmac'] = hmac.hmac_creation('{}:{}'.format(this_trait.name, this_trait.dataset.name)) + trait_dict['hmac'] = hmac.data_hmac('{}:{}'.format(this_trait.name, this_trait.dataset.name)) if this_trait.dataset.type == "ProbeSet": trait_dict['symbol'] = this_trait.symbol trait_dict['description'] = this_trait.description_display.decode('utf-8', 'replace') diff --git a/wqflask/wqflask/templates/base.html b/wqflask/wqflask/templates/base.html index 04cf718a..2c95bf5e 100644 --- a/wqflask/wqflask/templates/base.html +++ b/wqflask/wqflask/templates/base.html @@ -73,11 +73,7 @@ </li> <li class=""> <a href="/collections/list">Collections - {% if g.user_session.logged_in %} <span class="badge badge-info">{{ g.user_session.num_collections }}</span> - {% else %} - <span class="badge badge-info">{{ g.cookie_session.display_num_collections() }}</span> - {% endif %} </a> </li> <li class=""> diff --git a/wqflask/wqflask/templates/collections/list.html b/wqflask/wqflask/templates/collections/list.html index 19aec8ae..eb3fc061 100644 --- a/wqflask/wqflask/templates/collections/list.html +++ b/wqflask/wqflask/templates/collections/list.html @@ -15,17 +15,6 @@ <h1>Your Collections</h1> {% endif %} - <!-- <hr style="height: 1px; background-color: #A9A9A9;">--> - - <!-- - <div class="page-header"> - {% if g.user_session.logged_in %} - <h1>Collections owned by {{ g.user_session.user_name }}</h1> - {% else %} - <h1>Your Collections</h1> - {% endif %} - </div> - --> <div> <form id="collections_form" action="/delete" method="post"> <input type="hidden" name="uc_id" id="uc_id" value="" /> @@ -55,24 +44,11 @@ <tbody> {% for uc in collections %} <tr class="collection_line"> - {% if g.user_session.logged_in %} <td align="center" style="padding: 0px;"><INPUT TYPE="checkbox" NAME="collection" class="checkbox trait_checkbox" VALUE="{{ uc.id }}"></td> - {% else %} - <td align="center" style="padding: 0px;"><INPUT TYPE="checkbox" NAME="collection" class="checkbox trait_checkbox" VALUE="{{ uc.name }}"></td> - {% endif %} <td align="right">{{ loop.index }} - {% if g.user_session.logged_in %} <td><a class="collection_name" href="{{ url_for('view_collection', uc_id=uc.id) }}">{{ uc.name }}</a></td> - {% else %} - <td><a class="collection_name" href="{{ url_for('view_collection', collection_id=uc.id) }}">{{ uc.name }}</a></td> - {% endif %} - {% if g.user_session.logged_in %} <td>{{ uc.created_timestamp }}</td> <td>{{ uc.changed_timestamp }}</td> - {% else %} - <td>{{ timeago(uc.created_timestamp.isoformat() + "Z") }}</td> - <td>{{ timeago(uc.changed_timestamp.isoformat() + "Z") }}</td> - {% endif %} <td align="right">{{ uc.num_members }}</td> </tr> {% endfor %} diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index 640eb561..fd0025d9 100644 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -9,20 +9,15 @@ <!-- Start of body --> <div class="container" style="min-width: 1250px;"> - {% if uc %} - <h1>{{ uc.name }}</h1> - {% else %} - <h1>{{ collection_name }}</h1> - {% endif %} + <h1><input type="text" id="change_collection_name" style="display: none; width: 500px;" class="form-control" placeholder="{{ uc.name }}"></h1> + <h1>{{ uc.name }} <!--<button class="btn btn-default" style="margin-bottom: 5px;" id="change_collection_name">Change Collection Name</button>--></h1> <h3>This collection has {{ '{}'.format(numify(trait_obs|count, "record", "records")) }}</h3> <!--<hr style="height: 1px; background-color: #A9A9A9;">--> <div> <form id="collection_form" action="/delete" method="post"> - {% if uc %} <input type="hidden" name="uc_id" id="uc_id" value="{{ uc.id }}" /> - {% endif %} <input type="hidden" name="collection_name" id="collection_name" value="{{ collection_name }}" /> <input type="hidden" name="tool_used" value="" /> <input type="hidden" name="form_url" value="" /> diff --git a/wqflask/wqflask/templates/new_security/login_user.html b/wqflask/wqflask/templates/new_security/login_user.html index 9df0e16a..80fed82a 100644 --- a/wqflask/wqflask/templates/new_security/login_user.html +++ b/wqflask/wqflask/templates/new_security/login_user.html @@ -10,6 +10,7 @@ {% if redis_is_available: %} <form class="form-horizontal" action="/n/login" method="POST" name="login_user_form" id="loginUserForm"> + <input name="anon_id" type="hidden" value="{{ g.user_session.user_id }}"> <fieldset> <div class="form-group"> <label style="text-align:left;" class="col-xs-1 control-label" for="email_address">Email Address</label> diff --git a/wqflask/wqflask/templates/show_trait_mapping_tools.html b/wqflask/wqflask/templates/show_trait_mapping_tools.html index 841d6ad5..a806a8b3 100644 --- a/wqflask/wqflask/templates/show_trait_mapping_tools.html +++ b/wqflask/wqflask/templates/show_trait_mapping_tools.html @@ -81,7 +81,6 @@ <font size="2">Select covariate(s) from a collection</font> </div> <div style="margin-left:20px;" class="col-xs-7"> - {% if g.user_session.logged_in %} {% if g.user_session.num_collections < 1 %} No collections available. Please add traits to a collection to use them as covariates. {% else %} @@ -91,16 +90,6 @@ </div> <textarea rows="3" cols="50" readonly placeholder="No covariates selected" style="overflow-y: scroll; resize: none; width: 200px;" class="selected_covariates"></textarea> {% endif %} - {% elif g.cookie_session.display_num_collections() == "" %} - No collections available. Please add traits to a collection to use them as covariates. - {% else %} - <div style="margin-bottom: 10px;" class="btn-group" role="group"> - <button type="button" id="select_covariates" class="btn btn-default">Select</button> - <button type="button" id="remove_covariates" class="btn btn-default">Remove</button> - </div> - <br> - <textarea rows="3" cols="30" readonly placeholder="No covariates selected" style="overflow-y: scroll; resize: none;" class="selected_covariates"></textarea> - {% endif %} </div> </div> <div class="mapping_method_fields form-group"> diff --git a/wqflask/wqflask/templates/show_trait_statistics.html b/wqflask/wqflask/templates/show_trait_statistics.html index 4653b398..974081d3 100644 --- a/wqflask/wqflask/templates/show_trait_statistics.html +++ b/wqflask/wqflask/templates/show_trait_statistics.html @@ -21,11 +21,9 @@ <li> <a href="#violin_plot_tab" class="violin_plot_tab" data-toggle="tab">Violin Plot</a> </li> - {% if g.user_session.logged_in %} <li> <a href="#scatterplot_matrix" data-toggle="tab">Scatterplot Matrix</a> </li> - {% endif %} </ul> <div class="tab-content"> @@ -78,13 +76,11 @@ <i class="icon-signal"></i> Sort By Value </button> </div> - {% if g.user_session.logged_in %} <div class="btn-group"> <button type="button" class="btn btn-default" id="color_by_trait"> <i class="icon-tint"></i> Color by Trait </button> </div> - {% endif %} <div id="bar_chart_container"> <div id="bar_chart"></div> </div> @@ -126,7 +122,6 @@ <div id="violin_plot"></div> </div> </div> - {% if g.user_session.logged_in %} <div class="tab-pane" id="scatterplot_matrix"> <div style="margin-bottom:40px;" class="btn-group"> @@ -141,7 +136,6 @@ <div id="comparison_scatterplot" class="qtlcharts"></div> </div> </div> - {% endif %} </div> </div> <!--</div>--> diff --git a/wqflask/wqflask/user_login.py b/wqflask/wqflask/user_login.py index 05885e2c..da3cc504 100644 --- a/wqflask/wqflask/user_login.py +++ b/wqflask/wqflask/user_login.py @@ -21,9 +21,9 @@ from flask import (Flask, g, render_template, url_for, request, make_response, from wqflask import app from wqflask import pbkdf2 -from wqflask.hmac_func import hmac_creation from wqflask.user_session import UserSession +from utility import hmac from utility.redis_tools import is_redis_available, get_user_id, get_user_by_unique_column, set_user_attribute, save_user, save_verification_code, check_verification_code, get_user_collections, save_collections from utility.logger import getLogger @@ -83,7 +83,7 @@ def encrypt_password(unencrypted_password, pwfields): def get_signed_session_id(user): session_id = str(uuid.uuid4()) - session_id_signature = hmac_creation(session_id) + session_id_signature = hmac.hmac_creation(session_id) session_id_signed = session_id + ":" + session_id_signature #ZS: Need to check if this is ever actually used or exists @@ -197,12 +197,14 @@ def login(): if password_match: # If password correct if user_details['confirmed']: # If account confirmed import_col = "false" + anon_id = "" if 'import_collections' in params: import_col = "true" + anon_id = params['anon_id'] session_id_signed = get_signed_session_id(user_details) flash("Thank you for logging in {}.".format(user_details['full_name']), "alert-success") - response = make_response(redirect(url_for('index_page', import_collections = import_col))) + response = make_response(redirect(url_for('index_page', import_collections = import_col, anon_id = anon_id))) response.set_cookie(UserSession.user_cookie_name, session_id_signed, max_age=None) return response else: diff --git a/wqflask/wqflask/user_session.py b/wqflask/wqflask/user_session.py index 1f3e6558..e19ec9df 100644 --- a/wqflask/wqflask/user_session.py +++ b/wqflask/wqflask/user_session.py @@ -1,272 +1,281 @@ -from __future__ import print_function, division, absolute_import
-
-import datetime
-import time
-import uuid
-
-import simplejson as json
-
-import redis # used for collections
-Redis = redis.StrictRedis()
-
-from flask import (Flask, g, render_template, url_for, request, make_response,
- redirect, flash, abort)
-
-from wqflask import app
-from wqflask.hmac_func import hmac_creation
-
-#from utility.elasticsearch_tools import get_elasticsearch_connection
-from utility.redis_tools import get_user_id, get_user_by_unique_column, get_user_collections, save_collections
-
-from utility.logger import getLogger
-logger = getLogger(__name__)
-
-THREE_DAYS = 60 * 60 * 24 * 3
-THIRTY_DAYS = 60 * 60 * 24 * 30
-
-def verify_cookie(cookie):
- the_uuid, separator, the_signature = cookie.partition(':')
- assert len(the_uuid) == 36, "Is session_id a uuid?"
- assert separator == ":", "Expected a : here"
- assert the_signature == hmac_creation(the_uuid), "Uh-oh, someone tampering with the cookie?"
- return the_uuid
-
-def create_signed_cookie():
- the_uuid = str(uuid.uuid4())
- signature = hmac_creation(the_uuid)
- uuid_signed = the_uuid + ":" + signature
- logger.debug("uuid_signed:", uuid_signed)
- return the_uuid, uuid_signed
-
-class UserSession(object):
- """Logged in user handling"""
-
- user_cookie_name = 'session_id_v1'
- anon_cookie_name = 'anon_user_v1'
-
- def __init__(self):
- user_cookie = request.cookies.get(self.user_cookie_name)
- if not user_cookie:
- self.logged_in = False
- anon_cookie = request.cookies.get(self.anon_cookie_name)
- self.cookie_name = self.anon_cookie_name
- if anon_cookie:
- self.cookie = anon_cookie
- session_id = verify_cookie(self.cookie)
- else:
- session_id, self.cookie = create_signed_cookie()
- else:
- self.cookie_name = self.user_cookie_name
- self.cookie = user_cookie
- session_id = verify_cookie(self.cookie)
-
- self.redis_key = self.cookie_name + ":" + session_id
- self.session_id = session_id
- self.record = Redis.hgetall(self.redis_key)
-
- #ZS: If user correctled logged in but their session expired
- #ZS: Need to test this by setting the time-out to be really short or something
- if not self.record:
- if user_cookie:
- self.logged_in = False
-
- ########### Grrr...this won't work because of the way flask handles cookies
- # Delete the cookie
- response = make_response(redirect(url_for('login')))
- #response.set_cookie(self.cookie_name, '', expires=0)
- flash("Due to inactivity your session has expired. If you'd like please login again.")
- return response
- #return
- else:
- self.record = dict(login_time = time.time(),
- user_type = "anon",
- user_id = str(uuid.uuid4()))
-
- Redis.hmset(self.redis_key, self.record)
- Redis.expire(self.redis_key, THIRTY_DAYS)
- else:
- if user_cookie:
- self.logged_in = True
-
- if user_cookie:
- session_time = THREE_DAYS
- else:
- session_time = THIRTY_DAYS
-
- if Redis.ttl(self.redis_key) < session_time:
- # (Almost) everytime the user does something we extend the session_id in Redis...
- logger.debug("Extending ttl...")
- Redis.expire(self.redis_key, session_time)
-
- @property
- def user_id(self):
- """Shortcut to the user_id"""
- if 'user_id' in self.record:
- return self.record['user_id']
- else:
- return ''
-
- @property
- def redis_user_id(self):
- """User id from Redis (need to check if this is the same as the id stored in self.records)"""
-
- #ZS: This part is a bit weird. Some accounts used to not have saved user ids, and in the process of testing I think I created some duplicate accounts for myself.
- #ZS: Accounts should automatically generate user_ids if they don't already have one now, so this might not be necessary for anything other than my account's collections
-
- if 'user_email_address' in self.record:
- user_email = self.record['user_email_address']
-
- #ZS: Get user's collections if they exist
- user_id = None
- user_id = get_user_id("email_address", user_email)
- elif 'user_id' in self.record:
- user_id = self.record['user_id']
- elif 'github_id' in self.record:
- user_github_id = self.record['github_id']
- user_id = None
- user_id = get_user_id("github_id", user_github_id)
- else: #ZS: Anonymous user
- return None
-
- return user_id
-
- @property
- def user_name(self):
- """Shortcut to the user_name"""
- if 'user_name' in self.record:
- return self.record['user_name']
- else:
- return ''
-
- @property
- def user_collections(self):
- """List of user's collections"""
-
- #ZS: Get user's collections if they exist
- collections = get_user_collections(self.redis_user_id)
- return collections
-
- @property
- def num_collections(self):
- """Number of user's collections"""
-
- return len(self.user_collections)
-
- def add_collection(self, collection_name, traits):
- """Add collection into ElasticSearch"""
-
- collection_dict = {'id': unicode(uuid.uuid4()),
- 'name': collection_name,
- 'created_timestamp': datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p'),
- 'changed_timestamp': datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p'),
- 'num_members': len(traits),
- 'members': list(traits) }
-
- current_collections = self.user_collections
- current_collections.append(collection_dict)
- self.update_collections(current_collections)
-
- return collection_dict['id']
-
- def change_collection_name(self, collection_id, new_name):
- for collection in self.user_collections:
- if collection['id'] == collection_id:
- collection['name'] = new_name
- break
-
- return new_name
-
- def delete_collection(self, collection_id):
- """Remove collection with given ID"""
-
- updated_collections = []
- for collection in self.user_collections:
- if collection['id'] == collection_id:
- continue
- else:
- updated_collections.append(collection)
-
- self.update_collections(updated_collections)
-
- return collection['name']
-
- def add_traits_to_collection(self, collection_id, traits_to_add):
- """Add specified traits to a collection"""
-
- this_collection = self.get_collection_by_id(collection_id)
-
- updated_collection = this_collection
- updated_traits = this_collection['members'] + traits_to_add
-
- updated_collection['members'] = updated_traits
- updated_collection['num_members'] = len(updated_traits)
- updated_collection['changed_timestamp'] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')
-
- updated_collections = []
- for collection in self.user_collections:
- if collection['id'] == collection_id:
- updated_collections.append(updated_collection)
- else:
- updated_collections.append(collection)
-
- self.update_collections(updated_collections)
-
- def remove_traits_from_collection(self, collection_id, traits_to_remove):
- """Remove specified traits from a collection"""
-
- this_collection = self.get_collection_by_id(collection_id)
-
- updated_collection = this_collection
- updated_traits = []
- for trait in this_collection['members']:
- if trait in traits_to_remove:
- continue
- else:
- updated_traits.append(trait)
-
- updated_collection['members'] = updated_traits
- updated_collection['num_members'] = len(updated_traits)
- updated_collection['changed_timestamp'] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')
-
- updated_collections = []
- for collection in self.user_collections:
- if collection['id'] == collection_id:
- updated_collections.append(updated_collection)
- else:
- updated_collections.append(collection)
-
- self.update_collections(updated_collections)
-
- return updated_traits
-
- def get_collection_by_id(self, collection_id):
- for collection in self.user_collections:
- if collection['id'] == collection_id:
- return collection
-
- def get_collection_by_name(self, collection_name):
- for collection in self.user_collections:
- if collection['name'] == collection_name:
- return collection
-
- return None
-
- def update_collections(self, updated_collections):
- collection_body = json.dumps(updated_collections)
-
- save_collections(self.redis_user_id, collection_body)
-
- def delete_session(self):
- # And more importantly delete the redis record
- Redis.delete(self.redis_key)
- self.logged_in = False
-
-@app.before_request
-def before_request():
- g.user_session = UserSession()
-
-@app.after_request
-def set_cookie(response):
- if not request.cookies.get(g.user_session.cookie_name):
- response.set_cookie(g.user_session.cookie_name, g.user_session.cookie)
+from __future__ import print_function, division, absolute_import + +import datetime +import time +import uuid + +import simplejson as json + +import redis # used for collections +Redis = redis.StrictRedis() + +from flask import (Flask, g, render_template, url_for, request, make_response, + redirect, flash, abort) + +from wqflask import app +from utility import hmac + +#from utility.elasticsearch_tools import get_elasticsearch_connection +from utility.redis_tools import get_user_id, get_user_by_unique_column, get_user_collections, save_collections + +from utility.logger import getLogger +logger = getLogger(__name__) + +THREE_DAYS = 60 * 60 * 24 * 3 +THIRTY_DAYS = 60 * 60 * 24 * 30 + +def verify_cookie(cookie): + the_uuid, separator, the_signature = cookie.partition(':') + assert len(the_uuid) == 36, "Is session_id a uuid?" + assert separator == ":", "Expected a : here" + assert the_signature == hmac.hmac_creation(the_uuid), "Uh-oh, someone tampering with the cookie?" + return the_uuid + +def create_signed_cookie(): + the_uuid = str(uuid.uuid4()) + signature = hmac.hmac_creation(the_uuid) + uuid_signed = the_uuid + ":" + signature + logger.debug("uuid_signed:", uuid_signed) + return the_uuid, uuid_signed + +class UserSession(object): + """Logged in user handling""" + + user_cookie_name = 'session_id_v1' + anon_cookie_name = 'anon_user_v1' + + def __init__(self): + user_cookie = request.cookies.get(self.user_cookie_name) + if not user_cookie: + self.logged_in = False + anon_cookie = request.cookies.get(self.anon_cookie_name) + self.cookie_name = self.anon_cookie_name + if anon_cookie: + self.cookie = anon_cookie + session_id = verify_cookie(self.cookie) + else: + session_id, self.cookie = create_signed_cookie() + else: + self.cookie_name = self.user_cookie_name + self.cookie = user_cookie + session_id = verify_cookie(self.cookie) + + self.redis_key = self.cookie_name + ":" + session_id + self.session_id = session_id + self.record = Redis.hgetall(self.redis_key) + + #ZS: If user correctled logged in but their session expired + #ZS: Need to test this by setting the time-out to be really short or something + if not self.record: + if user_cookie: + self.logged_in = False + + ########### Grrr...this won't work because of the way flask handles cookies + # Delete the cookie + response = make_response(redirect(url_for('login'))) + #response.set_cookie(self.cookie_name, '', expires=0) + flash("Due to inactivity your session has expired. If you'd like please login again.") + return response + #return + else: + self.record = dict(login_time = time.time(), + user_type = "anon", + user_id = str(uuid.uuid4())) + + Redis.hmset(self.redis_key, self.record) + Redis.expire(self.redis_key, THIRTY_DAYS) + else: + if user_cookie: + self.logged_in = True + + if user_cookie: + session_time = THREE_DAYS + else: + session_time = THIRTY_DAYS + + if Redis.ttl(self.redis_key) < session_time: + # (Almost) everytime the user does something we extend the session_id in Redis... + logger.debug("Extending ttl...") + Redis.expire(self.redis_key, session_time) + + @property + def user_id(self): + """Shortcut to the user_id""" + if 'user_id' in self.record: + return self.record['user_id'] + else: + return '' + + @property + def redis_user_id(self): + """User id from Redis (need to check if this is the same as the id stored in self.records)""" + + #ZS: This part is a bit weird. Some accounts used to not have saved user ids, and in the process of testing I think I created some duplicate accounts for myself. + #ZS: Accounts should automatically generate user_ids if they don't already have one now, so this might not be necessary for anything other than my account's collections + + if 'user_email_address' in self.record: + user_email = self.record['user_email_address'] + + #ZS: Get user's collections if they exist + user_id = None + user_id = get_user_id("email_address", user_email) + elif 'user_id' in self.record: + user_id = self.record['user_id'] + elif 'github_id' in self.record: + user_github_id = self.record['github_id'] + user_id = None + user_id = get_user_id("github_id", user_github_id) + else: #ZS: Anonymous user + return None + + return user_id + + @property + def user_name(self): + """Shortcut to the user_name""" + if 'user_name' in self.record: + return self.record['user_name'] + else: + return '' + + @property + def user_collections(self): + """List of user's collections""" + + #ZS: Get user's collections if they exist + collections = get_user_collections(self.redis_user_id) + return collections + + @property + def num_collections(self): + """Number of user's collections""" + + return len(self.user_collections) + + def add_collection(self, collection_name, traits): + """Add collection into Redis""" + + collection_dict = {'id': unicode(uuid.uuid4()), + 'name': collection_name, + 'created_timestamp': datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p'), + 'changed_timestamp': datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p'), + 'num_members': len(traits), + 'members': list(traits) } + + current_collections = self.user_collections + current_collections.append(collection_dict) + self.update_collections(current_collections) + + return collection_dict['id'] + + def change_collection_name(self, collection_id, new_name): + for collection in self.user_collections: + if collection['id'] == collection_id: + collection['name'] = new_name + break + + return new_name + + def delete_collection(self, collection_id): + """Remove collection with given ID""" + + updated_collections = [] + for collection in self.user_collections: + if collection['id'] == collection_id: + continue + else: + updated_collections.append(collection) + + self.update_collections(updated_collections) + + return collection['name'] + + def add_traits_to_collection(self, collection_id, traits_to_add): + """Add specified traits to a collection""" + + this_collection = self.get_collection_by_id(collection_id) + + updated_collection = this_collection + updated_traits = this_collection['members'] + traits_to_add + + updated_collection['members'] = updated_traits + updated_collection['num_members'] = len(updated_traits) + updated_collection['changed_timestamp'] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') + + updated_collections = [] + for collection in self.user_collections: + if collection['id'] == collection_id: + updated_collections.append(updated_collection) + else: + updated_collections.append(collection) + + self.update_collections(updated_collections) + + def remove_traits_from_collection(self, collection_id, traits_to_remove): + """Remove specified traits from a collection""" + + this_collection = self.get_collection_by_id(collection_id) + + updated_collection = this_collection + updated_traits = [] + for trait in this_collection['members']: + if trait in traits_to_remove: + continue + else: + updated_traits.append(trait) + + updated_collection['members'] = updated_traits + updated_collection['num_members'] = len(updated_traits) + updated_collection['changed_timestamp'] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') + + updated_collections = [] + for collection in self.user_collections: + if collection['id'] == collection_id: + updated_collections.append(updated_collection) + else: + updated_collections.append(collection) + + self.update_collections(updated_collections) + + return updated_traits + + def get_collection_by_id(self, collection_id): + for collection in self.user_collections: + if collection['id'] == collection_id: + return collection + + def get_collection_by_name(self, collection_name): + for collection in self.user_collections: + if collection['name'] == collection_name: + return collection + + return None + + def update_collections(self, updated_collections): + collection_body = json.dumps(updated_collections) + + save_collections(self.redis_user_id, collection_body) + + def import_traits_to_user(self, anon_id): + collections = get_user_collections(anon_id) + for collection in collections: + collection_exists = self.get_collection_by_name(collection['name']) + if collection_exists: + continue + else: + self.add_collection(collection['name'], collection['members']) + + def delete_session(self): + # And more importantly delete the redis record + Redis.delete(self.redis_key) + self.logged_in = False + +@app.before_request +def before_request(): + g.user_session = UserSession() + +@app.after_request +def set_cookie(response): + if not request.cookies.get(g.user_session.cookie_name): + response.set_cookie(g.user_session.cookie_name, g.user_session.cookie) return response
\ No newline at end of file diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py index 436ebc91..8a80c26c 100644 --- a/wqflask/wqflask/views.py +++ b/wqflask/wqflask/views.py @@ -126,7 +126,7 @@ def index_page(): if 'import_collections' in params: import_collections = params['import_collections'] if import_collections == "true": - g.cookie_session.import_traits_to_user() + g.user_session.import_traits_to_user(params['anon_id']) #if USE_GN_SERVER: # # The menu is generated using GN_SERVER # return render_template("index_page.html", gn_server_url = GN_SERVER_URL, version=GN_VERSION) |