diff --git a/www/web/templates/two_factor/_base.html b/www/web/templates/two_factor/_base.html new file mode 100644 index 0000000000000000000000000000000000000000..c4c0b0f2a5d27acad17254fe5b5d49d17b08ba08 --- /dev/null +++ b/www/web/templates/two_factor/_base.html @@ -0,0 +1,2 @@ +{% extends "base.html" %} +{% load i18n %} diff --git a/www/web/templates/two_factor/_wizard_forms.html b/www/web/templates/two_factor/_wizard_forms.html new file mode 100644 index 0000000000000000000000000000000000000000..d4ebc122e173c0ad73ac3f3329cc488016ebaaaa --- /dev/null +++ b/www/web/templates/two_factor/_wizard_forms.html @@ -0,0 +1,5 @@ +{% load bootstrap %} +<table> + {{ wizard.management_form|bootstrap }} + {{ wizard.form|bootstrap}} +</table> \ No newline at end of file diff --git a/www/web/templates/two_factor/core/login.html b/www/web/templates/two_factor/core/login.html new file mode 100644 index 0000000000000000000000000000000000000000..736e633de8d937f555b55be16fe6125393d6d6ac --- /dev/null +++ b/www/web/templates/two_factor/core/login.html @@ -0,0 +1,54 @@ +{% extends "two_factor/_base_focus.html" %} +{% load i18n two_factor %} + +{% block content %} + <h1>{% block title %}{% trans "Login" %}{% endblock %}</h1> + + {% if wizard.steps.current == 'auth' %} + <p>{% blocktrans %}Enter your credentials.{% endblocktrans %}</p> + {% elif wizard.steps.current == 'token' %} + {% if device.method == 'call' %} + <p>{% blocktrans %}We are calling your phone right now, please enter the + digits you hear.{% endblocktrans %}</p> + {% elif device.method == 'sms' %} + <p>{% blocktrans %}We sent you a text message, please enter the tokens we + sent.{% endblocktrans %}</p> + {% else %} + <p>{% blocktrans %}Please enter the tokens generated by your token + generator.{% endblocktrans %}</p> + {% endif %} + {% elif wizard.steps.current == 'backup' %} + <p>{% 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 %} +<div style="width:600px;"> + <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> + </div> +{% endblock %} \ No newline at end of file diff --git a/www/web/templates/two_factor/profile/disable.html b/www/web/templates/two_factor/profile/disable.html new file mode 100644 index 0000000000000000000000000000000000000000..55c9ba8b799fd1f72321fbbfb1daa3b580d24873 --- /dev/null +++ b/www/web/templates/two_factor/profile/disable.html @@ -0,0 +1,14 @@ +{% extends "two_factor/_base_focus.html" %} +{% load i18n %} +{% load bootstrap %} +{% block content %} + <h1>{% block title %}{% trans "Disable Two-factor Authentication" %}{% endblock %}</h1> + <p>{% blocktrans %}You are about to disable two-factor authentication. This + compromises your account security, are you sure?{% endblocktrans %}</p> + <form method="post"> + {% csrf_token %} + <table>{{ form|bootstrap }}</table> + <button class="btn btn-danger" + type="submit">{% trans "Disable" %}</button> + </form> +{% endblock %} \ No newline at end of file diff --git a/www/web/templates/two_factor/profile/profile.html b/www/web/templates/two_factor/profile/profile.html new file mode 100644 index 0000000000000000000000000000000000000000..b5d59255409c4ae0b4a27cec6baee70bda911dad --- /dev/null +++ b/www/web/templates/two_factor/profile/profile.html @@ -0,0 +1,42 @@ +{% 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 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> + + <h3>{% trans "Disable Two-Factor Authentication" %}</h3> + <p>{% blocktrans %}However we strongly discourage you to do so, you can + also disable two-factor authentication for your account.{% endblocktrans %}</p> + <p><a class="btn btn-default" href="{% url 'two_factor:disable' %}"> + {% trans "Disable Two-Factor Authentication" %}</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 diff --git a/www/web/warpauth/templates/warpauth/profile.html b/www/web/warpauth/templates/warpauth/profile.html index 7d35d14b6c49725e3e3cfc552d4da2fc15bb06ff..5235e4acd07a307cb4524984ed5f2960bdcc4a3f 100644 --- a/www/web/warpauth/templates/warpauth/profile.html +++ b/www/web/warpauth/templates/warpauth/profile.html @@ -15,6 +15,9 @@ <li role="presentation"> <a href="#change_passwd" aria-controls="change_passwd" role="tab" data-toggle="tab">{% trans "Change Password" %}</a> </li> + <li role="presentation"> + <a href="/account/two_factor/">{% trans "Two Factor Authentication" %}</a> + </li> </ul> <div class="tab-content"> @@ -28,5 +31,6 @@ </div> <script> $('a[href="{{ selected_tab }}"]').tab('show'); + </script> {% endblock %} \ No newline at end of file diff --git a/www/web/warpauth/urls.py b/www/web/warpauth/urls.py index 1bec8cad1af8cb8bdb0f001a4e1ab77503667f19..2e38924105deaacac97628aa30395f110d255fdf 100644 --- a/www/web/warpauth/urls.py +++ b/www/web/warpauth/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url +from django.conf.urls import include, url from warpauth.views import login, reset_password, profile, register # @@ -8,10 +8,10 @@ from warpauth.views import login, reset_password, profile, register urlpatterns = [ # Authentication Pages - url(r'^login/$', login.login_view, name='index'), - url(r'^logout/$', login.logout_view, name='index'), - url(r'^register/$', register.register, name='index'), - url(r'^reset_password/$', reset_password.gen_token, name='index'), + url(r'', include('two_factor.urls', 'two_factor')), + url(r'^logout/$', login.logout_view, name='logout'), + url(r'^register/$', register.register, name='register'), + url(r'^reset_password/$', reset_password.gen_token, name='reset_password'), url(r'^reset_password/(?P<reset_hash>\w+)/$', reset_password.change_password, name='index'), diff --git a/www/web/warpauth/views/login.py b/www/web/warpauth/views/login.py index d084429cd70220f8fe3ae0192f15ba5bfa8f927c..c59977db8d57229e08abe4764ab7cb37069d8fbb 100644 --- a/www/web/warpauth/views/login.py +++ b/www/web/warpauth/views/login.py @@ -32,7 +32,7 @@ def login_view(request): return HttpResponse(render(request, 'warpauth/login.html', pages)) -@login_required(login_url='/login/', redirect_field_name=None) +@login_required(login_url='/account/login/', redirect_field_name=None) def logout_view(request): logout(request) - return redirect('/login/') + return redirect('/account/login/') diff --git a/www/web/warpauth/views/profile.py b/www/web/warpauth/views/profile.py index a7ec28d4ad0a611e2d4ad242f66a2aa642ef839f..82bc95bb3ab5a3a986dbb17269fd65477e54e925 100644 --- a/www/web/warpauth/views/profile.py +++ b/www/web/warpauth/views/profile.py @@ -26,7 +26,7 @@ def clear_error_messages(): pages["success_passwd"] = False pages["selected_tab"]="" -@login_required(login_url='/login/', redirect_field_name=None) +@login_required(login_url='/account/login/', redirect_field_name=None) def index(request): clear_error_messages() pages['ldap_groups'] = request.user.ldap_user.group_names @@ -34,7 +34,7 @@ def index(request): return HttpResponse(render(request, 'warpauth/profile.html', pages)) -@login_required(login_url='/login/', redirect_field_name=None) +@login_required(login_url='/account/login/', redirect_field_name=None) def change_information(request): clear_error_messages() setUserObject(request) @@ -73,7 +73,7 @@ def change_information(request): return HttpResponse(render(request, 'warpauth/profile.html', pages)) -@login_required(login_url='/login/', redirect_field_name=None) +@login_required(login_url='/account/login/', redirect_field_name=None) def change_password(request): clear_error_messages() pages["selected_tab"]="#change_passwd" diff --git a/www/web/warpzone/settings.py b/www/web/warpzone/settings.py index aabdc17f8d2f9b783358cef5e58ac74c3035a718..82d9c3fdf8ef3a2c00030d07e29709aaa752a061 100644 --- a/www/web/warpzone/settings.py +++ b/www/web/warpzone/settings.py @@ -3,6 +3,7 @@ import ldap import logging import configparser from django_auth_ldap.config import LDAPSearch, GroupOfNamesType, PosixGroupType +from django.core.urlresolvers import reverse_lazy BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -41,6 +42,9 @@ SECRET_KEY = config.get('security','SECRET_KEY') # DEBUG DEBUG = config.getboolean('debug','DEBUG') +LOGIN_URL = 'two_factor:login' +LOGOUT_URL = 'logout' +LOGIN_REDIRECT_URL = '/' MEDIA_ROOT = 'templates/media/' MEDIA_URL = '/media/' @@ -57,12 +61,16 @@ INSTALLED_APPS = ( 'django.contrib.humanize', 'bootstrapform', 'bootstrap3_datetime', + 'django_otp', + 'django_otp.plugins.otp_static', + 'django_otp.plugins.otp_totp', + 'two_factor', 'warpmain', 'warpauth', 'warpfood', # WARPPAY - 'rest_framework', - 'rest_framework.authtoken', +# 'rest_framework', +# 'rest_framework.authtoken', # 'warppay' ) @@ -72,6 +80,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django_otp.middleware.OTPMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', @@ -107,7 +116,7 @@ LOCALE_PATHS = ( DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': '/opt/database/warpzone.db' + 'NAME': 'warpzone.db' }, 'ldap': { 'ENGINE': 'ldapdb.backends.ldap',