Source code for backend.recovery

"""
recovery.py
==============
Endpoints for operations related to matching users. All routes start with
/api/recovery
All incoming request parameters are wrapped in a JSON body.
All outgoing response returns are wrapped in a JSON entry with key 'payload',
like this:

.. code-block::

    {
      "error": "false",
      "error-msg": None,
        "payload": {
        "return-1": "true"
      }
    }


Note that method documentation assumes you are using jsonResponse/errorResponse
to generate the response, and only shows the actual returns within payload.
Ditto for request parameters.
"""
import flask
from flask import (Blueprint, g, request, session, current_app)
from form_response import jsonResponse, errorResponse
import subprocess
import random
import string
import os
from werkzeug.security import generate_password_hash
random.seed()

baseurl = 'localhost'
if 'BASEURL' in os.environ:
    baseurl = os.environ['BASEURL']

bp = Blueprint('recovery', __name__, url_prefix='/api/recovery')

recoverystr = "echo -n \"From:recovery@bruintutor.com\nTo:{email}\n\
Subject:Password Reset Link\n\nHere is your password reset link:\n\
{baseurl}/reset/{secret}\n--Bruintutor\nNote: this link expires in 10 minutes.\" | exim {email}"

[docs]@bp.route('/forgot', methods=('POST',)) def forgot(): """ POST submit a password reset request. Emails the user a recovery link. Parameters ---------- email: str email to reset to Notes ----- returns an empty response body on success. Raises ------ BadRequest Some part of the required parameters is missing. EmailNotFound no user associated with email. ServerBad Mailserver is not functioning. """ redis_client = current_app.config['RDSCXN'] try: email = request.get_json(force=True)['email'] except: return errorResponse('Bad request') for uid in redis_client.keys("user*"): if email == redis_client.hget(uid, 'email'): secret = ''.join(random.choices(string.ascii_letters, k=20)) try: subprocess.call( recoverystr.format(email=email, secret=secret, baseurl=baseurl), shell = True) redis_client.setex(f"recovery{uid}", 600, secret) return jsonResponse() except: return errorResponse('Server bad') return errorResponse('Email not found')
[docs]@bp.route('/reset', methods=('POST',)) def reset (): """ POST complete a password reset request. Parameters ---------- secret: str secret sent in email password: str new password Notes ----- returns an empty response body on success. Raises ------ BadRequest Some part of the required parameters is missing. NoRecovery No recovery has been requested. """ redis_client = current_app.config['RDSCXN'] try: d = request.get_json(force=True) secret, password = d['secret'], d['password'] except: return errorResponse('Bad request') for uid in redis_client.keys("user*"): if secret == redis_client.get(f"recovery{uid}"): redis_client.hset(uid, 'password', generate_password_hash(password)) redis_client.delete(f"recovery{uid}") return jsonResponse() return errorResponse('No recovery requested, or recovery expired')