from datetime import datetime from datetime import timedelta from flask import current_app from flask import redirect from flask import render_template from flask import request from flask import session from flask import url_for import flask import json import logging import re import uuid from crypt_util import hash_pw import crypt_util from rz_mail import send_email_message from rz_req_handling import make_response__json, make_response__json__html from rz_user_db import User_Account log = logging.getLogger('rhizi') class User_Signup_Request(dict): def __init__(self, email_address, first_name, last_name, pw_plaintxt, rz_username): self['rz_username'] = rz_username self['email_address'] = email_address self['last_name'] = last_name self['first_name'] = first_name self['pw_plaintxt'] = pw_plaintxt self['submission_date'] = None self['validation_key'] = None def __str__(self): return 'email_address: %s' % (self['email_address']) def has_expired(self): date_now = datetime.now() dt_6h = timedelta(hours=6) dt = self['submission_date'] - date_now return dt > dt_6h def filter_expired_requests(us_req_map): for su_req_email, su_req in us_req_map.items(): if su_req.has_expired(): log.info('user sign-up request expired: %s' % (su_req)) del us_req_map[su_req_email] def activate_user_account(us_req): user_db = current_app.user_db existing_account = None try: existing_account = user_db.lookup_user__by_email_address(us_req['email_address']) except Exception as _: pass # thrown if user was not found if None != existing_account: raise Exception('account activation code reused: existing-account: %s' % (existing_account)) # calc pw hash pw_plaintxt = us_req['pw_plaintxt'] salt = current_app.rz_config.secret_key pw_hash = hash_pw(str(pw_plaintxt), salt) u_account = User_Account(first_name=us_req['first_name'], last_name=us_req['last_name'], rz_username=us_req['rz_username'], email_address=us_req['email_address'], pw_hash=pw_hash, role_set=['user']) user_db.user_add(u_account) log.info('user account activated: email: %s, rz_username: %s' % (us_req['email_address'], us_req['rz_username'])) def rest__login(): def sanitize_input(req): req_json = request.get_json() email_address = req_json['email_address'] p = req_json['password'] return email_address, p if request.method == 'POST': u_account = None try: email_address, p = sanitize_input(request) u_account = current_app.user_db.lookup_user__by_email_address(email_address) except: log.warn('failed to sanitize inputs. request: %s' % request) return make_response__json(status=401) # return empty response try: salt = current_app.rz_config.secret_key pw_hash = hash_pw(p, salt) current_app.user_db.validate_login(email_address=email_address, pw_hash=pw_hash) except Exception as e: # login failed log.warn('login: unauthorized: user: %s' % (email_address)) return make_response__json(status=401) # return empty response # login successful session['username'] = email_address log.debug('login: success: user: %s' % (email_address)) return make_response__json(status=200) # return empty response if request.method == 'GET': return render_template('login.html') def rest__logout(): """ REST API endpoint: logout """ # remove the username from the session if it's there u = session.pop('username', None) log.debug('logout: success: user: %s' % (u)) return redirect(url_for('login')) def rest__user_signup(): """ REST API endpoint: user sign up """ def add_user_signup_req(us_req_map, us_req): key = us_req['email_address'] us_req_map[key] = us_req # finally, add request def rm_user_signup_req(us_req_map, us_req): key = us_req['email_address'] del us_req_map[key] def sanitize_and_validate_input(req): req_json = request.get_json() # TODO: sanitize field_to_regex_map = {'first_name': r'\w{3,16}', 'last_name': r'\w{3,16}', 'rz_username': r'\w{3,16}', 'email_address': r'[^@]+@[^@]+\.[^@]+', 'pw_plaintxt': r'[A-Za-z0-9]{8,32}', # avoid symbols } for f_name, regex in field_to_regex_map.items(): f_val = req_json.get(f_name) if None == f_val: raise Exception('malformed signup request: missing field: %s' % (f_name)) if None == re.match(regex, f_val): raise Exception('malformed signup request: regex match failure: regex: %s, input: %s' % (regex, f_val)) first_name = req_json['first_name'] last_name = req_json['last_name'] rz_username = req_json['rz_username'] email_address = req_json['email_address'] pw_plaintxt = req_json['pw_plaintxt'] ret = User_Signup_Request(email_address=email_address, first_name=first_name, last_name=last_name, pw_plaintxt=pw_plaintxt, rz_username=rz_username) return ret def generate_email_confirmation_key(): """ generate a random UUID based string ID """ return str(uuid.uuid4()).replace('-', '') def get_or_init_usreq_map(): """ lazy user signup request map getter """ if not hasattr(current_app, 'usreq_email_to_req_map'): setattr(current_app, 'usreq_email_to_req_map', {}) us_req_map = current_app.usreq_email_to_req_map return us_req_map html_ok__submitted = '
Your request has been successfully submitted.
Please check your email to activate your account.
Your request has already been submitted.
please check your email to activate your account.
We are experiencing technical difficulty processing your request,
please try again later.
Activation failure,
please try again later.