aboutsummaryrefslogtreecommitdiff
path: root/wqflask
diff options
context:
space:
mode:
authorzsloan2020-03-03 15:18:16 -0600
committerzsloan2020-03-03 15:18:16 -0600
commiteda3b9d1ed752ef644914ebcff32f861ebb1d096 (patch)
tree2dc73917cf003065d0ce9b633ebaaf9af2ae340d /wqflask
parent8839637fbcb1b083e19367801ca7646962944d30 (diff)
downloadgenenetwork2-eda3b9d1ed752ef644914ebcff32f861ebb1d096.tar.gz
Also needed to remove the old login code
Diffstat (limited to 'wqflask')
-rw-r--r--wqflask/wqflask/user_manager.py1058
1 files changed, 0 insertions, 1058 deletions
diff --git a/wqflask/wqflask/user_manager.py b/wqflask/wqflask/user_manager.py
deleted file mode 100644
index 1b27d7cb..00000000
--- a/wqflask/wqflask/user_manager.py
+++ /dev/null
@@ -1,1058 +0,0 @@
-from __future__ import print_function, division, absolute_import
-
-import os
-import hashlib
-import datetime
-import time
-import logging
-import uuid
-import hashlib
-import hmac
-import base64
-import urlparse
-
-import simplejson as json
-
-#from redis import StrictRedis
-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 pprint import pformat as pf
-
-from wqflask import pbkdf2 # password hashing
-from wqflask.database import db_session
-from wqflask import model
-
-from utility import Bunch, Struct, after
-
-import logging
-from utility.logger import getLogger
-logger = getLogger(__name__)
-
-from base.data_set import create_datasets_list
-
-import requests
-
-from utility.redis_tools import 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 smtplib import SMTP
-from utility.tools import SMTP_CONNECT, SMTP_USERNAME, SMTP_PASSWORD, LOG_SQL_ALCHEMY
-
-THREE_DAYS = 60 * 60 * 24 * 3
-#THREE_DAYS = 45
-
-def timestamp():
- return datetime.datetime.utcnow().isoformat()
-
-class AnonUser(object):
- """Anonymous user handling"""
- cookie_name = 'anon_user_v1'
-
- def __init__(self):
- self.cookie = request.cookies.get(self.cookie_name)
- if self.cookie:
- logger.debug("ANON COOKIE ALREADY EXISTS")
- self.anon_id = verify_cookie(self.cookie)
- else:
- logger.debug("CREATING NEW ANON COOKIE")
- self.anon_id, self.cookie = create_signed_cookie()
-
- self.key = "anon_collection:v1:{}".format(self.anon_id)
-
- def add_collection(self, new_collection):
- collection_dict = dict(name = new_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 = new_collection.num_members,
- members = new_collection.get_members())
-
- Redis.set(self.key, json.dumps(collection_dict))
- Redis.expire(self.key, 60 * 60 * 24 * 365)
-
- def delete_collection(self, collection_name):
- existing_collections = self.get_collections()
- updated_collections = []
- for i, collection in enumerate(existing_collections):
- if collection['name'] == collection_name:
- continue
- else:
- this_collection = {}
- this_collection['id'] = collection['id']
- this_collection['name'] = collection['name']
- this_collection['created_timestamp'] = collection['created_timestamp'].strftime('%b %d %Y %I:%M%p')
- this_collection['changed_timestamp'] = collection['changed_timestamp'].strftime('%b %d %Y %I:%M%p')
- this_collection['num_members'] = collection['num_members']
- this_collection['members'] = collection['members']
- updated_collections.append(this_collection)
-
- Redis.set(self.key, json.dumps(updated_collections))
-
- def get_collections(self):
- json_collections = Redis.get(self.key)
- if json_collections == None or json_collections == "None":
- return []
- else:
- collections = json.loads(json_collections)
- for collection in collections:
- collection['created_timestamp'] = datetime.datetime.strptime(collection['created_timestamp'], '%b %d %Y %I:%M%p')
- collection['changed_timestamp'] = datetime.datetime.strptime(collection['changed_timestamp'], '%b %d %Y %I:%M%p')
-
- collections = sorted(collections, key = lambda i: i['changed_timestamp'], reverse = True)
- return collections
-
- def import_traits_to_user(self):
- result = Redis.get(self.key)
- collections_list = json.loads(result if result else "[]")
- for collection in collections_list:
- collection_exists = g.user_session.get_collection_by_name(collection['name'])
- if collection_exists:
- continue
- else:
- g.user_session.add_collection(collection['name'], collection['members'])
-
- def display_num_collections(self):
- """
- Returns the number of collections or a blank string if there are zero.
-
- Because this is so unimportant...we wrap the whole thing in a try/expect...last thing we
- want is a webpage not to be displayed because of an error here
-
- Importand TODO: use redis to cache this, don't want to be constantly computing it
- """
- try:
- num = len(self.get_collections())
- if num > 0:
- return num
- else:
- return ""
- except Exception as why:
- print("Couldn't display_num_collections:", why)
- return ""
-
-
-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 == actual_hmac_creation(the_uuid), "Uh-oh, someone tampering with the cookie?"
- return the_uuid
-
-def create_signed_cookie():
- the_uuid = str(uuid.uuid4())
- signature = actual_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"""
-
- cookie_name = 'session_id_v1'
-
- def __init__(self):
- cookie = request.cookies.get(self.cookie_name)
- if not cookie:
- logger.debug("NO USER COOKIE")
- self.logged_in = False
- return
- else:
- session_id = verify_cookie(cookie)
-
- self.redis_key = self.cookie_name + ":" + session_id
- logger.debug("self.redis_key is:", self.redis_key)
- self.session_id = session_id
- self.record = Redis.hgetall(self.redis_key)
-
- if not self.record:
- # This will occur, for example, when the browser has been left open over a long
- # weekend and the site hasn't been visited by the user
- 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
-
- if Redis.ttl(self.redis_key) < THREE_DAYS:
- # (Almost) everytime the user does something we extend the session_id in Redis...
- logger.debug("Extending ttl...")
- Redis.expire(self.redis_key, THREE_DAYS)
-
- logger.debug("record is:", self.record)
- self.logged_in = True
-
- @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 ElasticSearch (need to check if this is the same as the id stored in self.records)"""
-
- 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)
- 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)
-
-###
-# ZS: This is currently not used, but I'm leaving it here commented out because the old "set superuser" code (at the bottom of this file) used it
-###
-# @property
-# def user_ob(self):
-# """Actual sqlalchemy record"""
-# # Only look it up once if needed, then store it
-# # raise "OBSOLETE: use ElasticSearch instead"
-# try:
-# if LOG_SQL_ALCHEMY:
-# logging.getLogger('sqlalchemy.pool').setLevel(logging.DEBUG)
-#
-# # Already did this before
-# return self.db_object
-# except AttributeError:
-# # Doesn't exist so we'll create it
-# self.db_object = model.User.query.get(self.user_id)
-# return self.db_object
-
- 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 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.cookie_name)
- logger.debug("At end of delete_session")
-
-@app.before_request
-def before_request():
- g.user_session = UserSession()
- g.cookie_session = AnonUser()
-
-@app.after_request
-def set_cookie(response):
- if not request.cookies.get(g.cookie_session.cookie_name):
- response.set_cookie(g.cookie_session.cookie_name, g.cookie_session.cookie)
- return response
-
-class UsersManager(object):
- def __init__(self):
- self.users = model.User.query.all()
- logger.debug("Users are:", self.users)
-
-class UserManager(object):
- def __init__(self, kw):
- self.user_id = kw['user_id']
- logger.debug("In UserManager locals are:", pf(locals()))
- #self.user = model.User.get(user_id)
- #logger.debug("user is:", user)
- self.user = model.User.query.get(self.user_id)
- logger.debug("user is:", self.user)
- datasets = create_datasets_list()
- for dataset in datasets:
- if not dataset.check_confidentiality():
- continue
- logger.debug("\n Name:", dataset.name)
- logger.debug(" Type:", dataset.type)
- logger.debug(" ID:", dataset.id)
- logger.debug(" Confidential:", dataset.check_confidentiality())
- #logger.debug(" ---> 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', '').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("email_address", self.user.email_address)
- #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', '').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', '').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.')
-
- 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
-
- 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)
- save_user(self.user.__dict__, self.user.user_id)
-
-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"
-
- logger.debug("pwfields:", vars(pwfields))
- logger.debug("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 = self.key_prefix + ":" + verification_code
-
- data = json.dumps(dict(id=user.user_id,
- timestamp=timestamp())
- )
-
- 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)
-
-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, toaddr):
- from email.MIMEMultipart import MIMEMultipart
- from email.MIMEText import MIMEText
- verification_code = str(uuid.uuid4())
- key = self.key_prefix + ":" + verification_code
-
- data = {
- "verification_code": verification_code,
- "email_address": toaddr,
- "timestamp": timestamp()
- }
-
- save_verification_code(toaddr, verification_code)
-
-
- subject = self.subject
- 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):
- def __init__(self, unencrypted_password, salt, iterations, keylength, hashfunc):
- hashfunc = getattr(hashlib, hashfunc)
- logger.debug("hashfunc is:", hashfunc)
- # On our computer it takes around 1.4 seconds in 2013
- start_time = time.time()
- salt = base64.b64decode(salt)
- self.password = pbkdf2.pbkdf2_hex(str(unencrypted_password),
- salt, iterations, keylength, hashfunc)
- self.encrypt_time = round(time.time() - start_time, 3)
- logger.debug("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():
- user = DecodeUser(VerificationEmail.key_prefix).user
- user.confirmed = json.dumps(basic_info(), sort_keys=True)
- db_session.commit()
-
- # As long as they have access to the email account
- # We might as well log them in
-
- session_id_signed = LoginUser().successful_login(user)
- response = make_response(render_template("new_security/thank_you.html"))
- response.set_cookie(UserSession.cookie_name, session_id_signed)
- return response
-
-@app.route("/n/password_reset", methods=['GET'])
-def password_reset():
- """Entry point after user clicks link in E-mail"""
- 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()
- verification_code = request.args.get('code')
- hmac = request.args.get('hm')
-
- if verification_code:
- user_email = check_verification_code(verification_code)
- if user_email:
- user_details = get_user_by_unique_column('email_address', user_email)
- 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")
- else:
- return redirect(url_for("login"))
-
-@app.route("/n/password_reset_step2", methods=('POST',))
-def password_reset_step2():
- """Handle confirmation E-mail for password reset"""
- logger.debug("in password_reset request.url is:", request.url)
-
- errors = []
- user_id = request.form['user_encode']
-
- logger.debug("locals are:", locals())
-
-
- user = Bunch()
- password = request.form['password']
- set_password(password, user)
-
- set_user_attribute(user_id, "password", user.__dict__.get("password"))
-
- 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)
- logger.debug("in get_coded_user, data is:", data)
- data = json.loads(data)
- logger.debug("data is:", data)
- return model.User.query.get(data['id'])
-
-@app.route("/n/login", methods=('GET', 'POST'))
-def login():
- lu = LoginUser()
- 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"])
-
- user_details = get_user_by_unique_column("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(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"))
-
- user_details = get_user_by_unique_column("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(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
-
- def __init__(self):
- self.remember_me = False
- self.logged_in = False
-
- def oauth2_login(self, login_type, user_id):
- """Login via an OAuth2 provider"""
-
- user_details = get_user_by_unique_column("user_id", user_id)
- if user_details:
- user = model.User()
- user.id = user_details["user_id"] if user_details["user_id"] == None else "N/A"
- 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)
-
- if not params:
- 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
-
- return render_template(
- "new_security/login_user.html"
- , external_login=external_login
- , redis_is_available = is_redis_available())
- else:
- user_details = get_user_by_unique_column("email_address", params["email_address"])
- #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)
- 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)
-
- if valid and not user.confirmed:
- VerificationEmail(user)
- return render_template("new_security/verification_still_needed.html",
- subject=VerificationEmail.subject)
- if valid:
- if params.get('remember'):
- logger.debug("I will remember you")
- self.remember_me = True
-
- if 'import_collections' in params:
- import_col = "true"
- else:
- import_col = "false"
-
- #g.cookie_session.import_traits_to_user()
-
- self.logged_in = True
-
- return self.actual_login(user, import_collections=import_col)
-
- else:
- if user:
- self.unsuccessful_login(user)
- flash("Invalid email-address or password. Please try again.", "alert-danger")
- response = make_response(redirect(url_for('login')))
-
- return response
-
- def actual_login(self, user, assumed_by=None, import_collections=None):
- """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")
- response = make_response(redirect(url_for('index_page', import_collections=import_collections)))
- if self.remember_me:
- max_age = self.remember_time
- else:
- max_age = None
-
- response.set_cookie(UserSession.cookie_name, session_id_signed, max_age=max_age)
- return response
-
- def successful_login(self, user, assumed_by=None):
- login_rec = model.Login(user)
- login_rec.successful = True
- login_rec.session_id = str(uuid.uuid4())
- login_rec.assumed_by = assumed_by
- #session_id = "session_id:{}".format(login_rec.session_id)
- session_id_signature = actual_hmac_creation(login_rec.session_id)
- session_id_signed = login_rec.session_id + ":" + session_id_signature
- logger.debug("session_id_signed:", session_id_signed)
-
- if not user.id:
- user.id = ''
-
- session = dict(login_time = time.time(),
- user_id = user.id,
- user_name = user.full_name,
- user_email_address = user.email_address)
-
- key = UserSession.cookie_name + ":" + login_rec.session_id
- logger.debug("Key when signing:", key)
- Redis.hmset(key, session)
- if self.remember_me:
- expire_time = self.remember_time
- else:
- expire_time = THREE_DAYS
- Redis.expire(key, expire_time)
-
- return session_id_signed
-
- def unsuccessful_login(self, user):
- login_rec = model.Login(user)
- login_rec.successful = False
- db_session.add(login_rec)
- db_session.commit()
-
-@app.route("/n/logout")
-def logout():
- logger.debug("Logging out...")
- UserSession().delete_session()
- flash("You are now logged out. We hope you come back soon!")
- response = make_response(redirect(url_for('index_page')))
- # Delete the cookie
- response.set_cookie(UserSession.cookie_name, '', expires=0)
- return response
-
-
-@app.route("/n/forgot_password", methods=['GET'])
-def forgot_password():
- """Entry point for forgotten password"""
- print("ARGS: ", request.args)
- errors = {"no-email": request.args.get("no-email")}
- print("ERRORS: ", errors)
- return render_template("new_security/forgot_password.html", errors=errors)
-
-@app.route("/n/forgot_password_submit", methods=('POST',))
-def forgot_password_submit():
- """When a forgotten password form is submitted we get here"""
- params = request.form
- email_address = params['email_address']
- next_page = None
- if email_address != "":
- logger.debug("Wants to send password E-mail to ",email_address)
- user_details = get_user_by_unique_column("email_address", email_address)
- if user_details:
- ForgotPasswordEmail(user_details["email_address"])
- return render_template("new_security/forgot_password_step2.html",
- subject=ForgotPasswordEmail.subject)
- else:
- flash("The e-mail entered is not associated with an account.", "alert-danger")
- return redirect(url_for("forgot_password"))
-
- else:
- flash("You MUST provide an email", "alert-danger")
- return redirect(url_for("forgot_password"))
-
-@app.errorhandler(401)
-def unauthorized(error):
- return redirect(url_for('login'))
-
-def is_redis_available():
- try:
- Redis.ping()
- except:
- return False
- return True
-
-###
-# ZS: The following 6 functions require the old MySQL User accounts; I'm leaving them commented out just in case we decide to reimplement them using ElasticSearch
-###
-#def super_only():
-# try:
-# superuser = g.user_session.user_ob.superuser
-# except AttributeError:
-# superuser = False
-# if not superuser:
-# flash("You must be a superuser to access that page.", "alert-error")
-# abort(401)
-
-#@app.route("/manage/users")
-#def manage_users():
-# super_only()
-# template_vars = UsersManager()
-# return render_template("admin/user_manager.html", **template_vars.__dict__)
-
-#@app.route("/manage/user")
-#def manage_user():
-# super_only()
-# template_vars = UserManager(request.args)
-# return render_template("admin/ind_user_manager.html", **template_vars.__dict__)
-
-#@app.route("/manage/groups")
-#def manage_groups():
-# super_only()
-# template_vars = GroupsManager(request.args)
-# return render_template("admin/group_manager.html", **template_vars.__dict__)
-
-#@app.route("/manage/make_superuser")
-#def make_superuser():
-# super_only()
-# params = request.args
-# user_id = params['user_id']
-# user = model.User.query.get(user_id)
-# superuser_info = basic_info()
-# superuser_info['crowned_by'] = g.user_session.user_id
-# user.superuser = json.dumps(superuser_info, sort_keys=True)
-# db_session.commit()
-# flash("We've made {} a superuser!".format(user.name_and_org))
-# return redirect(url_for("manage_users"))
-
-#@app.route("/manage/assume_identity")
-#def assume_identity():
-# super_only()
-# params = request.args
-# user_id = params['user_id']
-# user = model.User.query.get(user_id)
-# assumed_by = g.user_session.user_id
-# return LoginUser().actual_login(user, assumed_by=assumed_by)
-
-
-@app.route("/n/register", methods=('GET', 'POST'))
-def register():
- params = None
- errors = None
-
-
- params = request.form if request.form else request.args
- params = params.to_dict(flat=True)
-
- if params:
- logger.debug("Attempting to register the user...")
- result = RegisterUser(params)
- errors = result.errors
-
- 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)
-
-
-################################# 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 = "&"
- else:
- combiner = "?"
- return url + combiner + "hm=" + hm
-
-def data_hmac(stringy):
- """Takes arbitray data string and appends :hmac so we know data hasn't been tampered with"""
- return stringy + ":" + actual_hmac_creation(stringy)
-
-
-def verify_url_hmac(url):
- """Pass in a url that was created with url_hmac and this assures it hasn't been tampered with"""
- logger.debug("url passed in to verify is:", url)
- # Verify parts are correct at the end - we expect to see &hm= or ?hm= followed by an hmac
- assert url[-23:-20] == "hm=", "Unexpected url (stage 1)"
- assert url[-24] in ["?", "&"], "Unexpected url (stage 2)"
- hmac = url[-20:]
- url = url[:-24] # Url without any of the hmac stuff
-
- #logger.debug("before urlsplit, url is:", url)
- #url = divide_up_url(url)[1]
- #logger.debug("after urlsplit, url is:", 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)
- hm = hmaced.hexdigest()
- # "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
-
-app.jinja_env.globals.update(url_for_hmac=url_for_hmac,
- data_hmac=data_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(toaddr, msg, fromaddr="no-reply@genenetwork.org"):
- """Send an E-mail through SMTP_CONNECT host. If SMTP_USERNAME is not
- 'UNKNOWN' TLS is used
-
- """
- if SMTP_USERNAME == 'UNKNOWN':
- logger.debug("SMTP: connecting with host "+SMTP_CONNECT)
- server = SMTP(SMTP_CONNECT)
- server.sendmail(fromaddr, toaddr, msg)
- else:
- logger.debug("SMTP: connecting TLS with host "+SMTP_CONNECT)
- server = SMTP(SMTP_CONNECT)
- server.starttls()
- logger.debug("SMTP: login with user "+SMTP_USERNAME)
- server.login(SMTP_USERNAME, SMTP_PASSWORD)
- logger.debug("SMTP: "+fromaddr)
- logger.debug("SMTP: "+toaddr)
- logger.debug("SMTP: "+msg)
- server.sendmail(fromaddr, toaddr, msg)
- server.quit()
- logger.info("Successfully sent email to "+toaddr)
-
-class GroupsManager(object):
- def __init__(self, kw):
- self.datasets = create_datasets_list()
-
-
-class RolesManager(object):
- def __init__(self):
- self.roles = model.Role.query.all()
- logger.debug("Roles are:", self.roles)