Added MFRENAMEMETHOD, MFA_REDIRECT_USER_TO_LAST_METHOD, Alot of theme fixes
This commit is contained in:
@@ -137,7 +137,7 @@ def authenticate_complete(request):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return HttpResponse(simplejson.dumps({'status': "ERR",
|
return HttpResponse(simplejson.dumps({'status': "ERR",
|
||||||
"message": excep.message}),
|
"message": str(excep)}),
|
||||||
content_type = "application/json")
|
content_type = "application/json")
|
||||||
|
|
||||||
if request.session.get("mfa_recheck", False):
|
if request.session.get("mfa_recheck", False):
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import simplejson
|
|||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import datetime
|
import datetime
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
USER_FRIENDLY_NAME = "Recovery Codes"
|
||||||
|
|
||||||
class Hash(PBKDF2PasswordHasher):
|
class Hash(PBKDF2PasswordHasher):
|
||||||
algorithm = 'pbkdf2_sha256_custom'
|
algorithm = 'pbkdf2_sha256_custom'
|
||||||
@@ -38,6 +41,7 @@ def genTokens(request):
|
|||||||
hashedKeys.append(hashedToken)
|
hashedKeys.append(hashedToken)
|
||||||
clearKeys.append(token)
|
clearKeys.append(token)
|
||||||
uk=User_Keys()
|
uk=User_Keys()
|
||||||
|
|
||||||
uk.username = request.user.username
|
uk.username = request.user.username
|
||||||
uk.properties={"secret_keys":hashedKeys, "salt":salt}
|
uk.properties={"secret_keys":hashedKeys, "salt":salt}
|
||||||
uk.key_type="RECOVERY"
|
uk.key_type="RECOVERY"
|
||||||
@@ -51,10 +55,11 @@ def verify_login(request, username, token):
|
|||||||
secret_keys = key.properties["secret_keys"]
|
secret_keys = key.properties["secret_keys"]
|
||||||
salt = key.properties["salt"]
|
salt = key.properties["salt"]
|
||||||
hashedToken = make_password(token, salt, "pbkdf2_sha256_custom")
|
hashedToken = make_password(token, salt, "pbkdf2_sha256_custom")
|
||||||
for i in range(len(secret_keys)):
|
for i,token in enumerate(secret_keys):
|
||||||
if hashedToken == secret_keys[i]:
|
if hashedToken == token:
|
||||||
secret_keys.pop(i)
|
secret_keys.pop(i)
|
||||||
key.properties["secret_keys"] = secret_keys
|
key.properties["secret_keys"] = secret_keys
|
||||||
|
key.last_used= timezone.now()
|
||||||
key.save()
|
key.save()
|
||||||
return [True, key.id, len(secret_keys) == 0]
|
return [True, key.id, len(secret_keys) == 0]
|
||||||
return [False]
|
return [False]
|
||||||
@@ -89,10 +94,10 @@ def auth(request):
|
|||||||
resBackup=verify_login(request, request.session["base_username"], token=request.POST["recovery"])
|
resBackup=verify_login(request, request.session["base_username"], token=request.POST["recovery"])
|
||||||
if resBackup[0]:
|
if resBackup[0]:
|
||||||
mfa = {"verified": True, "method": "RECOVERY","id":resBackup[1], "lastBackup":resBackup[2]}
|
mfa = {"verified": True, "method": "RECOVERY","id":resBackup[1], "lastBackup":resBackup[2]}
|
||||||
if getattr(settings, "MFA_RECHECK", False):
|
# if getattr(settings, "MFA_RECHECK", False):
|
||||||
mfa["next_check"] = datetime.datetime.timestamp((datetime.datetime.now()
|
# mfa["next_check"] = datetime.datetime.timestamp((datetime.datetime.now()
|
||||||
+ datetime.timedelta(
|
# + datetime.timedelta(
|
||||||
seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))))
|
# seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))))
|
||||||
request.session["mfa"] = mfa
|
request.session["mfa"] = mfa
|
||||||
if resBackup[2]:
|
if resBackup[2]:
|
||||||
#If the last bakup code has just been used, we return a response insead of redirecting to login
|
#If the last bakup code has just been used, we return a response insead of redirecting to login
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<script type="application/javascript" src="{% static 'mfa/js/ua-parser.min.js' %}"></script>
|
<script type="application/javascript" src="{% static 'mfa/js/ua-parser.min.js' %}"></script>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-10 col-sm-offset-1 col-xs-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
|
<div class="col-sm-10 col-sm-offset-1 col-xs-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2 offset-2 col-8">
|
||||||
<div class="panel panel-default card">
|
<div class="panel panel-default card">
|
||||||
<div class="panel-heading card-header">
|
<div class="panel-heading card-header">
|
||||||
<strong> Security Key</strong>
|
<strong> Security Key</strong>
|
||||||
@@ -35,10 +35,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 mb-3" style="padding-left: 15px">
|
<div class="col-md-12 mb-3" style="padding-left: 25px">
|
||||||
|
|
||||||
{% if request.session.mfa_methods|length > 1 %}
|
{% if request.session.mfa_methods|length > 1 %}
|
||||||
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -45,31 +45,31 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div align="center">
|
<div class="offset-5 col-2" style="text-align: center">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-success dropdown-toggle" data-toggle="dropdown" data-bs-toggle="dropdown">
|
<button class="btn btn-success dropdown-toggle" data-toggle="dropdown" data-bs-toggle="dropdown">
|
||||||
Add Method <span class="caret"></span>
|
Add Method <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
{% if not 'TOTP' in UNALLOWED_AUTHEN_METHODS %}
|
{% if not 'TOTP' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<li><a class="dropdown-item" href="{% url 'start_new_otop' %}">Authenticator app</a></li>
|
<li><a class="dropdown-item" href="{% url 'start_new_otop' %}">{% if 'TOTP' in RENAME_METHODS.keys %}{{ RENAME_METHODS.TOTP }}{% else %}Authenticator app{% endif %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not 'Email' in UNALLOWED_AUTHEN_METHODS %}
|
{% if not 'Email' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<li><a class="dropdown-item" href="{% url 'start_email' %}">Email Token</a></li>
|
<li><a class="dropdown-item" href="{% url 'start_email' %}">{% if 'Email' in RENAME_METHODS.keys %}{{ RENAME_METHODS.Email }}{% else %}Email Token{% endif %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not 'U2F' in UNALLOWED_AUTHEN_METHODS %}
|
{% if not 'U2F' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<li><a class="dropdown-item" href="{% url 'start_u2f' %}">Security Key</a></li>
|
<li><a class="dropdown-item" href="{% url 'start_u2f' %}">{% if 'U2F' in RENAME_METHODS.keys %}{{ RENAME_METHODS.U2F }}{% else %}Security Key{% endif %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not 'FIDO2' in UNALLOWED_AUTHEN_METHODS %}
|
{% if not 'FIDO2' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<li><a class="dropdown-item" href="{% url 'start_fido2' %}">FIDO2 Security Key</a></li>
|
<li><a class="dropdown-item" href="{% url 'start_fido2' %}">{% if 'FIDO2' in RENAME_METHODS.keys %}{{ RENAME_METHODS.FIDO2 }}{% else %}FIDO2 Security Key{% endif %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not 'Trusted_Devices' in UNALLOWED_AUTHEN_METHODS %}
|
{% if not 'Trusted_Devices' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<li><a class="dropdown-item" href="{% url 'start_td' %}">Trusted Device</a></li>
|
<li><a class="dropdown-item" href="{% url 'start_td' %}">{% if 'Trusted_Devices' in RENAME_METHODS.keys %}{{ RENAME_METHODS.Trusted_Devices }}{% else %}Trusted Device{% endif %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -86,11 +86,11 @@
|
|||||||
{% for key in keys %}
|
{% for key in keys %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
||||||
<td>{{ key.key_type }}</td>
|
<td>{{ key.name }}</td>
|
||||||
<td>{{ key.added_on }}</td>
|
<td>{{ key.added_on }}</td>
|
||||||
<td>{{ key.expires }}</td>
|
<td>{% if key.expires %}{{ key.expires }}{% else %}N/A{% endif %}</td>
|
||||||
<td>{% if key.device %}{{ key.device }}{% endif %}</td>
|
<td>{% if key.device %}{{ key.device }}{% endif %}</td>
|
||||||
<td>{{ key.last_used }}</td>
|
<td>{% if key.last_used %}{{ key.last_used }}{% else %}Never{% endif %}</td>
|
||||||
{% if key.key_type in HIDE_DISABLE %}
|
{% if key.key_type in HIDE_DISABLE %}
|
||||||
<td>{% if key.enabled %}On{% else %} Off{% endif %}</td>
|
<td>{% if key.enabled %}On{% else %} Off{% endif %}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
@@ -106,11 +106,11 @@
|
|||||||
{% if "RECOVERY" not in UNALLOWED_AUTHEN_METHODS %}
|
{% if "RECOVERY" not in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
||||||
<td>RECOVERY</td>
|
<td>{{ recovery.name }}</td>
|
||||||
<td></td>
|
<td>{{ recovery.added_on }}</td>
|
||||||
<td></td>
|
<td>N/A</td>
|
||||||
<td></td>
|
<td>N/A</td>
|
||||||
<td></td>
|
<td>{% if recovery.last_used %}{{ recovery.last_used }}{% else %}Never{% endif %}</td>
|
||||||
<td>On</td>
|
<td>On</td>
|
||||||
<td><a href="{% url 'manage_recovery_codes' %}"> <span class="fa fa-wrench fa-solid fa-wrench bi bi-wrench-fill"></span></a></td>
|
<td><a href="{% url 'manage_recovery_codes' %}"> <span class="fa fa-wrench fa-solid fa-wrench bi bi-wrench-fill"></span></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
{% extends "mfa_auth_base.html" %}
|
{% extends "mfa_auth_base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<br/>
|
|
||||||
<br/>
|
<br/>
|
||||||
<div class='container'>
|
<div class='container'>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-10 col-sm-offset-1 col-xs-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
|
<div class="col-sm-10 col-sm-offset-1 col-xs-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2 offset-2 col-8">
|
||||||
<div class="panel panel-default card">
|
<div class="panel panel-default card">
|
||||||
<div class="panel-heading card-header">
|
<div class="panel-heading card-header">
|
||||||
<strong> Select Second Verification Method</strong>
|
<strong> Select Second Verification Method</strong>
|
||||||
@@ -15,11 +14,11 @@
|
|||||||
{% for method in request.session.mfa_methods %}
|
{% for method in request.session.mfa_methods %}
|
||||||
|
|
||||||
<li><a href="{% url "mfa_goto" method %}">
|
<li><a href="{% url "mfa_goto" method %}">
|
||||||
{% if method == "TOTP" %}Authenticator App
|
{% if method == "TOTP" %}{% if 'TOTP' in RENAME_METHODS %}{{ RENAME_METHODS.TOTP }}{% else %}Authenticator App{% endif %}
|
||||||
{% elif method == "Email" %}Send OTP by Email
|
{% elif method == "Email" %}{% if 'Email' in RENAME_METHODS %}{{ RENAME_METHODS.Email }}{% else %}Send OTP by Email{% endif %}
|
||||||
{% elif method == "U2F" %}Secure Key
|
{% elif method == "U2F" %}{% if 'U2F' in RENAME_METHODS %}{{ RENAME_METHODS.U2F }}{% else %}Secure Key{% endif %}
|
||||||
{% elif method == "FIDO2" %}FIDO2 Secure Key
|
{% elif method == "FIDO2" %}{% if 'FIDO2' in RENAME_METHODS %}{{ RENAME_METHODS.FIDO2 }}{% else %}FIDO2 Secure Key{% endif %}
|
||||||
{% elif method == "RECOVERY" %}Recovery Code
|
{% elif method == "RECOVERY" %}{% if 'RECOVERY' in RENAME_METHODS %}{{ RENAME_METHODS.RECOVERY }}{% else %}Recovery Code{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a> </li>
|
</a> </li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
13
mfa/views.py
13
mfa/views.py
@@ -1,3 +1,5 @@
|
|||||||
|
import importlib
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.http import HttpResponse,HttpResponseRedirect
|
from django.http import HttpResponse,HttpResponseRedirect
|
||||||
from .models import *
|
from .models import *
|
||||||
@@ -16,13 +18,15 @@ from user_agents import parse
|
|||||||
def index(request):
|
def index(request):
|
||||||
keys=[]
|
keys=[]
|
||||||
context={"keys":User_Keys.objects.filter(username=request.user.username),"UNALLOWED_AUTHEN_METHODS":settings.MFA_UNALLOWED_METHODS
|
context={"keys":User_Keys.objects.filter(username=request.user.username),"UNALLOWED_AUTHEN_METHODS":settings.MFA_UNALLOWED_METHODS
|
||||||
,"HIDE_DISABLE":getattr(settings,"MFA_HIDE_DISABLE",[])}
|
,"HIDE_DISABLE":getattr(settings,"MFA_HIDE_DISABLE",[]),'RENAME_METHODS':getattr(settings,'MFA_RENAME_METHODS',{})}
|
||||||
for k in context["keys"]:
|
for k in context["keys"]:
|
||||||
if k.key_type =="Trusted Device" :
|
k.name = getattr(settings,'MFA_RENAME_METHODS',{}).get(k.key_type,k.key_type)
|
||||||
|
if k.key_type =="Trusted Device":
|
||||||
setattr(k,"device",parse(k.properties.get("user_agent","-----")))
|
setattr(k,"device",parse(k.properties.get("user_agent","-----")))
|
||||||
elif k.key_type == "FIDO2":
|
elif k.key_type == "FIDO2":
|
||||||
setattr(k,"device",k.properties.get("type","----"))
|
setattr(k,"device",k.properties.get("type","----"))
|
||||||
elif k.key_type == "RECOVERY":
|
elif k.key_type == "RECOVERY":
|
||||||
|
context["recovery"] = k
|
||||||
continue
|
continue
|
||||||
keys.append(k)
|
keys.append(k)
|
||||||
context["keys"]=keys
|
context["keys"]=keys
|
||||||
@@ -42,10 +46,13 @@ def verify(request,username):
|
|||||||
|
|
||||||
if len(methods)==1:
|
if len(methods)==1:
|
||||||
return HttpResponseRedirect(reverse(methods[0].lower()+"_auth"))
|
return HttpResponseRedirect(reverse(methods[0].lower()+"_auth"))
|
||||||
|
if getattr(settings,"MFA_ALWAYS_GO_TO_LAST_METHOD",False):
|
||||||
|
keys = keys.exclude(last_used__isnull=True).order_by("last_used")
|
||||||
|
return HttpResponseRedirect(reverse(keys[0].key_type.lower() + "_auth"))
|
||||||
return show_methods(request)
|
return show_methods(request)
|
||||||
|
|
||||||
def show_methods(request):
|
def show_methods(request):
|
||||||
return render(request,"select_mfa_method.html", {})
|
return render(request,"select_mfa_method.html", {'RENAME_METHODS':getattr(settings,'MFA_RENAME_METHODS',{})})
|
||||||
|
|
||||||
def reset_cookie(request):
|
def reset_cookie(request):
|
||||||
response=HttpResponseRedirect(settings.LOGIN_URL)
|
response=HttpResponseRedirect(settings.LOGIN_URL)
|
||||||
|
|||||||
Reference in New Issue
Block a user