Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa0fd3172c | |||
| fcfc6904b6 | |||
| ace60d7343 | |||
| aa76553edb | |||
| 33938ac6e8 | |||
| 5c799f15e9 | |||
| f33631ab48 | |||
| 11079528cb | |||
| b4ca28479a | |||
| e1d2bfa330 | |||
| a9ac8e7709 | |||
| 97ba9eeb44 | |||
| 71a0378ef7 | |||
| 7055a7da02 | |||
| e63e315f29 | |||
| b381a43927 | |||
| a31ecff6ef | |||
| 3eab9cbd46 | |||
| fc301a5a06 | |||
| c71c6d7cab | |||
| 79e9e906dd | |||
| fe1e2abd6f | |||
| 5db1bdca81 | |||
| 1c15c74444 | |||
| bc59406713 | |||
| 3ea974f750 | |||
| 37184edfb9 | |||
| 62a240ac28 | |||
| aa1d417c08 | |||
|
|
77905b0e83 | ||
|
|
1fe11273e3 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,9 +1,5 @@
|
|||||||
example/venv
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
example/venv
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
@@ -109,6 +105,3 @@ venv.bak/
|
|||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
example/test_db
|
example/test_db
|
||||||
|
|
||||||
# OS related
|
|
||||||
.DS_Store
|
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
## 2.9.0
|
|
||||||
* Fix a typo,
|
|
||||||
|
|
||||||
Thanks to @jkirkcaldy
|
|
||||||
|
|
||||||
## 2.8.0
|
## 2.8.0
|
||||||
* Support For Django 4.0+ JSONField
|
* Support For Django 4.0+ JSONField
|
||||||
* Removed jsonfield package from requirements
|
* Removed jsonfield package from requirements
|
||||||
|
|||||||
40
README.md
40
README.md
@@ -11,7 +11,6 @@ A Django app that handles MFA, it supports TOTP, U2F, FIDO2 U2F (Web Authn), Ema
|
|||||||
[](https://anaconda.org/conda-forge/django-mfa2)
|
[](https://anaconda.org/conda-forge/django-mfa2)
|
||||||
[](https://anaconda.org/conda-forge/django-mfa2)
|
[](https://anaconda.org/conda-forge/django-mfa2)
|
||||||
|
|
||||||
|
|
||||||
Web Authencation API (WebAuthn) is state-of-the art techology that is expected to replace passwords.
|
Web Authencation API (WebAuthn) is state-of-the art techology that is expected to replace passwords.
|
||||||
|
|
||||||

|

|
||||||
@@ -197,43 +196,6 @@ function some_func() {
|
|||||||
|
|
||||||
````
|
````
|
||||||
|
|
||||||
# Testing
|
|
||||||
|
|
||||||
We use `pytest` and several pytest plugins, especially the `pytest-django` and `pytest-cov` plugins that provide Django fixtures, and test coverage analysis.
|
|
||||||
|
|
||||||
In the root folder, `pytest.ini` contains configurations for running the tests, `requirements_testing.txt` contains the python packages required for running tests, and the folder `tests` contains the actual test files.
|
|
||||||
|
|
||||||
To run the tests, install the packages in requirements and requirements_testing.txt:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install -r requirements.txt -r requirements_testing.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
then simply run pytest
|
|
||||||
```
|
|
||||||
pytest
|
|
||||||
```
|
|
||||||
|
|
||||||
to generate the coverage html pages:
|
|
||||||
|
|
||||||
```
|
|
||||||
pytest --cov=. --cov-report html -v
|
|
||||||
```
|
|
||||||
|
|
||||||
the coverage html files will be generated in the `htmlcov` folder
|
|
||||||
|
|
||||||
We use `tox` to test the package against different isolated environments. `tox.ini` contains the configurations for tox. To run the tests in the environments defined in the `tox.ini` file, make sure the python package tox is installed:
|
|
||||||
|
|
||||||
```
|
|
||||||
pip install tox
|
|
||||||
```
|
|
||||||
|
|
||||||
then run tox in the project root:
|
|
||||||
|
|
||||||
```
|
|
||||||
tox
|
|
||||||
```
|
|
||||||
|
|
||||||
# Contributors
|
# Contributors
|
||||||
* [mahmoodnasr](https://github.com/mahmoodnasr)
|
* [mahmoodnasr](https://github.com/mahmoodnasr)
|
||||||
* [d3cline](https://github.com/d3cline)
|
* [d3cline](https://github.com/d3cline)
|
||||||
@@ -245,8 +207,6 @@ tox
|
|||||||
* [ezrajrice](https://github.com/ezrajrice)
|
* [ezrajrice](https://github.com/ezrajrice)
|
||||||
* [Spitfireap](https://github.com/Spitfireap)
|
* [Spitfireap](https://github.com/Spitfireap)
|
||||||
* [peterthomassen](https://github.com/peterthomassen)
|
* [peterthomassen](https://github.com/peterthomassen)
|
||||||
* [oussjarrousse](https://github.com/oussjarrousse)
|
|
||||||
* [jkirkcaldy](https://github.com/jkirkcaldy)
|
|
||||||
|
|
||||||
|
|
||||||
# Security contact information
|
# Security contact information
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
class myAppNameConfig(AppConfig):
|
class myAppNameConfig(AppConfig):
|
||||||
name = 'mfa'
|
name = 'mfa'
|
||||||
verbose_name = 'Django MFA2'
|
verbose_name = 'A Much Better Name'
|
||||||
@@ -4,8 +4,14 @@ from . import TrustedDevice, U2F, FIDO2, totp
|
|||||||
import simplejson
|
import simplejson
|
||||||
from django.shortcuts import HttpResponse
|
from django.shortcuts import HttpResponse
|
||||||
from mfa.views import verify,goto
|
from mfa.views import verify,goto
|
||||||
|
from mfa.recovery import delTokens
|
||||||
def has_mfa(request,username):
|
def has_mfa(request,username):
|
||||||
if User_Keys.objects.filter(username=username,enabled=1).count()>0:
|
uk = User_Keys.objects.filter(username=username,enabled=1)
|
||||||
|
if uk.count()==1:
|
||||||
|
for u in uk:
|
||||||
|
if u.key_type == "RECOVERY":
|
||||||
|
delTokens(request)
|
||||||
|
elif uk.count()>0:
|
||||||
return verify(request, username)
|
return verify(request, username)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
1
mfa/static/mfa/js/bootstrap-toggle.min.js.map
Normal file
1
mfa/static/mfa/js/bootstrap-toggle.min.js.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"bootstrap-toggle.min.js","sources":["bootstrap-toggle.js"],"names":["$","Plugin","option","this","each","$this","data","options","Toggle","element","$element","extend","defaults","render","VERSION","DEFAULTS","on","off","onstyle","offstyle","size","style","width","height","prototype","attr","_onstyle","_offstyle","$toggleOn","html","addClass","$toggleOff","$toggleHandle","$toggleGroup","append","$toggle","prop","wrap","parent","Math","max","outerWidth","outerHeight","css","update","trigger","toggle","silent","removeClass","enable","removeAttr","disable","change","proxy","destroy","remove","removeData","unwrap","old","fn","bootstrapToggle","Constructor","noConflict","document","e","$checkbox","find","preventDefault","jQuery"],"mappings":"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"}
|
||||||
1
mfa/static/mfa/js/qrious.min.js.map
Normal file
1
mfa/static/mfa/js/qrious.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -61,21 +61,24 @@
|
|||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<br/>
|
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||||
<br/>
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<div class="container">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<div class="panel panel-default card">
|
|
||||||
<div class="panel-heading card-header">
|
|
||||||
<strong> Adding a New {{ method.name }}</strong>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body card-body">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="row alert alert-pr" id="res" align="center">
|
|
||||||
<p style="color: green">Your browser should ask you to confirm you identity.</p>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
|
<div class="card-header pb-0 p-3">
|
||||||
|
<h6 class="mb-0"><strong> Adding a New {{ method.name }}</strong></h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="row alert alert-pr" id="res" align="center">
|
||||||
|
<p style="color: green">Your browser should ask you to confirm you identity.</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,50 +1,44 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
<script type="application/javascript" src="{% static 'mfa/js/cbor.js' %}"></script>
|
<script type="application/javascript" src="{% static 'mfa/js/cbor.js' %}"></script>
|
||||||
<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="col-lg-8 col-md-8 col-12 mx-auto">
|
||||||
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<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">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<div class="panel panel-default card">
|
</div>
|
||||||
<div class="panel-heading card-header">
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
<strong> Security Key</strong>
|
<div class="card-header pb-0 p-3">
|
||||||
</div>
|
<h6 class="mb-0"><strong> Security Key</strong></h6>
|
||||||
<div class="panel-body card-body">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div style="padding-left: 15px" class="col-md-10 col-md-offset-1" id="main_paragraph" align="center">
|
|
||||||
{% if mode == "auth" %}
|
|
||||||
Welcome back {% comment %}<img src="{% url 'getUserImage' request.session.base_username %}" title="{{ request.session.base_username }}" style="padding: 3px;height: 50px" class="img-circle"/>{% endcomment %} {{ request.session.base_username }}<br/>
|
|
||||||
<a href="{% url 'mfa_reset_cookie' %}">Not me</a>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
<div id="res">
|
|
||||||
<p style="color: green">please press the button on your security key to prove it is you.</p>
|
|
||||||
</div>
|
|
||||||
<div id="msgdiv"></div>
|
|
||||||
{% if mode == "auth" %}
|
|
||||||
<form id="u2f_login" action="{% url 'fido2_complete_auth' %}" method="post" enctype="multipart/form-data">
|
|
||||||
{% elif mode == "recheck" %}
|
|
||||||
<form id="u2f_login" action="{% url 'fido2_recheck' %}" method="post" enctype="multipart/form-data">
|
|
||||||
{% endif %}
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="response" id="response" value=""/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 mb-3" style="padding-left: 25px">
|
<div style="padding-left: 15px" class="col-md-10 col-md-offset-1" id="main_paragraph" align="center">
|
||||||
|
{% if mode == "auth" %}
|
||||||
{% if request.session.mfa_methods|length > 1 %}
|
Welcome back {% comment %}<img src="{% url 'getUserImage' request.session.base_username %}" title="{{ request.session.base_username }}" style="padding: 3px;height: 50px" class="img-circle"/>{% endcomment %} {{ request.session.base_username }}<br/>
|
||||||
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
<a href="{% url 'mfa_reset_cookie' %}">Not me</a>
|
||||||
{% endif %}
|
<br/>
|
||||||
|
{% endif %}
|
||||||
|
<div id="res">
|
||||||
|
<p style="color: green">please press the button on your security key to prove it is you.</p>
|
||||||
|
</div>
|
||||||
|
<div id="msgdiv"></div>
|
||||||
|
{% if mode == "auth" %}
|
||||||
|
<form id="u2f_login" action="{% url 'fido2_complete_auth' %}" method="post" enctype="multipart/form-data">
|
||||||
|
{% elif mode == "recheck" %}
|
||||||
|
<form id="u2f_login" action="{% url 'fido2_recheck' %}" method="post" enctype="multipart/form-data">
|
||||||
|
{% endif %}
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="response" id="response" value=""/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{% if request.session.mfa_methods|length > 1 %}
|
||||||
|
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|||||||
@@ -40,86 +40,92 @@
|
|||||||
<script src="{% static 'mfa/js/bootstrap-toggle.min.js'%}"></script>
|
<script src="{% static 'mfa/js/bootstrap-toggle.min.js'%}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{block.super}}
|
|
||||||
<br/>
|
|
||||||
<br/>
|
<div class="col-lg-10 col-md-10 col-12 mx-auto">
|
||||||
<div class="container">
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<div class="row">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<div class="offset-5 col-2" style="text-align: center">
|
</div>
|
||||||
<div class="btn-group">
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
<button class="btn btn-success dropdown-toggle" data-toggle="dropdown" data-bs-toggle="dropdown">
|
<div class="card-body">
|
||||||
Add Method <span class="caret"></span>
|
<div class="" style="text-align: center">
|
||||||
</button>
|
<div class="btn-group">
|
||||||
<ul class="dropdown-menu">
|
<button class="btn btn-success dropdown-toggle" data-toggle="dropdown" data-bs-toggle="dropdown">
|
||||||
{% if not 'TOTP' in UNALLOWED_AUTHEN_METHODS %}
|
Add Method <span class="caret"></span>
|
||||||
<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>
|
</button>
|
||||||
{% endif %}
|
<ul class="dropdown-menu">
|
||||||
{% if not 'Email' in UNALLOWED_AUTHEN_METHODS %}
|
{% if not 'TOTP' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<li><a class="dropdown-item" href="{% url 'start_email' %}">{% if 'Email' in RENAME_METHODS.keys %}{{ RENAME_METHODS.Email }}{% else %}Email Token{% endif %}</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 'U2F' in UNALLOWED_AUTHEN_METHODS %}
|
{% if not 'Email' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<li><a class="dropdown-item" href="{% url 'start_u2f' %}">{% if 'U2F' in RENAME_METHODS.keys %}{{ RENAME_METHODS.U2F }}{% else %}Security Key{% endif %}</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 'FIDO2' in UNALLOWED_AUTHEN_METHODS %}
|
{% if not 'U2F' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<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>
|
<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 'Trusted_Devices' in UNALLOWED_AUTHEN_METHODS %}
|
{% if not 'FIDO2' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
<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>
|
<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 %}
|
||||||
</ul>
|
{% if not 'Trusted_Devices' in UNALLOWED_AUTHEN_METHODS %}
|
||||||
|
<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 %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-responsive table-striped">
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Date Added</th>
|
||||||
|
<th>Expires On</th>
|
||||||
|
<th>Device</th>
|
||||||
|
<th>Last Used</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Delete</th>
|
||||||
|
</tr>
|
||||||
|
{% if keys %}
|
||||||
|
{% for key in keys %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ key.name }}</td>
|
||||||
|
<td>{{ key.added_on }}</td>
|
||||||
|
<td>{% if key.expires %}{{ key.expires }}{% else %}N/A{% endif %}</td>
|
||||||
|
<td>{% if key.device %}{{ key.device }}{% endif %}</td>
|
||||||
|
<td>{% if key.last_used %}{{ key.last_used }}{% else %}Never{% endif %}</td>
|
||||||
|
{% if key.key_type in HIDE_DISABLE %}
|
||||||
|
<td>{% if key.enabled %}On{% else %} Off{% endif %}</td>
|
||||||
|
{% else %}
|
||||||
|
<td><input type="checkbox" id="toggle_{{ key.id }}" {% if key.enabled %}checked{% endif %} data-onstyle="success" data-offstyle="danger" onchange="toggleKey({{ key.id }})" data-toggle="toggle" class="status_chk"></td>
|
||||||
|
{% endif %}
|
||||||
|
<td>{% if key.key_type in HIDE_DISABLE %}
|
||||||
|
----
|
||||||
|
{% else %}
|
||||||
|
<a href="javascript:void(0)" onclick="deleteKey({{ key.id }},'{{ key.key_type }}')"> <span class="fa fa-trash fa-solid fa-trash-can bi bi-trash-fill"></span></a></td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% if "RECOVERY" not in UNALLOWED_AUTHEN_METHODS %}
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td>{{ recovery.name }}</td>
|
||||||
|
<td>{{ recovery.added_on }}</td>
|
||||||
|
<td>N/A</td>
|
||||||
|
<td>N/A</td>
|
||||||
|
<td>{% if recovery.last_used %}{{ recovery.last_used }}{% else %}Never{% endif %}</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>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<tr><td colspan="7" align="center">You didn't have any keys yet.</td> </tr>
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
<table class="table table-striped">
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<th>Type</th>
|
</div>
|
||||||
<th>Date Added</th>
|
|
||||||
<th>Expires On</th>
|
|
||||||
<th>Device</th>
|
|
||||||
<th>Last Used</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Delete</th>
|
|
||||||
</tr>
|
|
||||||
{% if keys %}
|
|
||||||
{% for key in keys %}
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td>{{ key.name }}</td>
|
{% include "modal.html" %}
|
||||||
<td>{{ key.added_on }}</td>
|
|
||||||
<td>{% if key.expires %}{{ key.expires }}{% else %}N/A{% endif %}</td>
|
|
||||||
<td>{% if key.device %}{{ key.device }}{% endif %}</td>
|
|
||||||
<td>{% if key.last_used %}{{ key.last_used }}{% else %}Never{% endif %}</td>
|
|
||||||
{% if key.key_type in HIDE_DISABLE %}
|
|
||||||
<td>{% if key.enabled %}On{% else %} Off{% endif %}</td>
|
|
||||||
{% else %}
|
|
||||||
<td><input type="checkbox" id="toggle_{{ key.id }}" {% if key.enabled %}checked{% endif %} data-onstyle="success" data-offstyle="danger" onchange="toggleKey({{ key.id }})" data-toggle="toggle" class="status_chk"></td>
|
|
||||||
{% endif %}
|
|
||||||
<td>{% if key.key_type in HIDE_DISABLE %}
|
|
||||||
----
|
|
||||||
{% else %}
|
|
||||||
<a href="javascript:void(0)" onclick="deleteKey({{ key.id }},'{{ key.key_type }}')"> <span class="fa fa-trash fa-solid fa-trash-can bi bi-trash-fill"></span></a></td>
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% if "RECOVERY" not in UNALLOWED_AUTHEN_METHODS %}
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td>{{ recovery.name }}</td>
|
|
||||||
<td>{{ recovery.added_on }}</td>
|
|
||||||
<td>N/A</td>
|
|
||||||
<td>N/A</td>
|
|
||||||
<td>{% if recovery.last_used %}{{ recovery.last_used }}{% else %}Never{% endif %}</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>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<tr><td colspan="7" align="center">You don't have any keys yet.</td> </tr>
|
|
||||||
{% endif %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% include "modal.html" %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
@@ -98,37 +97,27 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<br/>
|
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||||
<br/>
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<div class="container d-flex justify-content-center">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<div class="col-md-6 col-md-offset-3" id="two-factor-steps">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<h4>Recovery Codes List</h4>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tokenrow" id="tokens">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col-md-4 col-md-offset-4" style="padding-left: 0px" align="center">
|
|
||||||
|
|
||||||
<button onclick="confirmRegenerateTokens()" class="btn btn-success">Regenerate</button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6" align="right" style="padding-right: 30px">
|
|
||||||
|
|
||||||
<a href="{{redirect_html}}" class="btn btn-default btn-secondary" role="button"> {{reg_success_msg}}</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
|
<div class="card-header pb-0 p-3">
|
||||||
|
<h6 class="mb-0">
|
||||||
|
Recovery Codes List
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="tokenrow mb-4" id="tokens">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 col-md-offset-4" style="padding-left: 0px" align="center">
|
||||||
|
<button onclick="confirmRegenerateTokens()" class="btn btn-success">Regenerate</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6" align="right" style="padding-right: 30px">
|
||||||
|
<a href="{{redirect_html}}" class="btn btn-default btn-secondary" role="button"> {{reg_success_msg}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -22,64 +22,55 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<div class="row">
|
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||||
<div class="col-sm-10 col-sm-offset-1 col-xs-12 col-md-10 col-md-offset-1 col-lg-10 col-lg-offset-1">
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<div class="panel panel-default card">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<div class="panel-heading card-header">
|
</div>
|
||||||
<strong> Recovery code</strong>
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
</div>
|
<div class="card-header pb-0 p-3">
|
||||||
<div class="panel-body card-body">
|
<h6 class="mb-0">
|
||||||
|
Recovery code
|
||||||
<FORM METHOD="POST" ACTION="{% url 'recovery_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
|
</h6>
|
||||||
|
|
||||||
|
|
||||||
{% csrf_token %}
|
|
||||||
{% if invalid %}
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
Sorry, The provided code is not valid, or has already been used.
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
<div class="card-body">
|
||||||
{% if quota %}
|
<form METHOD="POST" ACTION="{% url 'recovery_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
|
||||||
<div class="alert alert-warning">
|
{% csrf_token %}
|
||||||
{{ quota }}
|
{% if invalid %}
|
||||||
</div>
|
<div class="alert alert-danger">
|
||||||
{% endif %}
|
Sorry, The provided code is not valid, or has already been used.
|
||||||
<fieldset>
|
</div>
|
||||||
<div class="row">
|
{% endif %}
|
||||||
<div class="col-sm-12 col-md-12">
|
{% if quota %}
|
||||||
<p>Enter the 11-digits on your authenticator. Or input a recovery code</p>
|
<div class="alert alert-warning">
|
||||||
</div>
|
{{ quota }}
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
<div class="row">
|
<fieldset>
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="row">
|
||||||
<div class="form-group">
|
<p>Enter the 11-digits on your authenticator. Or input a recovery code</p>
|
||||||
<div class="input-group mb-3">
|
</div>
|
||||||
<span class="input-group-addon input-group-text">
|
|
||||||
<i class="glyphicon glyphicon-lock bi bi-lock"></i>
|
<div class="row">
|
||||||
</span>
|
<div class="form-group">
|
||||||
<input class="form-control" size="11" MaxLength="11" value="" placeholder="e.g abcde-fghij" name="recovery" type="text" id="recovery" autofocus>
|
<div class="input-group input-group-static mb-3">
|
||||||
|
<span class="input-group-addon input-group-text">
|
||||||
</div>
|
<i class="glyphicon glyphicon-lock bi bi-lock"></i>
|
||||||
</div>
|
</span>
|
||||||
|
<input class="form-control" size="11" MaxLength="11" value="" placeholder="e.g abcde-fghij" name="recovery" type="text" id="recovery" autofocus>
|
||||||
<div class="form-group d-grid gap-2">
|
</div>
|
||||||
|
|
||||||
<input type="{% if mode == "auth" %}submit{% elif mode == 'recheck' %}button{% endif %}" {% if mode == "recheck" %}onclick="send_recovery()" {% endif %} class="btn btn-lg btn-success btn-block" value="Sign in">
|
<div class="form-group d-grid gap-2">
|
||||||
</div>
|
<input type="{% if mode == "auth" %}submit{% elif mode == 'recheck' %}button{% endif %}" {% if mode == "recheck" %}onclick="send_recovery()" {% endif %} class="btn btn-lg btn-success btn-block" value="Sign in">
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</div>
|
||||||
</FORM>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
<div class="row">
|
</form>
|
||||||
<div class="col-md-12 mb-3" style="padding-left: 25px">
|
<div class="row">
|
||||||
{% 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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
|
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<style>
|
<style>
|
||||||
#two-factor-steps {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
.row{
|
.row{
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
@@ -87,47 +82,59 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
<div class="container d-flex justify-content-center">
|
<div class="container-fluid px-2 px-md-4">
|
||||||
<div class="col-md-6 col-md-offset-3" id="two-factor-steps">
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<div class="row" align="center">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<h4>Adding a new {{ method.name }}</h4>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<p>Scan the image below with the two-factor authentication app on your <a href="javascript:void(0)" onclick="showTOTP()">phone/PC</a>. If you can’t use a barcode,
|
|
||||||
<a href="javascript:void(0)" onclick="showKey()">enter this text</a> instead. </p>
|
|
||||||
</div>
|
|
||||||
<div id="res">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="row" style="text-align: center">
|
|
||||||
|
|
||||||
<div align="center" style="display: none;text-align: center;align-content: center" id="second_step">
|
<div class="card mx-3 mx-md-4 mt-n6 h-100" id="two-factor-steps" >
|
||||||
|
<div class="card-header pb-0 p-3">
|
||||||
|
<h6 class="mb-0">Adding a new {{ method.name }}</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-3">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
<img id="qr"/>
|
<p>Scan the image below with the two-factor authentication app on your <a href="javascript:void(0)" onclick="showTOTP()">phone/PC</a>. If you can’t use a barcode,
|
||||||
|
<a href="javascript:void(0)" onclick="showKey()">enter this text</a> instead. </p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="res">
|
||||||
<div class="row">
|
|
||||||
|
</div>
|
||||||
<p><b>Enter the six-digit code from the application</b></p>
|
<div class="row" style="text-align: center">
|
||||||
<p style="color: #333333;font-size: 10px">After scanning the barcode image, the app will display a six-digit code that you can enter below. </p>
|
|
||||||
|
<div align="center" style="display: none;text-align: center;align-content: center" id="second_step">
|
||||||
</div>
|
|
||||||
<div class="row">
|
<img id="qr"/>
|
||||||
<div class="offset-md-4 col-md-4">
|
|
||||||
<input style="display: inline;width: 95%" maxlength="6" size="6" class="form-control" id="answer" placeholder="e.g 785481"/>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<p><b>Enter the six-digit code from the application</b></p>
|
||||||
|
<p >After scanning the barcode image, the app will display a six-digit code that you can enter below. </p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="offset-md-4 col-md-4">
|
||||||
|
<div class="input-group input-group-static">
|
||||||
|
<input style="display: inline;width: 95%" maxlength="6" size="6" class="form-control" id="answer" placeholder="e.g 785481"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" style="padding-top: 10px;">
|
||||||
|
<div class="offset-md-4 col-md-4">
|
||||||
|
<button class="btn btn-success" onclick="verify()">Enable</button>
|
||||||
|
<a href="{% url 'mfa_home' %}" class="btn btn-default btn-secondary" role="button">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="padding-top: 10px;">
|
|
||||||
<div class="col-md-4 offset-md-4" style="padding-left: 0px">
|
|
||||||
<button class="btn btn-success" onclick="verify()">Enable</button>
|
|
||||||
<a href="{% url 'mfa_home' %}" class="btn btn-default btn-secondary" role="button">Cancel</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{% extends "mfa_auth_base.html" %}
|
{% extends "mfa_auth_base.html" %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<style>
|
<!--<style>
|
||||||
.row{
|
.row{
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>-->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<br/>
|
<br/>
|
||||||
|
|||||||
@@ -1,77 +1,64 @@
|
|||||||
<script type="application/javascript">
|
{% extends "mfa_auth_base.html" %}
|
||||||
function send_totp() {
|
{% load static %}
|
||||||
$.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<div class='container'>
|
|
||||||
<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">
|
{% block content %}
|
||||||
<div class="panel panel-default card">
|
|
||||||
<div class="panel-heading card-header">
|
|
||||||
<strong> One Time Password</strong>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body card-body">
|
|
||||||
|
|
||||||
<FORM METHOD="POST" ACTION="{% url 'totp_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
|
|
||||||
|
|
||||||
|
|
||||||
{% csrf_token %}
|
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||||
{% if invalid %}
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<div class="alert alert-danger">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
Sorry, The provided token is not valid.
|
</div>
|
||||||
</div>
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
{% endif %}
|
<div class="card-header pb-0 p-3 ">
|
||||||
{% if quota %}
|
<h6 class="mb-0">One Time Password</h6>
|
||||||
<div class="alert alert-warning">
|
</div>
|
||||||
{{ quota }}
|
<div class="card-body">
|
||||||
</div>
|
<form METHOD="POST" ACTION="{% url 'totp_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
|
||||||
{% endif %}
|
{% csrf_token %}
|
||||||
<fieldset>
|
{% if invalid %}
|
||||||
<div class="row">
|
<div class="alert alert-danger">
|
||||||
<div class="col-sm-12 col-md-12">
|
Sorry, The provided token is not valid.
|
||||||
<p>Enter the 6-digits on your authenticator</p>
|
</div>
|
||||||
</div>
|
{% endif %}
|
||||||
</div>
|
{% if quota %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{{ quota }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<fieldset>
|
||||||
|
|
||||||
<div class="row">
|
<div class=" text-left">
|
||||||
<div class="col-sm-12 col-md-12">
|
<p>Enter the 6-digits on your authenticator</p>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<div class="input-group mb-3">
|
|
||||||
<span class="input-group-addon input-group-text">
|
|
||||||
<i class="glyphicon glyphicon-lock bi bi-lock"></i>
|
|
||||||
</span>
|
|
||||||
<input class="form-control" size="6" MaxLength="6" value="" placeholder="e.g 55552" name="otp" type="text" id="otp" autofocus>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group d-grid gap-2">
|
<div class="form-group">
|
||||||
|
<div class="input-group input-group-dynamic mb-3">
|
||||||
|
<span class="input-group-addon input-group-text">
|
||||||
|
<i class="glyphicon glyphicon-lock bi bi-lock"></i>
|
||||||
|
</span>
|
||||||
|
<input class="form-control" size="6" MaxLength="6" value="" placeholder="e.g 55552" name="otp" type="text" id="otp" autofocus>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group d-grid gap-2">
|
||||||
|
<input type="{% if mode == "auth" %}submit{% elif mode == 'recheck' %}button{% endif %}" {% if mode == "recheck" %}onclick="send_totp()" {% endif %} class="btn btn-lg btn-success btn-block" value="Sign in">
|
||||||
|
</div>
|
||||||
|
|
||||||
<input type="{% if mode == "auth" %}submit{% elif mode == 'recheck' %}button{% endif %}" {% if mode == "recheck" %}onclick="send_totp()" {% endif %} class="btn btn-lg btn-success btn-block" value="Sign in"> </div>
|
|
||||||
</div>
|
</fieldset>
|
||||||
</fieldset>
|
</form>
|
||||||
</FORM>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
<div class="">
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% include "modal.html" %}
|
|
||||||
|
{% include "modal.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -25,101 +25,88 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<br/>
|
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||||
<br/>
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<div class="row">
|
</div>
|
||||||
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
<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="card-header pb-0 p-3">
|
||||||
<div class="panel panel-default card">
|
<h6 class="mb-0">
|
||||||
<div class="panel-heading card-header">
|
<strong> Add Trusted Device</strong>
|
||||||
<strong> Add Trusted Device</strong>
|
</h6>
|
||||||
</div>
|
|
||||||
<div class="panel-body card-body">
|
|
||||||
{% if success %}
|
|
||||||
<div class="alert alert-warning">
|
|
||||||
Please check your PC window, to continue the process.
|
|
||||||
</div>
|
|
||||||
{% elif added %}
|
|
||||||
<div class="alert alert-success">
|
|
||||||
Your device is now trusted, please try to <a href="{% url 'login' %}"> login</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<div class="alert alert-warning">Please make sure you are not in private (incognito) mode <i class="fal fa-user-secret"></i></div>
|
|
||||||
<FORM METHOD="POST" ACTION="{% url 'add_td' %}" Id="formLogin" onSubmit="return checkFlag()" name="FrontPage_Form1">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% if invalid %}
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
{{ invalid }}
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
<div class="card-body">
|
||||||
{% if quota %}
|
{% if success %}
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
{{ quota }}
|
Please check your PC window, to continue the process.
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% elif added %}
|
||||||
<fieldset>
|
<div class="alert alert-success">
|
||||||
<div class="row">
|
Your device is now trusted, please try to <a href="{% url 'login' %}"> login</a>
|
||||||
<div class="col-sm-12 col-md-12">
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
{% else %}
|
||||||
<div class="row">
|
<div class="alert alert-warning text-white">Please make sure you are not in private (incognito) mode <i class="fa fa-user-secret"></i></div>
|
||||||
<div class="col-sm-12 col-md-12">
|
<form METHOD="POST" ACTION="{% url 'add_td' %}" Id="formLogin" onSubmit="return checkFlag()" name="FrontPage_Form1">
|
||||||
<div class="form-group">
|
{% csrf_token %}
|
||||||
<div class="input-group mb-3">
|
{% if invalid %}
|
||||||
<span class="input-group-addon input-group-text">
|
<div class="alert alert-danger">
|
||||||
<i class="glyphicon glyphicon-user bi bi-person"></i>
|
{{ invalid }}
|
||||||
</span>
|
</div>
|
||||||
<input class="form-control" id="username" size="30" MaxLength="30" placeholder="Username" name="username" value="{{ username }}" type="text" autofocus autocomplete="on">
|
{% endif %}
|
||||||
|
{% if quota %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{{ quota }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<fieldset>
|
||||||
|
|
||||||
</div>
|
<div class="row">
|
||||||
</div>
|
<div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group input-group-static mb-3">
|
||||||
<span class="input-group-addon input-group-text">
|
<span class="input-group-addon input-group-text">
|
||||||
<i class="fa fa-key"></i>
|
<i class="glyphicon glyphicon-user bi bi-person"></i>
|
||||||
</span>
|
</span>
|
||||||
<input class="form-control" placeholder="e.g GAK-Y2M" id='key' style="text-transform: uppercase" name="key" type="text" size="9" MaxLength="9" value="{{ key }}">
|
<input class="form-control" id="username" size="30" MaxLength="30" placeholder="Username" name="username" value="{{ username }}" type="text" autofocus autocomplete="on">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<span class="input-group mb-3">
|
<div class="input-group input-group-static mb-3">
|
||||||
<input id='agree' name="agree" type="checkbox"><span style="color: red"> I confirm that this device is mine and it is only used by me.</span>
|
<span class="input-group-addon input-group-text">
|
||||||
|
<i class="fa fa-key"></i>
|
||||||
</div>
|
</span>
|
||||||
{% comment %}
|
<input class="form-control" placeholder="e.g GAK-Y2M" id='key' style="text-transform: uppercase" name="key" type="text" size="9" MaxLength="9" value="{{ key }}">
|
||||||
<div class="form-group">
|
</div>
|
||||||
<div class="input-group mb-3">
|
</div>
|
||||||
<span class="input-group-addon input-group-text">
|
<div class="form-group">
|
||||||
|
<span class=" form-check ">
|
||||||
|
<input class="form-check-input" id='agree' name="agree" type="checkbox">
|
||||||
<select size="1" name="Institution_Code" style="font-size: 10pt; font-family: Calibri; height: 34px;width: 230px">
|
<span style="color: red"> I confirm that this device is mine and it is only used by me.
|
||||||
{% for ins in institutes %}
|
</span>
|
||||||
<option value="{{ ins.institution_code }}">{{ ins.alias }}</option>
|
</div>
|
||||||
{% endfor %}
|
{% comment %}
|
||||||
|
<div class="form-group">
|
||||||
</select>
|
<div class="input-group mb-3">
|
||||||
</span>
|
<span class="input-group-addon input-group-text">
|
||||||
</div>
|
<select size="1" name="Institution_Code" style="font-size: 10pt; font-family: Calibri; height: 34px;width: 230px">
|
||||||
</div>
|
{% for ins in institutes %}
|
||||||
{% endcomment %}
|
<option value="{{ ins.institution_code }}">{{ ins.alias }}</option>
|
||||||
<div class="form-group d-grid gap-2">
|
{% endfor %}
|
||||||
<input type="submit" class="btn btn-lg btn-success btn-block" value="Trust Device">
|
</select>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
{% endcomment %}
|
||||||
</form>
|
<div class="form-group d-grid gap-2 mt-2">
|
||||||
{% endif %}
|
<input type="submit" class="btn btn-lg btn-success btn-block" value="Trust Device">
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-footer card-footer">
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
</div>
|
</form>
|
||||||
</div>
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% include "modal.html" %}
|
||||||
|
{% endblock %}
|
||||||
{% endblock %}
|
|
||||||
|
|||||||
@@ -78,21 +78,22 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<br/>
|
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||||
<br/>
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<div class="container">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<div class="col-md-6 col-md-offset-3" id="two-factor-steps">
|
</div>
|
||||||
<div class="row" align="center">
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
<h4>Add Trusted Device</h4>
|
<div class="card-header pb-0 p-3">
|
||||||
</div>
|
<h6 class="mb-0">
|
||||||
|
Add Trusted Device
|
||||||
<div class="row" >
|
</h6>
|
||||||
{% if not_allowed %}
|
</div>
|
||||||
<div class="alert alert-danger">You can't add any more devices, you need to remove previously trusted devices first.</div>
|
<div class="card-body">
|
||||||
{% else %}
|
{% if not_allowed %}
|
||||||
<p style="color: green">Allow access from mobile phone and tables.</p><br/>
|
<div class="alert alert-danger">You can't add any more devices, you need to remove previously trusted devices first.</div>
|
||||||
<br/>
|
{% else %}
|
||||||
</div>
|
<p style="color: green">Allow access from mobile phone and tables.</p><br/>
|
||||||
|
<br/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h5>Steps:</h5>
|
<h5>Steps:</h5>
|
||||||
</div>
|
</div>
|
||||||
@@ -101,41 +102,34 @@
|
|||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h5>Using Camera</h5>
|
<h5>Using Camera</h5>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Using your mobile/table, open Chrome/Firefox.</li>
|
<li>Using your mobile/table, open Chrome/Firefox.</li>
|
||||||
<li>Scan the following barcode <br/>
|
<li>Scan the following barcode <br/>
|
||||||
<img id="qr"/> <br/>
|
<img id="qr"/> <br/>
|
||||||
</li>
|
</li>
|
||||||
<li>Confirm the consent and submit form.</li>
|
<li>Confirm the consent and submit form.</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h5>Manual</h5>
|
||||||
<div class="col-md-6">
|
<ol>
|
||||||
<h5>Manual</h5>
|
<li>Using your mobile/table, open Chrome/Firefox.</li>
|
||||||
<ol>
|
<li>Go to <b>{{ url }}</b> </li>
|
||||||
<li>Using your mobile/table, open Chrome/Firefox.</li>
|
<li>Enter your username & following 6 digits<br/>
|
||||||
<li>Go to <b>{{ url }}</b> </li>
|
<span style="font-size: 16px;font-weight: bold; margin-left: 50px">{{ key|slice:":3" }} - {{ key|slice:"3:" }}</span>
|
||||||
<li>Enter your username & following 6 digits<br/>
|
</li>
|
||||||
<span style="font-size: 16px;font-weight: bold; margin-left: 50px">{{ key|slice:":3" }} - {{ key|slice:"3:" }}</span>
|
<li>Confirm the consent and submit form.</li>
|
||||||
</li>
|
|
||||||
<li>Confirm the consent and submit form.</li>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8 offset-2">
|
This window will ask to confirm the device.
|
||||||
This window will ask to confirm the device.
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<br/>
|
</div>
|
||||||
<br/>
|
</div>
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
{% include 'mfa_check.html' %}
|
{% include 'mfa_check.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,16 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<style>
|
|
||||||
#two-factor-steps {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
.row{
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script src="{% static 'mfa/js/u2f-api.js' %}" type="text/javascript"></script>
|
<script src="{% static 'mfa/js/u2f-api.js' %}" type="text/javascript"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function addToken() {
|
function addToken() {
|
||||||
@@ -43,20 +34,29 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<br/>
|
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||||
<br/>
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<div class="container">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
|
</div>
|
||||||
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
|
<div class="card-body">
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<div id="two-factor-steps">
|
||||||
|
<div id="res"></div>
|
||||||
|
<div class="row" align="center">
|
||||||
|
<h4>Adding {{ method.name}}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="row" align="center">
|
||||||
|
<p style="color: green">Your secure Key should be flashing now, please press on button.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6 col-md-offset-3" id="two-factor-steps">
|
</div>
|
||||||
<div id="res"></div>
|
</div>
|
||||||
<div class="row" align="center">
|
|
||||||
<h4>Adding {{ method.name}}</h4>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
|
||||||
<p style="color: green">Your secure Key should be flashing now, please press on button.</p>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,39 +1,39 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
<div class="row">
|
< <div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||||
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<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">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<div class="panel panel-default card">
|
</div>
|
||||||
<div class="panel-heading card-header">
|
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||||
<strong> Verify your identity using {{ method.name }}</strong>
|
<div class="card-header pb-0 p-3">
|
||||||
</div>
|
<h6 class="mb-0">Verify your identity using {{ method.name }}</h6>
|
||||||
<div class="panel-body card-body">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div style="padding-left: 15px" class="col-md-10 col-md-offset-1" id="main_paragraph" align="center">
|
|
||||||
<p style="color: green">Your key should be flashing now, please press the button.</p>
|
|
||||||
{% if mode == "auth" %}
|
|
||||||
<form id="u2f_login" action="{% url 'u2f_verify' %}" method="post">
|
|
||||||
{% elif mode == "recheck" %}
|
|
||||||
<form id="u2f_login" action="{% url 'u2f_recheck' %}" method="post">
|
|
||||||
{% endif %}
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="response" id="response" value=""/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12 mb-3" style="padding-left: 15px">
|
<div id="main_paragraph" align="center">
|
||||||
|
<p style="color: green">Your key should be flashing now, please press the button.</p>
|
||||||
|
{% if mode == "auth" %}
|
||||||
|
<form id="u2f_login" action="{% url 'u2f_verify' %}" method="post">
|
||||||
|
{% elif mode == "recheck" %}
|
||||||
|
<form id="u2f_login" action="{% url 'u2f_recheck' %}" method="post">
|
||||||
|
{% endif %}
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="response" id="response" value=""/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
{% if request.session.mfa_methods|length > 1 %}
|
||||||
|
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% if request.session.mfa_methods|length > 1 %}
|
|
||||||
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<script src="{% static 'mfa/js/u2f-api.js' %}" type="text/javascript"></script>
|
<script src="{% static 'mfa/js/u2f-api.js' %}" type="text/javascript"></script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,31 @@
|
|||||||
{% extends "mfa_auth_base.html" %}
|
{% extends "mfa_auth_base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<br/>
|
|
||||||
<div class='container'>
|
|
||||||
<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 offset-2 col-8">
|
<div class="container-fluid px-2 px-md-4 mt-6">
|
||||||
<div class="panel panel-default card">
|
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||||
<div class="panel-heading card-header">
|
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||||
<strong> Select Second Verification Method</strong>
|
</div>
|
||||||
</div>
|
<div class="card mx-3 mx-md-4 mt-n6 h-100" >
|
||||||
<div class="panel-body card-body">
|
<div class="card-header pb-0 p-3">
|
||||||
<ul>
|
<h6 class="mb-0">Select Second Verification Method</h6>
|
||||||
{% for method in request.session.mfa_methods %}
|
</div>
|
||||||
|
<div class="card-body p-3">
|
||||||
<li><a href="{% url "mfa_goto" method %}">
|
<div class="panel-body card-body">
|
||||||
{% if method == "TOTP" %}{% if 'TOTP' in RENAME_METHODS %}{{ RENAME_METHODS.TOTP }}{% else %}Authenticator App{% endif %}
|
<ul>
|
||||||
{% elif method == "Email" %}{% if 'Email' in RENAME_METHODS %}{{ RENAME_METHODS.Email }}{% else %}Send OTP by Email{% endif %}
|
{% for method in request.session.mfa_methods %}
|
||||||
{% elif method == "U2F" %}{% if 'U2F' in RENAME_METHODS %}{{ RENAME_METHODS.U2F }}{% else %}Secure Key{% endif %}
|
|
||||||
{% elif method == "FIDO2" %}{% if 'FIDO2' in RENAME_METHODS %}{{ RENAME_METHODS.FIDO2 }}{% else %}FIDO2 Secure Key{% endif %}
|
<li style="list-style-type: none;"><a class="btn btn-sm btn-outline-primary" href="{% url 'mfa_goto' method %}" >
|
||||||
{% elif method == "RECOVERY" %}{% if 'RECOVERY' in RENAME_METHODS %}{{ RENAME_METHODS.RECOVERY }}{% else %}Recovery Code{% endif %}
|
{% if method == "TOTP" %}{% if 'TOTP' in RENAME_METHODS %}{{ RENAME_METHODS.TOTP }}{% else %}Authenticator App{% endif %}
|
||||||
{% endif %}
|
{% elif method == "Email" %}{% if 'Email' in RENAME_METHODS %}{{ RENAME_METHODS.Email }}{% else %}Send OTP by Email{% endif %}
|
||||||
</a> </li>
|
{% elif method == "U2F" %}{% if 'U2F' in RENAME_METHODS %}{{ RENAME_METHODS.U2F }}{% else %}Secure Key{% endif %}
|
||||||
{% endfor %}
|
{% elif method == "FIDO2" %}{% if 'FIDO2' in RENAME_METHODS %}{{ RENAME_METHODS.FIDO2 }}{% else %}FIDO2 Secure Key{% endif %}
|
||||||
</ul>
|
{% elif method == "RECOVERY" %}{% if 'RECOVERY' in RENAME_METHODS %}{{ RENAME_METHODS.RECOVERY }}{% else %}Recovery Code{% endif %}
|
||||||
</div>
|
{% endif %}
|
||||||
</div>
|
</a> </li>
|
||||||
</div>
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
3
mfa/tests.py
Normal file
3
mfa/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
109
pytest.ini
109
pytest.ini
@@ -1,109 +0,0 @@
|
|||||||
[pytest]
|
|
||||||
# Searching
|
|
||||||
python_files = test_*
|
|
||||||
python_classes = Tests*
|
|
||||||
python_functions = test_*
|
|
||||||
|
|
||||||
env_files =
|
|
||||||
.env
|
|
||||||
|
|
||||||
# do not search for tests in these folders
|
|
||||||
norecursedirs = .vscode .tox docs example img mfa venv .coverage django_mfa2.egg-info
|
|
||||||
|
|
||||||
# Add folder to PYTHONPATH
|
|
||||||
# requires pytest >= 7.0.0
|
|
||||||
pythonpath = .
|
|
||||||
|
|
||||||
|
|
||||||
# https://pytest-django.readthedocs.io/en/latest/usage.html
|
|
||||||
DJANGO_SETTINGS_MODULE = tests.settings
|
|
||||||
|
|
||||||
|
|
||||||
# do not override the debug mode (True/False) set in the django settings module
|
|
||||||
# https://pytest-django.readthedocs.io/en/latest/usage.html#additional-pytest-ini-settings
|
|
||||||
django_debug_mode = keep
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# set env variables
|
|
||||||
# https://tech.serhatteker.com/post/2020-02/test-env-vars-in-python/
|
|
||||||
# https://github.com/pytest-dev/pytest-env
|
|
||||||
; env =
|
|
||||||
; KEY=value
|
|
||||||
|
|
||||||
|
|
||||||
addopts =
|
|
||||||
# verbose
|
|
||||||
-v
|
|
||||||
# more verbosity
|
|
||||||
# -vv
|
|
||||||
# Don't show warnings
|
|
||||||
# -p no:warnings
|
|
||||||
# generates coverage report
|
|
||||||
# note that enabling pytest coverage will cause debugging pytest to fail on pycharm
|
|
||||||
# add the --no-cov to the pytest configuration on pycharm to allow for debugging pytest
|
|
||||||
--cov=./mfa
|
|
||||||
# surpress generating converage if one or more tests failed
|
|
||||||
; --no-cov-on-fail
|
|
||||||
# do not run migrations => faster test initialization
|
|
||||||
# --nomigrations
|
|
||||||
# Show hypthesis statistics whereever hypothesis was used
|
|
||||||
# ignore these tests/files when looking for tests
|
|
||||||
#--ignore=
|
|
||||||
# black
|
|
||||||
# --black
|
|
||||||
--hypothesis-show-statistics
|
|
||||||
# Add --reuse-db if you want to speed up tests by reusing the database between test runs.
|
|
||||||
#--reuse-db
|
|
||||||
|
|
||||||
|
|
||||||
# Define additional pytest markers so that using them in test will not trigger warnings
|
|
||||||
# To show the help line use: % pytest --marker
|
|
||||||
# To run pytest on a specifc marker use: pytest -m mark
|
|
||||||
# to run pytestt on several markers use quotation and logic operators as in:
|
|
||||||
# pytest -m "mark1 and mark2"
|
|
||||||
# pytest -m "mark1 or mark2"
|
|
||||||
# pytest -m "mark1 and not mark2"
|
|
||||||
markers =
|
|
||||||
API: tests of server api functions whether it is exposed as REST API or otherwise
|
|
||||||
BLACK_BOX: Black Box tests
|
|
||||||
WHITE_BOX: White Box tests
|
|
||||||
ENVIRONMENT: tests for the environment
|
|
||||||
CONFIGURATION: tests related configurations
|
|
||||||
LOGGING: tests related to logging
|
|
||||||
UNIT: Unit tests
|
|
||||||
INTEGRATION: Integration testing
|
|
||||||
UTILS: tests for utilities
|
|
||||||
FOCUS: tests under the microscope... under the spotlight... in focus
|
|
||||||
FUNC: functional teesting
|
|
||||||
REGRESSION: tests for fixed bugs
|
|
||||||
|
|
||||||
DJANGO: tests related to DJANGO
|
|
||||||
|
|
||||||
HTTP_REQUEST: tests of functions that handles HTTP REQUESTS
|
|
||||||
HTTP_GET: tests of functions that handles HTTP_GET_REQUESTS
|
|
||||||
HTTP_POST: tests of functions that handles HTTP_POST_REQUESTS
|
|
||||||
AUTH: tests related to user authentication
|
|
||||||
SQL_DB: tests related to the sql database
|
|
||||||
|
|
||||||
CLI: tests related to flask-cli
|
|
||||||
SERVER: tests for the server
|
|
||||||
|
|
||||||
API_V1: API related tests
|
|
||||||
|
|
||||||
PRIVILEGED_USER: tests for privileged users
|
|
||||||
NON_PRIVILEGED_USER: tests for non-privileged users
|
|
||||||
PERMISSIONS: tests related to permissions
|
|
||||||
|
|
||||||
ANNONYMOUS_USER: tests for non-authenticated users
|
|
||||||
AUTHENTICATED_USER: tests for authenticated users
|
|
||||||
|
|
||||||
ENDPOINTS: tests for endpoints (API nodes)
|
|
||||||
SERIALIZERS: tests for serializers
|
|
||||||
VIEWS: tests for DRF viewsets
|
|
||||||
FILTERS: tests for DRF filters
|
|
||||||
MODELS: tests for models
|
|
||||||
VALIDATORS: tests for validators
|
|
||||||
|
|
||||||
ERROR_HANDLING: tests for error handling
|
|
||||||
SECURITY: tests for security
|
|
||||||
@@ -6,5 +6,5 @@ python-u2flib-server
|
|||||||
ua-parser
|
ua-parser
|
||||||
user-agents
|
user-agents
|
||||||
python-jose
|
python-jose
|
||||||
fido2==1.1.2
|
fido2 == 1.0.0
|
||||||
jsonLookup
|
jsonLookup
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
tox
|
|
||||||
pytest>=7.0.0
|
|
||||||
pytest-xdist
|
|
||||||
pytest-cov # Test coverage
|
|
||||||
pytest-dotenv # plugin to load environment from .env file
|
|
||||||
pytest-env # plugin to allow passing environment variable to pytest environmentt
|
|
||||||
pytest-mock # plugin that provides a mocker fixture which is a thin-wrapper around the patching API provided by the mock package
|
|
||||||
hypothesis # plugin that helps in automatize generating random values instead of static values in pyttests
|
|
||||||
pytest-django # pytest support for Django
|
|
||||||
validators # a package containing several validator functions
|
|
||||||
4
setup.py
4
setup.py
@@ -14,7 +14,7 @@ setup(
|
|||||||
url = 'https://github.com/mkalioby/django-mfa2/',
|
url = 'https://github.com/mkalioby/django-mfa2/',
|
||||||
download_url='https://github.com/mkalioby/django-mfa2/',
|
download_url='https://github.com/mkalioby/django-mfa2/',
|
||||||
license='MIT',
|
license='MIT',
|
||||||
packages=find_packages(exclude=("tests",)),
|
packages=find_packages(),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'django >= 2.0',
|
'django >= 2.0',
|
||||||
'simplejson',
|
'simplejson',
|
||||||
@@ -23,7 +23,7 @@ setup(
|
|||||||
'ua-parser',
|
'ua-parser',
|
||||||
'user-agents',
|
'user-agents',
|
||||||
'python-jose',
|
'python-jose',
|
||||||
'fido2 == 1.1.2',
|
'fido2 == 1.0.0',
|
||||||
],
|
],
|
||||||
python_requires=">=3.5",
|
python_requires=">=3.5",
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
# @pytest.fixture
|
|
||||||
# def api_request(rf):
|
|
||||||
# request = rf.get('/url')
|
|
||||||
# # Modify the request object as needed (e.g., set user, add data)
|
|
||||||
# return request
|
|
||||||
|
|
||||||
# @pytest.fixture
|
|
||||||
# def create_test_model(db):
|
|
||||||
# def make_model(**kwargs):
|
|
||||||
# return MyModel.objects.create(**kwargs)
|
|
||||||
# return make_model
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def authenticated_user(client, django_user_model):
|
|
||||||
user = django_user_model.objects.create_user(username='test', password='123')
|
|
||||||
client.login(username='test', password='123')
|
|
||||||
return user
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
SECRET_KEY = 'fake-key-for-testing'
|
|
||||||
INSTALLED_APPS = [
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'mfa'
|
|
||||||
]
|
|
||||||
ROOT_URLCONF="mfa.urls"
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
'default': {
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
|
||||||
'NAME': ':memory:',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MIDDLEWARE = [
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
]
|
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [
|
|
||||||
os.path.join(BASE_DIR ,'mfa','templates' ),
|
|
||||||
os.path.join(BASE_DIR ,'tests','templates' )
|
|
||||||
],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
MFA_UNALLOWED_METHODS = []
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
{% block head %}
|
|
||||||
{% endblock %}
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{% block content %}
|
|
||||||
{% endblock %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import pytest
|
|
||||||
from django.urls import reverse
|
|
||||||
|
|
||||||
@pytest.mark.VIEWS
|
|
||||||
@pytest.mark.DJANGO
|
|
||||||
@pytest.mark.ANNONYMOUS_USER
|
|
||||||
@pytest.mark.django_db
|
|
||||||
def test_index_unauthenticated(client):
|
|
||||||
url = reverse("mfa_home")
|
|
||||||
response = client.get(url)
|
|
||||||
assert response is not None
|
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.url=="/accounts/login/?next=/"
|
|
||||||
|
|
||||||
@pytest.mark.VIEWS
|
|
||||||
@pytest.mark.AUTHENTICATED_USER
|
|
||||||
@pytest.mark.django_db
|
|
||||||
def test_index_authenticated(client, authenticated_user):
|
|
||||||
url = reverse("mfa_home")
|
|
||||||
response = client.get(url)
|
|
||||||
assert response is not None
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert isinstance(response.templates, list)
|
|
||||||
assert len(response.templates) == 4
|
|
||||||
for template in response.templates:
|
|
||||||
assert template.name in ["modal.html", "base.html", "mfa_base.html", "MFA.html"]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
52
tox.ini
52
tox.ini
@@ -1,52 +0,0 @@
|
|||||||
# Tox (https://tox.readthedocs.io/) is a tool for running tests
|
|
||||||
# in multiple virtualenvs. This configuration file will run the
|
|
||||||
# test suite on all supported python versions. To use it, "pip install tox"
|
|
||||||
# and then run "tox" from this directory.
|
|
||||||
#
|
|
||||||
# See also https://tox.readthedocs.io/en/latest/config.html for more
|
|
||||||
# configuration options.
|
|
||||||
|
|
||||||
[tox]
|
|
||||||
# Choose your Python versions. They have to be available
|
|
||||||
# on the system the tests are run on.
|
|
||||||
# comma separated
|
|
||||||
envlist =
|
|
||||||
python{3.6,3.7,3.8,3.9}-django2.2
|
|
||||||
python{3.6,3.7,3.8,3.9}-django3
|
|
||||||
python{3.8,3.9,3.10,3.11}-django4
|
|
||||||
python{3.8,3.9,3.10,3.11}-django5
|
|
||||||
|
|
||||||
# Tell tox to not require a setup.py file
|
|
||||||
;skipsdist = True
|
|
||||||
|
|
||||||
isolated_build = True
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
# https://tox.wiki/en/latest/example/basic.html#using-a-different-default-pypi-url
|
|
||||||
;setenv =
|
|
||||||
; PIP_INDEX_URL = https://pypi.my-alternative-index.org
|
|
||||||
|
|
||||||
# https://tech.serhatteker.com/post/2020-02/test-env-vars-in-python/
|
|
||||||
;setenv =
|
|
||||||
; NAME=value
|
|
||||||
|
|
||||||
# https://tox.wiki/en/latest/example/basic.html#passing-down-environment-variables
|
|
||||||
# passenv = ENV_VAR_NAME
|
|
||||||
|
|
||||||
# https://tox.wiki/en/latest/example/pytest.html#extended-example-change-dir-before-test-and-use-per-virtualenv-tempdir
|
|
||||||
;changedir = tests
|
|
||||||
|
|
||||||
deps =
|
|
||||||
django22: django>=2.2,<2.3
|
|
||||||
django32: django>=3.2,<3.3
|
|
||||||
django40: django>=4.0,<4.1
|
|
||||||
django41: django>=4.1,<4.2
|
|
||||||
django42: django>=4.2,<4.3
|
|
||||||
django50: django==5.0
|
|
||||||
-rrequirements.txt
|
|
||||||
-rrequirements_testing.txt
|
|
||||||
|
|
||||||
# https://tox.wiki/en/latest/example/basic.html#ignoring-a-command-exit-code
|
|
||||||
commands =
|
|
||||||
; pytest --junitxml=report.xml
|
|
||||||
pytest {posargs}
|
|
||||||
Reference in New Issue
Block a user