from django.shortcuts import render from django.views.decorators.cache import never_cache from django.template.context_processors import csrf from django.contrib.auth.hashers import make_password, PBKDF2PasswordHasher from django.http import HttpResponse from .Common import get_redirect_url from .models import * import simplejson import random import string import datetime from django.utils import timezone USER_FRIENDLY_NAME = "Recovery Codes" class Hash(PBKDF2PasswordHasher): algorithm = 'pbkdf2_sha256_custom' iterations = getattr(settings,"RECOVERY_ITERATION",1) def delTokens(request): #Only when all MFA have been deactivated, or to generate new ! #We iterate only to clean if any error happend and multiple entry of RECOVERY created for one user for key in User_Keys.objects.filter(username=request.user.username, key_type = "RECOVERY"): if key.username == request.user.username: key.delete() def randomGen(n): return ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(n)) @never_cache def genTokens(request): #Delete old ones delTokens(request) #Then generate new one salt = randomGen(15) hashedKeys = [] clearKeys = [] for i in range(5): token = randomGen(5) + "-" + randomGen(5) hashedToken = make_password(token, salt, 'pbkdf2_sha256_custom') hashedKeys.append(hashedToken) clearKeys.append(token) uk=User_Keys() uk.username = request.user.username uk.properties={"secret_keys":hashedKeys, "salt":salt} uk.key_type="RECOVERY" uk.enabled = True uk.save() return HttpResponse(simplejson.dumps({"keys":clearKeys})) def verify_login(request, username, token): for key in User_Keys.objects.filter(username=username, key_type = "RECOVERY"): secret_keys = key.properties["secret_keys"] salt = key.properties["salt"] hashedToken = make_password(token, salt, "pbkdf2_sha256_custom") for i,token in enumerate(secret_keys): if hashedToken == token: secret_keys.pop(i) key.properties["secret_keys"] = secret_keys key.last_used= timezone.now() key.save() return [True, key.id, len(secret_keys) == 0] return [False] def getTokenLeft(request): uk = User_Keys.objects.filter(username=request.user.username, key_type = "RECOVERY") keyLeft=0 for key in uk: keyLeft += len(key.properties["secret_keys"]) return HttpResponse(simplejson.dumps({"left":keyLeft})) def recheck(request): context = csrf(request) context["mode"]="recheck" if request.method == "POST": if verify_login(request,request.user.username, token=request.POST["recovery"])[0]: import time request.session["mfa"]["rechecked_at"] = time.time() return HttpResponse(simplejson.dumps({"recheck": True}), content_type="application/json") else: return HttpResponse(simplejson.dumps({"recheck": False}), content_type="application/json") return render(request,"RECOVERY/recheck.html", context) @never_cache def auth(request): from .views import login context=csrf(request) if request.method=="POST": tokenLength = len(request.POST["recovery"]) if tokenLength == 11 and "RECOVERY" not in settings.MFA_UNALLOWED_METHODS: #Backup code check resBackup=verify_login(request, request.session["base_username"], token=request.POST["recovery"]) if resBackup[0]: mfa = {"verified": True, "method": "RECOVERY","id":resBackup[1], "lastBackup":resBackup[2]} # if getattr(settings, "MFA_RECHECK", False): # mfa["next_check"] = datetime.datetime.timestamp((datetime.datetime.now() # + datetime.timedelta( # seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX)))) request.session["mfa"] = mfa if resBackup[2]: #If the last bakup code has just been used, we return a response insead of redirecting to login context["lastBackup"] = True return render(request,"RECOVERY/Auth.html", context) return login(request) context["invalid"]=True elif request.method=="GET": mfa = request.session.get("mfa") if mfa and mfa["verified"] and mfa["lastBackup"]: return login(request) return render(request,"RECOVERY/Auth.html", context) @never_cache def start(request): """Start Managing recovery tokens""" context = get_redirect_url() if "mfa_reg" in request.session: context["mfa_redirect"] = request.session["mfa_reg"]["name"] return render(request,"RECOVERY/Add.html",context)