Skip to content
Snippets Groups Projects
Commit 107d833c authored by Valentin Groues's avatar Valentin Groues :eyes:
Browse files

2 steps authentication wit OTP

parent 0a0236b5
No related branches found
No related tags found
1 merge request!53Two factors authentication
...@@ -7,3 +7,4 @@ lxml==3.7.3 ...@@ -7,3 +7,4 @@ lxml==3.7.3
python-docx==0.8.6 python-docx==0.8.6
django-cleanup==0.4.2 django-cleanup==0.4.2
django_cron==0.5.0 django_cron==0.5.0
django-two-factor-auth==1.6.1
...@@ -14,7 +14,7 @@ import os ...@@ -14,7 +14,7 @@ import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
...@@ -31,8 +31,13 @@ INSTALLED_APPS = [ ...@@ -31,8 +31,13 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django_cleanup', 'django_cleanup',
'django_cron', 'django_cron',
'debug_toolbar', 'django_otp',
'web' 'django_otp.plugins.otp_static',
'django_otp.plugins.otp_totp',
'two_factor',
'web',
'debug_toolbar'
] ]
MIDDLEWARE = [ MIDDLEWARE = [
...@@ -42,6 +47,7 @@ MIDDLEWARE = [ ...@@ -42,6 +47,7 @@ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django_otp.middleware.OTPMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
...@@ -51,7 +57,7 @@ ROOT_URLCONF = 'smash.urls' ...@@ -51,7 +57,7 @@ ROOT_URLCONF = 'smash.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [], 'DIRS': [os.path.join(PROJECT_PATH, '../web', 'templates')],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
...@@ -112,7 +118,9 @@ STATIC_URL = '/static/' ...@@ -112,7 +118,9 @@ STATIC_URL = '/static/'
MEDIA_URL = '/media/' MEDIA_URL = '/media/'
# Used for @login_required ecosystem # Used for @login_required ecosystem
LOGIN_URL = '/login' # LOGIN_URL = '/login'
LOGOUT_URL = '/logout' LOGIN_URL = 'two_factor:login'
LOGIN_REDIRECT_URL = 'web.views.appointments'
LOGOUT_REDIRECT_URL = 'web.views.appointments'
from local_settings import * from local_settings import *
...@@ -14,16 +14,17 @@ Including another URLconf ...@@ -14,16 +14,17 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
""" """
from django.conf.urls import url, include
from django.contrib import admin
from django.conf import settings from django.conf import settings
from django.conf.urls import url, include
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib import admin
import web.urls from web import api_urls
import web.api_urls from web import urls
urlpatterns = [ urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'', include(web.urls)), url(r'', include(urls)),
url(r'^api/', include(web.api_urls)) url(r'^api/', include(api_urls)),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) url(r'', include('two_factor.urls', 'two_factor'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# coding=utf-8 # coding=utf-8
import datetime import datetime
from django.contrib.auth.models import User from django.contrib.auth.models import User, AnonymousUser
from django.db import models from django.db import models
...@@ -68,6 +68,8 @@ class Worker(models.Model): ...@@ -68,6 +68,8 @@ class Worker(models.Model):
return None return None
elif isinstance(the_user, Worker): elif isinstance(the_user, Worker):
return the_user return the_user
elif isinstance(the_user, AnonymousUser):
return None
elif the_user is not None: elif the_user is not None:
raise TypeError("Unknown class type: " + the_user.__class__.__name__) raise TypeError("Unknown class type: " + the_user.__class__.__name__)
else: else:
......
...@@ -194,9 +194,13 @@ desired effect ...@@ -194,9 +194,13 @@ desired effect
<a href="#" class="btn btn-default btn-flat"><i class="fa fa-user"></i> Profile</a> <a href="#" class="btn btn-default btn-flat"><i class="fa fa-user"></i> Profile</a>
</div>--> </div>-->
<div class="pull-right"> <div class="pull-right">
<a href="{% url 'web.views.logout' %}" class="btn btn-default btn-flat"><i <a href="{% url 'logout' %}" class="btn btn-default btn-flat"><i
class="fa fa-sign-out"></i> Sign out</a> class="fa fa-sign-out"></i> Sign out</a>
</div> </div>
<div class="pull-left">
<a href="{% url 'two_factor:profile' %}" class="btn btn-default btn-flat"><i
class="fa fa-lock"></i> Security</a>
</div>
</li> </li>
</ul> </ul>
</li> </li>
......
...@@ -50,60 +50,62 @@ ...@@ -50,60 +50,62 @@
<h5 class="login-logo-h5">(Smart Scheduling)</h5> <h5 class="login-logo-h5">(Smart Scheduling)</h5>
</div> </div>
<!-- /.login-logo --> <!-- /.login-logo -->
{% block content %}
{% if state == "logout" %}
<div class="callout callout-info">
<h4>Success!</h4>
{% if state == "logout" %} <p>You have logged out of the Scheduling System</p>
<div class="callout callout-info"> </div>
<h4>Success!</h4> {% elif state == "logout_failed" %}
<div class="callout callout-danger">
<p>You have logged out of the Scheduling System</p> <h4>Error!</h4>
</div>
{% elif state == "logout_failed" %} <p>You cannot log out, if you are not logged in!</p>
<div class="callout callout-danger"> </div>
<h4>Error!</h4> {% elif state == "login_failed" %}
<div class="callout callout-danger">
<p>You cannot log out, if you are not logged in!</p> <h4>Error!</h4>
</div>
{% elif state == "login_failed" %} <p>Username does not exist, or the password is incorrect!</p>
<div class="callout callout-danger"> </div>
<h4>Error!</h4> {% else %}
<hr/>
<p>Username does not exist, or the password is incorrect!</p>
</div>
{% else %}
<hr/>
{% endif %}
<p class="login-box-msg">Please sign in</p>
<form action="{% url 'web.views.login' %}" method="post">
{% csrf_token %}
{% if next %}
<input type="hidden" name="next" value="{{ next }}"/>
{% endif %} {% endif %}
<div class="form-group has-feedback"> <p class="login-box-msg">Please sign in</p>
<input type="text" name="username" class="form-control" placeholder="Login">
<span class="glyphicon glyphicon-envelope form-control-feedback"></span> <form action="{% url 'web.views.login' %}" method="post">
</div> {% csrf_token %}
<div class="form-group has-feedback"> {% if next %}
<input type="password" name="password" class="form-control" placeholder="Password"> <input type="hidden" name="next" value="{{ next }}"/>
<span class="glyphicon glyphicon-lock form-control-feedback"></span> {% endif %}
</div>
<div class="row"> <div class="form-group has-feedback">
<div class="col-xs-8"> <input type="text" name="username" class="form-control" placeholder="Login">
<div class="checkbox icheck"> <span class="glyphicon glyphicon-envelope form-control-feedback"></span>
<label> </div>
<input type="checkbox">&nbsp;Remember Me <div class="form-group has-feedback">
</label> <input type="password" name="password" class="form-control" placeholder="Password">
</div> <span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div> </div>
<!-- /.col --> <div class="row">
<div class="col-xs-4"> <div class="col-xs-8">
<button type="submit" class="btn btn-primary btn-block btn-flat">Sign In</button> <div class="checkbox icheck">
<label>
<input type="checkbox">&nbsp;Remember Me
</label>
</div>
</div>
<!-- /.col -->
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">Sign In</button>
</div>
<!-- /.col -->
</div> </div>
<!-- /.col --> </form>
</div> {% endblock content %}
</form>
<hr/> <hr/>
......
{% extends "_base.html" %}
{% block maincontent %}
{% block content %}
{% endblock content %}
{% endblock maincontent %}
\ No newline at end of file
{% extends "login.html" %}
{% load i18n two_factor %}
{% block content %}
{% if wizard.steps.current == 'auth' %}
<p class="login-box-msg">Please sign in</p>
{% elif wizard.steps.current == 'token' %}
{% if device.method == 'call' %}
<p class="login-box-msg">{% blocktrans %}We are calling your phone right now, please enter the
digits you hear.{% endblocktrans %}</p>
{% elif device.method == 'sms' %}
<p class="login-box-msg">{% blocktrans %}We sent you a text message, please enter the tokens we
sent.{% endblocktrans %}</p>
{% else %}
<p class="login-box-msg">{% blocktrans %}Please enter the tokens generated by your token
generator.{% endblocktrans %}</p>
{% endif %}
{% elif wizard.steps.current == 'backup' %}
<p class="login-box-msg">{% blocktrans %}Use this form for entering backup tokens for logging in.
These tokens have been generated for you to print and keep safe. Please
enter one of these backup tokens to login to your account.{% endblocktrans %}</p>
{% endif %}
<form action="" method="post">{% csrf_token %}
{% include "two_factor/_wizard_forms.html" %}
{# hidden submit button to enable [enter] key #}
<div style="margin-left: -9999px"><input type="submit" value=""/></div>
{% if other_devices %}
<p>{% trans "Or, alternatively, use one of your backup phones:" %}</p>
<p>
{% for other in other_devices %}
<button name="challenge_device" value="{{ other.persistent_id }}"
class="btn btn-default btn-block" type="submit">
{{ other|device_action }}
</button>
{% endfor %}</p>
{% endif %}
{% if backup_tokens %}
<p>{% trans "As a last resort, you can use a backup token:" %}</p>
<p>
<button name="wizard_goto_step" type="submit" value="backup"
class="btn btn-default btn-block">{% trans "Use Backup Token" %}</button>
</p>
{% endif %}
{% include "two_factor/_wizard_actions.html" %}
</form>
{% endblock %}
\ No newline at end of file
{% extends "two_factor/_base.html" %}
{% load i18n two_factor %}
{% block content %}
<h1>{% block title %}{% trans "Account Security" %}{% endblock %}</h1>
{% if default_device %}
{% if default_device_type == 'TOTPDevice' %}
<p>{% trans "Tokens will be generated by your token generator." %}</p>
{% elif default_device_type == 'PhoneDevice' %}
<p>{% blocktrans with primary=default_device|device_action %}Primary method:
{{ primary }}{% endblocktrans %}</p>
{% elif default_device_type == 'RemoteYubikeyDevice' %}
<p>{% blocktrans %}Tokens will be generated by your YubiKey.{% endblocktrans %}</p>
{% endif %}
<h2>{% trans "Backup Phone Numbers" %}</h2>
<p>{% blocktrans %}If your primary method is not available, we are able to
send backup tokens to the phone numbers listed below.{% endblocktrans %}</p>
<ul>
{% for phone in backup_phones %}
<li>
{{ phone|device_action }}
<form method="post" action="{% url 'two_factor:phone_delete' phone.id %}"
onsubmit="return confirm('Are you sure?')">
{% csrf_token %}
<button class="btn btn-xs btn-warning"
type="submit">{% trans "Unregister" %}</button>
</form>
</li>
{% endfor %}
</ul>
{% if available_phone_methods %}
<p><a href="{% url 'two_factor:phone_create' %}"
class="btn btn-info">{% trans "Add Phone Number" %}</a></p>
{% endif %}
<h2>{% trans "Backup Tokens" %}</h2>
<p>
{% blocktrans %}If you don't have any device with you, you can access
your account using backup tokens.{% endblocktrans %}
{% blocktrans count counter=backup_tokens %}
You have only one backup token remaining.
{% plural %}
You have {{ counter }} backup tokens remaining.
{% endblocktrans %}
</p>
<p><a href="{% url 'two_factor:backup_tokens' %}"
class="btn btn-info">{% trans "Show Codes" %}</a></p>
{% else %}
<p>{% blocktrans %}Two-factor authentication is not enabled for your
account. Enable two-factor authentication for enhanced account
security.{% endblocktrans %}</p>
<p><a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
{% trans "Enable Two-Factor Authentication" %}</a>
</p>
{% endif %}
{% endblock %}
\ No newline at end of file
...@@ -17,12 +17,22 @@ from django.conf import settings ...@@ -17,12 +17,22 @@ from django.conf import settings
from django.conf.urls import include from django.conf.urls import include
from django.conf.urls import url from django.conf.urls import url
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import logout
from django.views.defaults import page_not_found
from django.views.generic import TemplateView from django.views.generic import TemplateView
from web import views from web import views
urlpatterns = [ urlpatterns = [
# make sure that users cannot disable two factors authentication
url(
r'^account/two_factor/disable/$',
page_not_found,
{'exception': Exception('Not Found')}
),
#################### ####################
# APPOINTMENTS # # APPOINTMENTS #
#################### ####################
...@@ -118,7 +128,8 @@ urlpatterns = [ ...@@ -118,7 +128,8 @@ urlpatterns = [
#################### ####################
url(r'^daily_planning$', login_required(TemplateView.as_view(template_name='daily_planning.html')), name='web.views.daily_planning'), url(r'^daily_planning$', login_required(TemplateView.as_view(template_name='daily_planning.html')),
name='web.views.daily_planning'),
#################### ####################
# LANGUAGES # # LANGUAGES #
...@@ -154,8 +165,9 @@ urlpatterns = [ ...@@ -154,8 +165,9 @@ urlpatterns = [
# AUTH # # AUTH #
#################### ####################
url(r'^login$', views.auth.login, name='web.views.login'), # url(r'^login$', views.auth.login, name='web.views.login'),
url(r'^logout$', views.auth.logout, name='web.views.logout'), # url(r'^logout$', views.auth.logout, name='web.views.logout'),
url(r'^logout$', logout, name='logout'),
url(r'^$', views.index, name='web.views.index') url(r'^$', views.index, name='web.views.index')
] ]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment