diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..f4e7ec5 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index b6d98ff..38aff96 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,6 @@ coverage.xml .pytest_cache/ # Translations -*.mo *.pot # Django stuff: diff --git a/MANIFEST.in b/MANIFEST.in index 05b33b9..d337313 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ recursive-include mfa/templates * -recursive-include mfa/static * \ No newline at end of file +recursive-include mfa/static * +recursive-include mfa/locale * \ No newline at end of file diff --git a/mfa/.DS_Store b/mfa/.DS_Store new file mode 100644 index 0000000..5cfa419 Binary files /dev/null and b/mfa/.DS_Store differ diff --git a/mfa/FIDO2.py b/mfa/FIDO2.py index dcdf9f2..a2b5b5b 100644 --- a/mfa/FIDO2.py +++ b/mfa/FIDO2.py @@ -16,7 +16,7 @@ import datetime from .Common import get_redirect_url from django.utils import timezone - +from django.http import JsonResponse def recheck(request): """Starts FIDO2 recheck""" @@ -49,13 +49,15 @@ def begin_registeration(request): def complete_reg(request): """Completes the registeration, called by API""" try: + if not "fido_state" in request.session: + return JsonResponse({'status': 'ERR', "message": "FIDO Status can't be found, please try again"}) data = cbor.decode(request.body) client_data = CollectedClientData(data['clientDataJSON']) att_obj = AttestationObject((data['attestationObject'])) server = getServer() auth_data = server.register_complete( - request.session['fido_state'], + request.session.pop['fido_state'], client_data, att_obj ) @@ -75,7 +77,7 @@ def complete_reg(request): client.captureException() except: pass - return HttpResponse(simplejson.dumps({'status': 'ERR', "message": "Error on server, please try again later"})) + return JsonResponse({'status': 'ERR', "message": "Error on server, please try again later"}) def start(request): diff --git a/mfa/TrustedDevice.py b/mfa/TrustedDevice.py index 94b2136..26dd957 100644 --- a/mfa/TrustedDevice.py +++ b/mfa/TrustedDevice.py @@ -2,11 +2,12 @@ import random from django.shortcuts import render from django.http import HttpResponse -from django.template.context import RequestContext +from django.utils.translation import gettext from django.template.context_processors import csrf from .models import * import user_agents from django.utils import timezone +from django.utils.translation import gettext def id_generator(size=6, chars=string.ascii_uppercase + string.digits): x=''.join(random.choice(chars) for _ in range(size)) @@ -28,7 +29,7 @@ def trust_device(request): tk.properties["status"]="trusted" tk.save() del request.session["td_id"] - return HttpResponse("OK") + return HttpResponse(gettext("OK")) def checkTrusted(request): res = "" @@ -70,7 +71,7 @@ def add(request): ua=request.META['HTTP_USER_AGENT'] agent=user_agents.parse(ua) if agent.is_pc: - context["invalid"]="This is a PC, it can't used as a trusted device." + context["invalid"]=gettext("This is a PC, it can't used as a trusted device.") else: tk.properties["user_agent"]=ua tk.save() @@ -80,7 +81,7 @@ def add(request): # context["success"]=True else: - context["invalid"]="The username or key is wrong, please check and try again." + context["invalid"]=gettext("The username or key is wrong, please check and try again.") return render(request,"TrustedDevices/Add.html", context) @@ -110,11 +111,11 @@ def send_email(request): if e=="": e=request.session.get("user",{}).get("email","") if e=="": - res = "User has no email on the system." - elif send([e],"Add Trusted Device Link",body): - res="Sent Successfully" + res = gettext("User has no email on the system.") + elif send([e],gettext("Add Trusted Device Link"),body): + res=gettext("Sent Successfully") else: - res="Error occured, please try again later." + res=gettext("Error occured, please try again later.") return HttpResponse(res) diff --git a/mfa/U2F.py b/mfa/U2F.py index 0eb04f0..a534e4a 100644 --- a/mfa/U2F.py +++ b/mfa/U2F.py @@ -10,11 +10,12 @@ from django.template.context_processors import csrf from django.conf import settings from django.http import HttpResponse +from django.utils.translation import gettext from .models import * from .views import login from .Common import get_redirect_url -import datetime from django.utils import timezone +from django.utils.translation import gettext def recheck(request): context = csrf(request) @@ -37,7 +38,7 @@ def check_errors(request, data): if "errorCode" in data: if data["errorCode"] == 0: return True if data["errorCode"] == 4: - return HttpResponse("Invalid Security Key") + return HttpResponse(gettext("Invalid Security Key")) if data["errorCode"] == 1: return auth(request) return True @@ -90,7 +91,7 @@ def bind(request): cert_hash=hashlib.md5(cert.public_bytes(Encoding.PEM)).hexdigest() q=User_Keys.objects.filter(key_type="U2F", properties__icontains= cert_hash) if q.exists(): - return HttpResponse("This key is registered before, it can't be registered again.") + return HttpResponse(gettext("This key is registered before, it can't be registered again.")) User_Keys.objects.filter(username=request.user.username,key_type="U2F").delete() uk = User_Keys() uk.username = request.user.username diff --git a/mfa/__init__.py b/mfa/__init__.py index d9d106a..bf03578 100644 --- a/mfa/__init__.py +++ b/mfa/__init__.py @@ -1 +1 @@ -__version__="2.2.0" +__version__="2.4.1" diff --git a/mfa/helpers.py b/mfa/helpers.py index a61c769..e56f0f5 100644 --- a/mfa/helpers.py +++ b/mfa/helpers.py @@ -1,9 +1,8 @@ -import pyotp from .models import * from . import TrustedDevice, U2F, FIDO2, totp import simplejson from django.shortcuts import HttpResponse -from mfa.views import verify,goto +from mfa.views import verify def has_mfa(request,username): if User_Keys.objects.filter(username=username,enabled=1).count()>0: return verify(request, username) diff --git a/mfa/locale/de/LC_MESSAGES/django.mo b/mfa/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 0000000..ee56ea2 Binary files /dev/null and b/mfa/locale/de/LC_MESSAGES/django.mo differ diff --git a/mfa/locale/de/LC_MESSAGES/django.po b/mfa/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..9092043 --- /dev/null +++ b/mfa/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,359 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-07-07 14:18+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: mfa/TrustedDevice.py:32 mfa/views.py:101 +msgid "OK" +msgstr "OK" + +#: mfa/TrustedDevice.py:74 +msgid "This is a PC, it can't used as a trusted device." +msgstr "Dies ist ein PC, Sie können diesen nicht als Trusted Device verwenden" + +#: mfa/TrustedDevice.py:84 +msgid "The username or key is wrong, please check and try again." +msgstr "Ihr Username oder Key ist falsch, bitte prüfen Sie diese und probieren es erneut" + +#: mfa/TrustedDevice.py:114 +msgid "User has no email on the system." +msgstr "Der Nutzer hat keine Email auf dem System hinterlegt" + +#: mfa/TrustedDevice.py:115 +msgid "Add Trusted Device Link" +msgstr "Link zu Trusted Device hinzufügen" + +#: mfa/TrustedDevice.py:116 +msgid "Sent Successfully" +msgstr "erfolgreich gesendet" + +#: mfa/TrustedDevice.py:118 +msgid "Error occured, please try again later." +msgstr "Ein Fehler trat auf, bitte versuchen Sie es erneut" + +#: mfa/U2F.py:41 +msgid "Invalid Security Key" +msgstr "Ungültiger Sicherheitsschlüssel" + +#: mfa/U2F.py:94 +msgid "This key is registered before, it can't be registered again." +msgstr "Dieser Schlüssel wurde bereits registriert" + +#: mfa/templates/ApproveLogin/Add.html:6 +msgid "Title" +msgstr "Titel" + +#: mfa/templates/Email/mfa_email_token_template.html:7 +#: mfa/templates/TrustedDevices/email.html:2 +msgid "Dear" +msgstr "Sehr geehrte/r" + +#: mfa/templates/Email/mfa_email_token_template.html:8 +msgid "Your OTP is" +msgstr "Ihr OTP ist" + +#: mfa/templates/Email/mfa_email_token_template.html:10 +msgid "Thank you" +msgstr "Danke sehr" + +#: mfa/templates/Email/recheck.html:13 +msgid "Email One Time Password" +msgstr "Email Einmal Passwort" + +#: mfa/templates/Email/recheck.html:31 mfa/templates/TOTP/recheck.html:25 +msgid "Sorry, The provided token is not valid." +msgstr "Entschuldigung, der angegebene Token ist nicht validiert" + +#: mfa/templates/Email/recheck.html:42 +msgid "Enter the code sent to your email." +msgstr "Bitte geben Sie den Code aus der Email ein" + +#: mfa/templates/FIDO2/Add.html:16 mfa/templates/MFA.html:34 +msgid "FIDO2 Security Key" +msgstr "FIDO2 Security Key" + +#: mfa/templates/FIDO2/Add.html:21 mfa/templates/TOTP/Add.html:16 +#: mfa/templates/U2F/Add.html:20 +msgid "Your device is added successfully." +msgstr "Ihr Gerät wurde erfolgreich hinzugefügt" + +#: mfa/templates/FIDO2/Add.html:28 +msgid "Your browser should ask you to confirm you identity." +msgstr "Ihr Browsser sollte Sie nun um eine Identitätsbestätigung fragen" + +#: mfa/templates/FIDO2/recheck.html:12 mfa/templates/MFA.html:31 +msgid "Security Key" +msgstr "Security Key" + +#: mfa/templates/FIDO2/recheck.html:19 +msgid "Welcome back" +msgstr "Wilkommen zurück" + +#: mfa/templates/FIDO2/recheck.html:21 +msgid "Not me" +msgstr "Nicht ich" + +#: mfa/templates/FIDO2/recheck.html:26 +msgid "please press the button on your security key to prove it is you." +msgstr "Bitte bestätigen Sie Ihre Identität indem Sie auf den Knopf auf Ihrem Securty Key drücken" + +#: mfa/templates/FIDO2/recheck.html:46 mfa/templates/TOTP/recheck.html:63 +#: mfa/templates/U2F/recheck.html:35 +msgid "Select Another Method" +msgstr "Weitere Methode auswählen" + +#: mfa/templates/MFA.html:21 +msgid "Add Method" +msgstr "Methode hinzufügen" + +#: mfa/templates/MFA.html:25 +msgid "Authenticator app" +msgstr "Authenticator app" + +#: mfa/templates/MFA.html:28 +msgid "Email Token" +msgstr "Email Token" + +#: mfa/templates/MFA.html:37 +msgid "Trusted Device" +msgstr "Trusted Device" + +#: mfa/templates/MFA.html:47 +msgid "Type" +msgstr "Typ" + +#: mfa/templates/MFA.html:48 +msgid "Date Added" +msgstr "Hinzufügedatum" + +#: mfa/templates/MFA.html:49 +msgid "Expires On" +msgstr "läuft ab am" + +#: mfa/templates/MFA.html:50 mfa/templates/TrustedDevices/user-agent.html:12 +msgid "Device" +msgstr "Gerät" + +#: mfa/templates/MFA.html:51 +msgid "Last Used" +msgstr "Zuletzt verwendet" + +#: mfa/templates/MFA.html:52 +msgid "Status" +msgstr "Status" + +#: mfa/templates/MFA.html:53 +msgid "Delete" +msgstr "Löschen" + +#: mfa/templates/MFA.html:64 +msgid "On" +msgstr "An" + +#: mfa/templates/MFA.html:64 +msgid "Off" +msgstr "Aus" + +#: mfa/templates/MFA.html:75 +msgid "You didn't have any keys yet." +msgstr "Sie haben noch keine Sicherheitsschlüssel" + +#: mfa/templates/TOTP/Add.html:17 +msgid "You entered wrong numbers, please try again" +msgstr "Die Eingabe ist ungültig, bitte versuchen Sie es erneut" + +#: mfa/templates/TOTP/Add.html:94 +msgid "Adding Authenticator" +msgstr "Authenticator hinzufügen" + +#: mfa/templates/TOTP/Add.html:98 +msgid "Scan the image below with the two-factor authentication app on your" +msgstr "Scannen Sie das Bild mit einer 2 Faktor Authenticator App" + +#: mfa/templates/TOTP/Add.html:99 +msgid "phone/P" +msgstr "Telfon" + +#: mfa/templates/TOTP/Add.html:99 +msgid "If you can’t use a barcode," +msgstr "Wenn Sie keinene Barcode verwenden können," + +#: mfa/templates/TOTP/Add.html:100 +msgid "enter this text" +msgstr "geben Sie diesen Text ein" + +#: mfa/templates/TOTP/Add.html:100 +msgid "instead." +msgstr "stattdessen." + +#: mfa/templates/TOTP/Add.html:109 +msgid "Enter the six-digit code from the application" +msgstr "Geben Sie den 6 stelligen Code aus Ihrer App ein" + +#: mfa/templates/TOTP/Add.html:110 +msgid "" +"After scanning the barcode image, the app will display a six-digit code that " +"you can enter below." +msgstr "Nach dem Scannen des Bildes wird Ihre App Ihnen einen 6 stelligen Code zeigen, den sie hier unten eingeben können" + +#: mfa/templates/TOTP/Add.html:120 +msgid "Enable" +msgstr "Erlauben" + +#: mfa/templates/TOTP/Add.html:124 +msgid "Cancel" +msgstr "Abbrechen" + +#: mfa/templates/TOTP/recheck.html:15 +msgid "One Time Password" +msgstr "One Time Passwort" + +#: mfa/templates/TOTP/recheck.html:36 +msgid "Enter the 6-digits on your authenticator." +msgstr "Bitte 6 stelligen Code aus Ihrer Authenticator App eingeben" + +#: mfa/templates/TrustedDevices/Add.html:15 +#: mfa/templates/TrustedDevices/Done.html:15 +#: mfa/templates/TrustedDevices/start.html:16 +msgid "Add Trusted Device" +msgstr "vertrauenswürdiges Gerät hinzufügen" + +#: mfa/templates/TrustedDevices/Add.html:20 +msgid "Please check your PC window, to continue the process" +msgstr "Bitte prüfen Sie Ihren Browser Bildschirm um fortzufahren" + +#: mfa/templates/TrustedDevices/Add.html:24 +#: mfa/templates/TrustedDevices/Done.html:19 +msgid "Your device is now trusted, please try to" +msgstr "Ihr Gerät gilt nun als vertrauenswürdig, bitte versuchen Sie" + +#: mfa/templates/TrustedDevices/Add.html:24 +#: mfa/templates/TrustedDevices/Done.html:20 +msgid "login" +msgstr "login" + +#: mfa/templates/TrustedDevices/Add.html:27 +msgid "Please make sure you are not in private (incognito) mode" +msgstr "Bitte stellen Sie sicher, dass Sie nicht im incognito Modus sind" + +#: mfa/templates/TrustedDevices/Add.html:75 +msgid "I confirm that this device is mine and it is only used by me" +msgstr "Ich bestätige, dass dieses Gerät meins ist und nur von mir verwendet wird" + +#: mfa/templates/TrustedDevices/email.html:3 +msgid "" +"You requested the link to add a new trusted device, please follow the link " +"below" +msgstr "Sie haben einen Link für eine neues vertrauenswürdiges Gerät angefordert, bitte folgen Sie dem Link" + +#: mfa/templates/TrustedDevices/start.html:21 +msgid "" +"You can't add any more devices, you need to remove previously trusted " +"devices first." +msgstr "Sie können nicht mehr als ein vertrauenswürdiges Gerät hinzufügen, bitte entfernen Sie bisher hinzugefügte Geräte zuvor" + +#: mfa/templates/TrustedDevices/start.html:23 +msgid "Allow access from mobile phone and tables." +msgstr "Zugang vom Mobilgerät oder Tablet erlauben" + +#: mfa/templates/TrustedDevices/start.html:26 +msgid "Using your mobile/table, open Chrome/Firefox" +msgstr "Auf Ihrem Mobilen Gerät, öffnen Sie Chrome/Firefox" + +#: mfa/templates/TrustedDevices/start.html:27 +msgid "Go to" +msgstr "Gehe zu" + +#: mfa/templates/TrustedDevices/start.html:33 +msgid "Enter your username & following 6 digits" +msgstr "Geben Sie Ihren Usernamen und den 6 Stelligen Code ein" + +#: mfa/templates/TrustedDevices/start.html:36 +msgid "This window will ask to confirm the device." +msgstr "Dieser Dialog wird Sie um eine Bestätigung des Geräts bitten" + +#: mfa/templates/TrustedDevices/user-agent.html:4 +msgid "Browser" +msgstr "Browser" + +#: mfa/templates/TrustedDevices/user-agent.html:8 +msgid "Version" +msgstr "Version" + +#: mfa/templates/U2F/Add.html:14 +msgid "Adding Security Key" +msgstr "Sicherheitsschlüssel hinzufügen" + +#: mfa/templates/U2F/Add.html:23 +msgid "Your secure Key should be flashing now, please press on button." +msgstr "Ihr Sicherheitsschlüssel sollte nun blinken, bitte drücken Sie auf den Knopf" + +#: mfa/templates/U2F/recheck.html:14 +msgid "Your key should be flashing now, please press the button." +msgstr "Ihr Key sollte nun blinken, bitte drücken Sie auf den Knopf" + +#: mfa/templates/U2F/recheck.html:25 +msgid "U2F must work under secure context" +msgstr "U2F muss unter einem sicheren context (SSL) verwendet werden" + +#: mfa/templates/modal.html:14 +msgid "Close" +msgstr "Schliessen" + +#: mfa/templates/select_mfa_method.html:12 +msgid "Select Second Verification Method" +msgstr "Wählen Sie eine zweite Verifikations Methode" + +#: mfa/templates/select_mfa_method.html:19 +msgid "Authenticator App" +msgstr "Authenticator App" + +#: mfa/templates/select_mfa_method.html:20 +msgid "Send OTP by Email" +msgstr "OTP über Email versenden" + +#: mfa/templates/select_mfa_method.html:21 +msgid "Secure Key" +msgstr "Sicherheitsschlüssel" + +#: mfa/templates/select_mfa_method.html:22 +msgid "FIDO2 Secure Key" +msgstr "FIDO2 Secure Key" + +#: mfa/views.py:72 +msgid "The key was deleted successfully." +msgstr "Der Key wurde erfolgreich gelöscht." + +#: mfa/views.py:75 +msgid "Error: You own this token so you can't delete it" +msgstr "Fehler: Da Sie diesen Token besitzen, können Sie Ihn nicht löschen" + +#: mfa/views.py:81 +msgid "class Name should include modulename.classname" +msgstr "der Klassenname sollte modulename.classname beinhalten" + +#: mfa/views.py:88 +msgid "Module does not have requested function" +msgstr "Das Modul beinhaltet die angefragte Funktion nicht" + +#: mfa/views.py:103 +msgid "You can't change this method." +msgstr "Sie können diese Methode nicht verändern" + +#: mfa/views.py:105 +msgid "Error" +msgstr "Fehler" diff --git a/mfa/middleware.py b/mfa/middleware.py index 4923416..4370393 100644 --- a/mfa/middleware.py +++ b/mfa/middleware.py @@ -1,6 +1,10 @@ import time from django.http import HttpResponseRedirect -from django.core.urlresolvers import reverse +try: + from django.urls import reverse +except: + from django.core.urlresolvers import reverse + from django.conf import settings def process(request): next_check=request.session.get('mfa',{}).get("next_check",False) diff --git a/mfa/static/mfa/css/mfa.css b/mfa/static/mfa/css/mfa.css new file mode 100644 index 0000000..cef6f81 --- /dev/null +++ b/mfa/static/mfa/css/mfa.css @@ -0,0 +1,29 @@ +.alert-pr{ + align-items: center; +} +.alert-pr>p.success,#res>p.success,.row>success{ + color:green; +} + +.panel-body>.paragraph{ + padding-left: 15px; +} +.panel-body{ + align-items: center; +} +#popUpModal{ + top: 40px; +} +#popUpModal>.modal-dialog{ + height: 80%; + width: 80%; +} +#two-factor-steps { + border: 1px solid #ccc; + border-radius: 3px; + padding: 15px; +} +#two-factor-steps>.row{ + margin: 0px; + align-items: center; +} \ No newline at end of file diff --git a/mfa/static/mfa/js/EMAIL/recheck.js b/mfa/static/mfa/js/EMAIL/recheck.js new file mode 100644 index 0000000..c3e1771 --- /dev/null +++ b/mfa/static/mfa/js/EMAIL/recheck.js @@ -0,0 +1,31 @@ +$(document).ready(function () { + // 1. If the Document is loaded, add an eventlistener to the object with the id=send-totp + let sendTotp = document.getElementById('send-totp'); + sendTotp.addEventListener('click', check_mode) + // 2. If the Django-variable "mode" == "recheck", then call send_totp() + +}); + +function check_mode() { + const mode = JSON.parse(document.getElementById('recheck-js').textContent); + if (mode === 'recheck') { + send_totp(); + } +} + +function send_totp() { + const form = $('#formLogin'); + var formData = new FormData(form); + + $.ajax({ + "url": formData.get('recheck'), method: "POST", dataType: "JSON", + data: {"csrfmiddlewaretoken": formData.get('csrf_token'), "otp": $("#otp").val()}, + success: function (data) { + if (data["recheck"]) + mfa_success_function(); + else { + mfa_failed_function(); + } + } + }) +} \ No newline at end of file diff --git a/mfa/static/mfa/js/FIDO2/add.js b/mfa/static/mfa/js/FIDO2/add.js new file mode 100644 index 0000000..925ceb6 --- /dev/null +++ b/mfa/static/mfa/js/FIDO2/add.js @@ -0,0 +1,49 @@ +function begin_reg(){ + var formData = new FormData($('#fido2_form')[0]); + fetch(formData.get('rbegin'),{}).then(function(response) { + if(response.ok) + { + return response.arrayBuffer(); + } + throw new Error('Error getting registration data!'); + }).then(CBOR.decode).then(function(options) { + //options.publicKey.attestation="direct" + options.publicKey.attestation="none" + + return navigator.credentials.create(options); + }).then(function(attestation) { + return fetch(formData.get('complete'), { + method: 'POST', + headers: {'Content-Type': 'application/cbor'}, + body: CBOR.encode({ + "attestationObject": new Uint8Array(attestation.response.attestationObject), + "clientDataJSON": new Uint8Array(attestation.response.clientDataJSON), + }) + }); + }).then(function(response) { + + var stat = response.ok ? 'successful' : 'unsuccessful'; + return response.json() + }).then(function (res) + { + if (res["status"] =='OK') + window.location.href = formData.get('redirect'); + else + $("#res").html("
Registeration Failed as " + res["message"] + ", try again or Go to Security Home
") + + + }, function(reason) { + $("#res").html("
Registeration Failed as " +reason +", try again or Go to Security Home
") + }) + } + $(document).ready(function (){ + ua=new UAParser().getResult() + if (ua.browser.name == "Safari" || ua.browser.name == "Mobile Safari") + { + $("#res").html("") + } + else + { + setTimeout(begin_reg, 500) + } + }) diff --git a/mfa/static/mfa/js/FIDO2/recheck.js b/mfa/static/mfa/js/FIDO2/recheck.js new file mode 100644 index 0000000..eaad479 --- /dev/null +++ b/mfa/static/mfa/js/FIDO2/recheck.js @@ -0,0 +1,66 @@ +function authen() +{ +const begin_url = document.getElementById('begin').value; +const complete_url = document.getElementById('u2f_login').getAttribute('action'); +const recheck_text = document.getElementById('rechecktext') +const mode = $('u2f_login').attr('name') === 'complete'?'auth':'recheck'; +fetch(begin_url, { +method: 'GET', +}).then(function(response) { +if(response.ok) return response.arrayBuffer(); +throw new Error('No credential available to authenticate!'); +}).then(CBOR.decode).then(function(options) { +console.log(options) +return navigator.credentials.get(options); +}).then(function(assertion) { +res=CBOR.encode({ + "credentialId": new Uint8Array(assertion.rawId), + "authenticatorData": new Uint8Array(assertion.response.authenticatorData), + "clientDataJSON": new Uint8Array(assertion.response.clientDataJSON), + "signature": new Uint8Array(assertion.response.signature) +}); + +return fetch(complete_url, { + +method: 'POST', +headers: {'Content-Type': 'application/cbor'}, +body:res, + +}).then(function (response) {if (response.ok) return res = response.json()}).then(function (res) { + if (res.status=="OK") + { + $("#msgdiv").addClass("alert alert-success").removeClass("alert-danger") + $("#msgdiv").html("Verified....please wait") + $("#msgdiv").html("

