Compare commits

..

1 Commits

Author SHA1 Message Date
Mohamed El-Kalioby
b39fa1a99b Adding Touch ID to Django 1.8 2021-01-30 17:10:33 +03:00
22 changed files with 140 additions and 205 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1 +0,0 @@
tidelift: "pypi/django-mfa2"

View File

@@ -1,11 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

View File

@@ -1,32 +1,5 @@
# Change Log
## 2.0.5
* Fixed issue in __version__
## 2.0.4
* Fixed: Closes #30
## 2.0.3
* Fixed: __version__ to show correct version
## 2.0.2
* Added: A missing migration
thnks to @swainn
## 2.0.1
* Fixed: issue in migration between Postgres and SQLite
thnks to @swainn and @willingham
## 2.0
* Dropped support to djangp-1.8 and Python 2.7
* Added: never-cache decorator
* Fixes to Make Email Method More Robust
* Addresses several structure and style issues with TOTP and Email dialogs
* Updated to fido2 0.8.1
Thanks to @swainn
## v1.9.1
* Fixed: is_authenticated #13
* Fixed: is_anonymous #6

View File

@@ -1,14 +1,7 @@
# django-mfa2
A Django app that handles MFA, it supports TOTP, U2F, FIDO2 U2F (Web Authn), Email Tokens , and Trusted Devices
### Pip Stats
[![PyPI version](https://badge.fury.io/py/django-mfa2.svg)](https://badge.fury.io/py/django-mfa2)
[![Downloads Count](https://static.pepy.tech/personalized-badge/django-mfa2?period=total&units=international_system&left_color=black&right_color=green&left_text=Downloads)](https://pepy.tech/project/django-mfa2)
### Conda Stats
[![Conda Recipe](https://img.shields.io/badge/recipe-django--mfa2-green.svg)](https://anaconda.org/conda-forge/django-mfa2)
[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/django-mfa2.svg)](https://anaconda.org/conda-forge/django-mfa2)
[![Conda Version](https://img.shields.io/conda/vn/conda-forge/django-mfa2.svg)](https://anaconda.org/conda-forge/django-mfa2)
Web Authencation API (WebAuthn) is state-of-the art techology that is expected to replace passwords.
@@ -36,23 +29,11 @@ Depends on
* ua-parser
* user-agents
* python-jose
* fido2==0.8.1
* fido2==0.7
# Installation
1. using pip
`pip install django-mfa2`
2. Using Conda forge
`conda config --add channels conda-forge`
`conda install django-mfa2`
For more info, see the conda-forge repo (https://github.com/conda-forge/django-mfa2-feedstock)
Thanks for [swainn](https://github.com/swainn) for adding package to conda-forge
# Usage
1. `pip install django-mfa2`
1. in your settings.py add the application to your installed apps
```python
INSTALLED_APPS=(
@@ -72,7 +53,7 @@ Depends on
MFA_RECHECK_MAX=30 # Maximum in seconds
MFA_QUICKLOGIN=True # Allow quick login for returning users by provide only their 2FA
MFA_HIDE_DISABLE=('FIDO2',) # Can the user disable his key (Added in 1.2.0).
MFA_OWNED_BY_ENTERPRISE = FALSE # Who owns security keys
MFA_OWNED_BY_ENTERPRISE = FALSE # Who ownes security keys
TOKEN_ISSUER_NAME="PROJECT_NAME" #TOTP Issuer name
@@ -175,10 +156,3 @@ function some_func() {
# Contributors
* [mahmoodnasr](https://github.com/mahmoodnasr)
* [d3cline](https://github.com/d3cline)
* [swainn](https://github.com/swainn)
* [unramk](https://github.com/unramk)
* [willingham](https://github.com/willingham)
# Security contact information
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.

View File

@@ -77,10 +77,8 @@ WSGI_APPLICATION = 'example.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mfa',
'USER': 'root',
'PASSWORD': 'password',
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}

View File

@@ -2,10 +2,7 @@ from django.conf import settings
from django.core.mail import EmailMessage
def send(to,subject,body):
from_email_address = settings.EMAIL_HOST_USER
if '@' not in from_email_address:
from_email_address = settings.DEFAULT_FROM_EMAIL
From = "%s <%s>" % (settings.EMAIL_FROM, from_email_address)
From = "%s <%s>" % (settings.EMAIL_FROM, settings.EMAIL_HOST_USER)
email = EmailMessage(subject,body,From,to)
email.content_subtype = "html"
return email.send(False)

View File

@@ -1,5 +1,4 @@
from django.shortcuts import render
from django.views.decorators.cache import never_cache
from django.template.context_processors import csrf
import datetime,random
from random import randint
@@ -14,9 +13,8 @@ def sendEmail(request,username,secret):
kwargs = {key: username}
user = User.objects.get(**kwargs)
res=render(request,"mfa_email_token_template.html",{"request":request,"user":user,'otp':secret})
return send([user.email],"OTP", res.content.decode())
return send([user.email],"OTP", str(res.content))
@never_cache
def start(request):
context = csrf(request)
if request.method == "POST":
@@ -38,7 +36,6 @@ def start(request):
if sendEmail(request, request.user.username, request.session["email_secret"]):
context["sent"] = True
return render(request,"Email/Add.html", context)
@never_cache
def auth(request):
context=csrf(request)
if request.method=="POST":

View File

@@ -1,5 +1,5 @@
from fido2.client import ClientData
from fido2.server import Fido2Server, PublicKeyCredentialRpEntity
from fido2.server import Fido2Server, RelyingParty
from fido2.ctap2 import AttestationObject, AuthenticatorData
from django.template.context_processors import csrf
from django.views.decorators.csrf import csrf_exempt
@@ -24,7 +24,7 @@ def recheck(request):
def getServer():
rp = PublicKeyCredentialRpEntity(settings.FIDO_SERVER_ID, settings.FIDO_SERVER_NAME)
rp = RelyingParty(settings.FIDO_SERVER_ID, settings.FIDO_SERVER_NAME)
return Fido2Server(rp)
def begin_registeration(request):
server = getServer()

View File

@@ -1 +1 @@
__version__="2.0.5"
__version__="1.6.0"

View File

@@ -4,12 +4,6 @@ from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
def update_owned_by_enterprise(apps, schema_editor):
user_keys = apps.get_model('mfa', 'user_keys')
user_keys.objects.filter(key_type='FIDO2').update(owned_by_enterprise=getattr(settings,"MFA_OWNED_BY_ENTERPRISE",False))
class Migration(migrations.Migration):
dependencies = [
@@ -22,5 +16,5 @@ class Migration(migrations.Migration):
name='owned_by_enterprise',
field=models.NullBooleanField(default=None),
),
migrations.RunPython(update_owned_by_enterprise)
migrations.RunSQL("update mfa_user_keys set owned_by_enterprise = %s where key_type='FIDO2'"%(True if getattr(settings,"MFA_OWNED_BY_ENTERPRISE",False) else False ))
]

View File

@@ -1,18 +0,0 @@
# Generated by Django 2.0 on 2020-11-10 05:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mfa', '0009_user_keys_owned_by_enterprise'),
]
operations = [
migrations.AlterField(
model_name='user_keys',
name='key_type',
field=models.CharField(default='TOTP', max_length=25),
),
]

View File

@@ -4,7 +4,6 @@ from jose import jwt
from django.conf import settings
from jsonLookup import shasLookup
JSONField.register_lookup(shasLookup)
JSONField.register_lookup(hasLookup)
class User_Keys(models.Model):
username=models.CharField(max_length = 50)

9
mfa/static/mfa/js/ua-parser.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -4,13 +4,16 @@
{% block content %}
<br/>
<br/>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<strong> Activate Token by email</strong>
</div>
<div class="panel-body">
<FORM METHOD="POST" ACTION="{% url 'start_email' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
{% csrf_token %}
{% if invalid %}
<div class="alert alert-danger">
@@ -25,9 +28,10 @@
<fieldset>
<div class="row">
<div class="col-sm-12 col-md-12">
<p>Enter the code sent to your email.</p>
<p>Enter the 6-digits sent to your email.</p>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="form-group">
@@ -36,16 +40,16 @@
<i class="glyphicon glyphicon-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">
<input type="submit" class="btn btn-lg btn-success btn-block" value="Verify">
</div>
</div>
</div>
</fieldset>
</FORM>
</div>
</div>
</div>
{% endblock %}

View File

@@ -39,7 +39,7 @@
<fieldset>
<div class="row">
<div class="col-sm-12 col-md-12">
<p>Enter the code sent to your email.</p>
<p>Enter the 6-digits sent to your email.</p>
</div>
</div>

View File

@@ -2,6 +2,7 @@
{% load static %}
{% block head %}
<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">
function begin_reg(){
fetch('{% url 'fido2_begin_reg' %}',{}).then(function(response) {
@@ -40,7 +41,17 @@
$("#res").html("<div class='alert alert-danger'>Registeration Failed as " +reason +", <a href='javascript:void(0)' onclick='begin_reg()'> try again </a> or <a href='{% url 'mfa_home' %}'> Go to Security Home</a></div>")
})
}
$(document).ready(setTimeout(begin_reg,500))
$(document).ready(function (){
ua=new UAParser()
if (ua.getBrowser().name == "Safari")
{
$("#res").html("<button class='btn btn-primary' onclick='begin_reg()'>Start...</button>")
}
else
{
setTimeout(begin_reg, 500)
}
})
</script>
{% endblock %}
@@ -56,7 +67,7 @@
<div class="row alert alert-pr" id="res">
<p style="color: green">Your broswer should ask you to confirm you indentity.</p>
<p style="color: green">Your browser should ask you to confirm you identity.</p>
</div>
</div>

View File

@@ -1,5 +1,6 @@
{% load static %}
<script type="application/javascript" src="{% static 'mfa/js/cbor.js' %}"></script>
<script type="application/javascript" src="{% static 'mfa/js/us-parser.min.js' %}"></script>
<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">
@@ -17,7 +18,9 @@
<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">
@@ -101,6 +104,10 @@
$("#main_paragraph").addClass("alert alert-danger")
$("#main_paragraph").html("FIDO2 must work under secure context")
} else {
ua=UAParser()
if (ua.getBrowser().name == "Safari")
$("#res").html("<button class='btn btn-success' onclick='authen()'>Authenticate...</button>")
else
authen()
}
});

View File

@@ -66,7 +66,7 @@
{% endif %}
</ul>
</div>
</div>
<br/>
<br/>
<table class="table table-striped">

View File

@@ -70,7 +70,7 @@
</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 cant use a barcode,
<p>Scan the image below with the two-factor authentication app on your <a href="javascript:void(0)" onclick="showTOTP()">phone/PC</a> phone/PC. If you cant use a barcode,
<a href="javascript:void(0)" onclick="showKey()">enter this text</a> instead. </p>
</div>
@@ -93,7 +93,7 @@
<input style="display: inline;width: 95%" maxlength="6" size="6" class="form-control" id="answer" placeholder="e.g 785481"/>
</div>
<div class="row" style="padding-top: 10px;">
<div class="row">
<div class="col-md-6" style="padding-left: 0px">
<button class="btn btn-success" onclick="verify()">Enable</button>
</div>

View File

@@ -1,5 +1,4 @@
from django.shortcuts import render
from django.views.decorators.cache import never_cache
from django.http import HttpResponse
from .models import *
from django.template.context_processors import csrf
@@ -32,7 +31,6 @@ def recheck(request):
return HttpResponse(simplejson.dumps({"recheck": False}), content_type="application/json")
return render(request,"TOTP/recheck.html", context)
@never_cache
def auth(request):
context=csrf(request)
if request.method=="POST":
@@ -70,6 +68,5 @@ def verify(request):
return HttpResponse("Success")
else: return HttpResponse("Error")
@never_cache
def start(request):
return render(request,"TOTP/Add.html",{})

View File

@@ -6,5 +6,6 @@ python-u2flib-server
ua-parser
user-agents
python-jose
fido2 == 0.8.1
fido2 == 0.7
jsonLookup

View File

@@ -4,7 +4,7 @@ from setuptools import find_packages, setup
setup(
name='django-mfa2',
version='2.0.5',
version='1.10.0,
description='Allows user to add 2FA to their accounts',
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
@@ -16,7 +16,7 @@ setup(
license='MIT',
packages=find_packages(),
install_requires=[
'django >= 2.0',
'django >= 1.7',
'jsonfield',
'simplejson',
'pyotp',
@@ -24,28 +24,32 @@ setup(
'ua-parser',
'user-agents',
'python-jose',
'fido2 == 0.8.1',
'fido2 == 0.9',
'jsonLookup'
],
python_requires=">=3.5",
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
include_package_data=True,
zip_safe=False, # because we're including static files
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Django",
"Framework :: Django :: 1.7",
"Framework :: Django :: 1.8",
"Framework :: Django :: 1.9",
"Framework :: Django :: 1.10",
"Framework :: Django :: 1.11",
"Framework :: Django :: 2.0",
"Framework :: Django :: 2.1",
"Framework :: Django :: 2.2",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Topic :: Software Development :: Libraries :: Python Modules",
]
)