"+recheck_text+"

") + if (mode == "auth") { + window.location.href = res.redirect; + } else if (mode === "recheck") { + window.location.href = '/' + } + + } + else { + $("#msgdiv").addClass("alert alert-danger").removeClass("alert-success") + $("#msgdiv").html("Verification Failed as " + res.message + ", try again or Go Back") + + if(mode === "recheck"){ + mfa_failed_function(); + } + } +}) + + }) + +} +$(document).ready(function () { +if (location.protocol != 'https:') { + $("#main_paragraph").addClass("alert alert-danger") + $("#main_paragraph").html("FIDO2 must work under secure context") +} else { + ua=new UAParser().getResult() + if (ua.browser.name == "Safari" || ua.browser.name == "Mobile Safari" || ua.os.name == "iOS" || ua.os.name == "iPadOS") + $("#res").html("") +else + authen() +} +}); diff --git a/mfa/static/mfa/js/TOTP/add.js b/mfa/static/mfa/js/TOTP/add.js new file mode 100644 index 0000000..61de981 --- /dev/null +++ b/mfa/static/mfa/js/TOTP/add.js @@ -0,0 +1,58 @@ +var key = ""; +$(document).ready(function addToken() { + var start_url = document.getElementById('new_otp'); + $.ajax({ + "url": start_url.value, dataType: "JSON", + success: function (data) { + window.key = data.secret_key; + var qr = new QRious({ + element: document.getElementById('qr'), + value: data.qr + }); + $("#second_step").show() + } + }) + + // Replace Onclick + // $('showTOTP').on('click', showTOTP); + document.getElementById('show-TOTP').addEventListener('click', showTOTP); + document.getElementById('show-key').addEventListener('click', showKey); + document.getElementById('verify').addEventListener('click', verify); + +}); + +function showKey() { + $("#modal-title").html("Your Secret Key") + $("#modal-body").html("
" + window.key + "Android: Google Authenticator | Authy"
+    html += "
  • iPhone/iPad: Authy
  • " + html += "
  • Chrome: Google Authenticator | Authy
  • " + html += "" + $("#modal-body").html(html) + $('#popUpModal').modal('show') +} \ No newline at end of file diff --git a/mfa/static/mfa/js/TOTP/recheck.js b/mfa/static/mfa/js/TOTP/recheck.js new file mode 100644 index 0000000..338553a --- /dev/null +++ b/mfa/static/mfa/js/TOTP/recheck.js @@ -0,0 +1,25 @@ +$(document).ready(function () { + document.getElementById('send-totp').addEventListener('click', check_mode); +}) + +function check_mode() { + const mode = JSON.parse(document.getElementById('recheck-js').textContent); + if (mode === 'recheck') { + send_totp(); + } +} + +function send_totp() { + $.ajax({ + "url": "{% url 'totp_recheck' %}", method: "POST", dataType: "JSON", + data: {"csrfmiddlewaretoken": "{{ csrf_token }}", "otp": $("#otp").val()}, + success: function (data) { + if (data["recheck"]) + mfa_success_function(); + else { + mfa_failed_function(); + } + } + }) + +} \ No newline at end of file diff --git a/mfa/static/mfa/js/TrustedDevice/add.js b/mfa/static/mfa/js/TrustedDevice/add.js new file mode 100644 index 0000000..a40620a --- /dev/null +++ b/mfa/static/mfa/js/TrustedDevice/add.js @@ -0,0 +1,29 @@ +$(document).ready(function () { + document.getElementById('formLogin').addEventListener('submit', checkFlag); +}) + +function checkFlag() { + if ($("#agree").is(":checked")) + return true; + else + alert("Please agree to the statement first"); + return false; +} + +function checkTrusted() { + var trustedURL = document.getElementById("id_begin").value; + var secureDeviceURL = document.getElementById('id_secure').value; + $.ajax({ + url: trustedURL, + success: function (data) { + if (data == "OK") + window.location.href = secureDeviceURL; + else + setTimeout('checkTrusted()', 2000) + } + + }) + +} + +$(document).ready(checkTrusted()) diff --git a/mfa/static/mfa/js/TrustedDevice/start.js b/mfa/static/mfa/js/TrustedDevice/start.js new file mode 100644 index 0000000..bbfec49 --- /dev/null +++ b/mfa/static/mfa/js/TrustedDevice/start.js @@ -0,0 +1,55 @@ +function sendEmail() { + $("#modal-title").html("Send Link") + $("#modal-body").html("Sending Email, Please wait...."); + $("#popUpModal").modal(); + $.ajax({ + "url": $("#sendMail").attr( "td-link" ), + success:function (data) { + alert(data); + $("#popUpModal").modal('toggle') + + } + }) +} +function failedMFA() { + $("#modal-body").html("
    Failed to validate you, please try again
    ") +} +function checkMFA() { + recheck_mfa(trustDevice,failedMFA,true) +} +function trustDevice() { + + $.ajax( + { + + "url":$("#sendMail").attr( "td-trustlink" ), + success: function (data) { + if (data == "OK") + { + alert("Your are done, your device should show final confirmation") + window.location.href=$("#sendMail").attr( "td-home" ) + + } + } + } + ) +} +function getUserAgent() { + $.ajax({ + "url":$("#sendMail").attr( "td-useragent" ),success: function(data) + { + if (data == "") + setTimeout('getUserAgent()',5000) + else + { + $("#modal-title").html("Confirm Trusted Device") + $("#actionBtn").remove(); + $("#modal-footer").prepend("") + $("#modal-body").html(data) + $("#popUpModal").modal() + } + } + }) + +} +$(document).ready(getUserAgent()) \ No newline at end of file diff --git a/mfa/static/mfa/js/U2F/add.js b/mfa/static/mfa/js/U2F/add.js new file mode 100644 index 0000000..f0b605e --- /dev/null +++ b/mfa/static/mfa/js/U2F/add.js @@ -0,0 +1,18 @@ +$(document).ready(function addToken() { + const form = $('#u2f_form'); + var formData = new FormData(form); + data=JSON.parse(formData.get('token')); + u2f.register(data.appId,data.registerRequests,data.registeredKeys,function (response) { + $.ajax({ + "url":form.attr('action'),method:"POST", + data:{"csrfmiddlewaretoken":formData.get('csrf_token'),"response":JSON.stringify(response)}, + success:function (data) { + if (data == "OK") + { + alert(formData.get('success')) + window.location.href=formData.get('redirect') + } + } + }) + },5000) +}) \ No newline at end of file diff --git a/mfa/static/mfa/js/U2F/recheck.js b/mfa/static/mfa/js/U2F/recheck.js new file mode 100644 index 0000000..c820d05 --- /dev/null +++ b/mfa/static/mfa/js/U2F/recheck.js @@ -0,0 +1,57 @@ +$(document).ready(function () { + const form = $('#u2f_form'); + var formData = new FormData(form); + if (location.protocol != 'https:') + { + $("#main_paragraph").addClass("alert alert-danger") + $("#main_paragraph").html(formData.get('protocol_message')) + } + else { + + + data = JSON.parse(formData.get('token')) + console.log(data) + u2f.sign(data.appId, data.challenge, data.registeredKeys, function (response) { + console.log(response) + if (response.hasOwnProperty("errorCode") && response.errorCode != 0 ) + { + if (response.errorCode == 4) + { + alert("Invalid Security Key, this security isn't linked to your account") + } + else if (response.errorCode == 5) + { + alert("Verification Timeout, please refresh the page to try again") + } + else + { + alert("Unspecified error, please try again later or try another browser.") + } + } + else if(formData.get('mode') === 'auth') + { + $("#response").val(JSON.stringify(response)) + $("#u2f_login").submit(); + } + else if(formData.get('mode') === 'recheck') { + var recheckURL = document.getElementById('id_recheck').value; + $.ajax({ + "url":recheckURL, + method: "POST", + data: {"csrfmiddlewaretoken":formData.get('csrfmiddlewaretoken'),"response":JSON.stringify(response)}, + success:function (data) { + if (data["recheck"]) { + mfa_success_function(); + } + else { + mfa_failed_function(); + } + } + + }) + + } + + }, 5000) + } + }) \ No newline at end of file diff --git a/mfa/static/mfa/js/delete-token.js b/mfa/static/mfa/js/delete-token.js new file mode 100644 index 0000000..398655d --- /dev/null +++ b/mfa/static/mfa/js/delete-token.js @@ -0,0 +1,45 @@ +$(document).ready(function () { + const key_id = JSON.parse(document.getElementById('key_id').textContent); + const key_type = JSON.parse(document.getElementById('key_type').textContent); + let id = `toggle_${key_id}` + document.getElementById(id).addEventListener('change', function () { + toggleKey(key_id, `toggle_key?id=${key_id}`); + }); + document.getElementById('delete-key').addEventListener('change', function () { + deleteKey(key_id, String(key_type), 'delete'); + }) +}) + + +function confirmDel(id, confirm_url) { + $.ajax({ + url: confirm_url, + data: {"id": id}, + success: function (data) { + alert(data) + window.location.reload(); + } + }) +} + +function deleteKey(id, name, confirm_url) { + $("#modal-title").html("Confirm Delete") + $("#modal-body").html("Are you sure you want to delete '" + name + "'? you may lose access to your system if this your only 2FA."); + $("#actionBtn").remove() + $("#modal-footer").prepend("") + $("#popUpModal").modal() +} + +function toggleKey(id, toggle_url) { + $.ajax({ + url: toggle_url, + success: function (data) { + if (data == "Error") + $("#toggle_" + id).toggle() + + }, + error: function (data) { + $("#toggle_" + id).toggle() + } + }) +} diff --git a/mfa/static/mfa/js/mfa-check.js b/mfa/static/mfa/js/mfa-check.js new file mode 100644 index 0000000..57fb060 --- /dev/null +++ b/mfa/static/mfa/js/mfa-check.js @@ -0,0 +1,30 @@ +mfa_success_function=null; +mfa_failed_function=null; +function is_mfa() { + return $("#is_mfa").value; +} + +function recheck_mfa(success_func,fail_func,must_mfa) { + if (!must_mfa) success_func() + window.mfa_success_function=success_func; + window.mfa_failed_function=fail_func; + $.ajax({ + "url":"{% url 'mfa_recheck' %}", + success:function (data) { + if (data.hasOwnProperty("res")) { + if (data["res"]) + success_func(); + else fail_func(); + } + else + { + $("#modal-title").html("Recheck Indentity") + $("#modal-body").html(data["html"]) + $("#popUpModal").modal() + } + + + + } + }) +} \ No newline at end of file diff --git a/mfa/templates/.DS_Store b/mfa/templates/.DS_Store new file mode 100644 index 0000000..e67120d Binary files /dev/null and b/mfa/templates/.DS_Store differ diff --git a/mfa/templates/ApproveLogin/Add.html b/mfa/templates/ApproveLogin/Add.html index 566549b..9c856f9 100644 --- a/mfa/templates/ApproveLogin/Add.html +++ b/mfa/templates/ApproveLogin/Add.html @@ -1,8 +1,9 @@ +{% load i18n %} - Title + {% trans 'Title' %} diff --git a/mfa/templates/Email/Add.html b/mfa/templates/Email/Add.html index f8c66e5..40d8551 100644 --- a/mfa/templates/Email/Add.html +++ b/mfa/templates/Email/Add.html @@ -1,5 +1,7 @@ {% extends "base.html" %} +{% load i18n %} {% block head %} + {% endblock %} {% block content %}
    @@ -42,9 +44,7 @@ - - - + diff --git a/mfa/templates/Email/Auth.html b/mfa/templates/Email/Auth.html index bb58497..a092f0a 100644 --- a/mfa/templates/Email/Auth.html +++ b/mfa/templates/Email/Auth.html @@ -1,14 +1,6 @@ {% extends "mfa_auth_base.html" %} -{% block head %} - -{% endblock %} {% block content %} -
    -
    -{% include "Email/recheck.html" with mode='auth' %} - +
    +
    + {% include "Email/recheck.html" with mode='auth' %} {% endblock %} diff --git a/mfa/templates/Email/mfa_email_token_template.html b/mfa/templates/Email/mfa_email_token_template.html index 7600a0c..548a798 100644 --- a/mfa/templates/Email/mfa_email_token_template.html +++ b/mfa/templates/Email/mfa_email_token_template.html @@ -1,10 +1,12 @@ + +{% load i18n %} -Dear {{ username }},
    -Your OTP is: {{ otp }} + {% trans 'Dear' %} {{ username }},
    + {% trans 'Your OTP is' %}: {{ otp }} -Thanks + {% trans 'Thank you' %} \ No newline at end of file diff --git a/mfa/templates/Email/recheck.html b/mfa/templates/Email/recheck.html index be342f1..217cb4c 100644 --- a/mfa/templates/Email/recheck.html +++ b/mfa/templates/Email/recheck.html @@ -1,20 +1,18 @@ -
    +
    +
    +
    + {% trans 'Email One Time Password' %} +
    +
    @@ -26,23 +24,24 @@
    - {% csrf_token %} - {% if invalid %} -
    - Sorry, The provided token is not valid. -
    - {% endif %} - {% if quota %} -
    - {{ quota }} -
    - {% endif %} -
    -
    -
    -

    Enter the code sent to your email.

    -
    -
    + {% csrf_token %} + + {% if invalid %} +
    + {% trans 'Sorry, The provided token is not valid.' %} +
    + {% endif %} + {% if quota %} +
    + {{ quota }} +
    + {% endif %} +
    +
    +
    +

    {% trans 'Enter the code sent to your email.' %}

    +
    +
    @@ -53,8 +52,8 @@ -
    -
    +
    +
    diff --git a/mfa/templates/FIDO2/Add.html b/mfa/templates/FIDO2/Add.html index fc11d37..79c8121 100644 --- a/mfa/templates/FIDO2/Add.html +++ b/mfa/templates/FIDO2/Add.html @@ -1,58 +1,10 @@ {% extends "base.html" %} {% load static %} +{% load i18n %} {% block head %} - + {% endblock %} {% block content %} @@ -61,13 +13,19 @@
    - FIDO2 Security Key + {% trans 'FIDO2 Security Key' %}
    - - -
    -

    Your browser should ask you to confirm you identity.

    + + + + + + + + +
    +

    {% trans 'Your browser should ask you to confirm you identity.' %}

    diff --git a/mfa/templates/FIDO2/recheck.html b/mfa/templates/FIDO2/recheck.html index 0b128bf..c44a4dc 100644 --- a/mfa/templates/FIDO2/recheck.html +++ b/mfa/templates/FIDO2/recheck.html @@ -1,34 +1,38 @@ -{% load static %} +{% load static %} +{% load i18n %} + +
    - Security Key + {% trans 'Security Key' %}
    -
    +
    {% if mode == "auth" %} - Welcome back {% comment %}{% endcomment %} {{ request.session.base_username }}
    - Not me + {% trans 'Welcome back' %} {% comment %}{% endcomment %} {{ request.session.base_username }}
    + {% trans 'Not me' %}
    {% endif %}
    -

    please press the button on your security key to prove it is you.

    +

    {% trans 'please press the button on your security key to prove it is you.' %}

    {% if mode == "auth" %} -
    + {% elif mode == "recheck" %} - + {% endif %} {% csrf_token %} +
    @@ -38,80 +42,11 @@
    {% if request.session.mfa_methods|length > 1 %} - Select Another Method + {% trans 'Select Another Method' %} {% endif %}
    -
    +
    - - diff --git a/mfa/templates/MFA.html b/mfa/templates/MFA.html index 4940ad8..b119ded 100644 --- a/mfa/templates/MFA.html +++ b/mfa/templates/MFA.html @@ -1,46 +1,16 @@ {% extends "mfa_base.html" %} {% load static %} +{% load i18n %} {% block head %} -{{block.super}} - - - + {{ block.super }} + {{ key.id|json_script:"key_id" }} + {{ key.key_type|json_script:"key_type" }} + + + + {% endblock %} {% block content %} -{{block.super}}

    @@ -48,39 +18,39 @@
    -
    +

    - - - - - - - + + + + + + + {% for key in keys %} @@ -91,18 +61,18 @@ {% if key.key_type in HIDE_DISABLE %} - + {% else %} - + {% endif %} + {% endif %} {% empty %} - + {% endfor %}
    TypeDate AddedExpires OnDeviceLast UsedStatusDelete{% trans 'Type' %}{% trans 'Date Added' %}{% trans 'Expires On' %}{% trans 'Device' %}{% trans 'Last Used' %}{% trans 'Status' %}{% trans 'Delete' %}
    {% if key.device %}{{ key.device }}{% endif %} {{ key.last_used }}{% if key.enabled %}On{% else %} Off{% endif %}{% if key.enabled %}{% trans 'On' %}{% else %} {% trans 'Off' %}{% endif %}{% if key.key_type in HIDE_DISABLE %} ---- {% else %} -
    You didn't have any keys yet.
    {% trans "You didn't have any keys yet." %}
    diff --git a/mfa/templates/TOTP/Add.html b/mfa/templates/TOTP/Add.html index 7bd2ce6..f63cb62 100644 --- a/mfa/templates/TOTP/Add.html +++ b/mfa/templates/TOTP/Add.html @@ -1,17 +1,24 @@ - {% extends "base.html" %} {% load static %} {% block head %} - +<<<<<<< HEAD +{% load i18n %} + + + +{% endblock %} +{% block content %} +
    +
    +
    +