diff --git a/README.rst b/README.rst index b280ef9..0d557e9 100644 --- a/README.rst +++ b/README.rst @@ -25,14 +25,14 @@ Open settings: $ vim demo/settings.py -Replace Twilio credentials in ``AUTH_SMS_BACKEND_AUTH`` to your demo account settings. +Replace Twilio credentials in ``AUTH_SMS_BACKEND_AUTH`` to your demo account settings or change SMS_FORCE to False. -Run RabbitMQ server and Celery daemon(or remove 'djcelery' from ``INSTALLED_APPS``): +Run Redis server and Celery daemon(or remove 'djcelery' from ``INSTALLED_APPS``): .. code-block:: bash - $ rabbitmq-server -detached - $ python manage.py celeryd --loglevel=info >& /dev/null & + $ redis-server >& /dev/null & + $ python manage.py celeryd --loglevel=info >& /tmp/celery.log & Run test server: @@ -45,7 +45,7 @@ Run test server: Now you can open http://127.0.0.1:8000/accounts/register/ and register new account and setup all available authentication methods. -*Note: activation link will be output on console.* +*Note: activation link will be output to console.* Screenshots diff --git a/demo/demo/settings.py b/demo/demo/settings.py index b53cf66..b06cdc5 100644 --- a/demo/demo/settings.py +++ b/demo/demo/settings.py @@ -83,7 +83,7 @@ 'django_extensions', #'debug_toolbar', 'bootstrap', - # 'djcelery', + 'djcelery', 'django_tables2', 'registration', 'south', @@ -120,7 +120,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # IMPORTANT: django-secure-auth required configs -AUTH_SMS_FORCE = False +AUTH_SMS_FORCE = True AUTH_SMS_BACKEND = 'Twilio' # You can register new account on https://www.twilio.com/ and enter diff --git a/demo/demo/templates/base.html b/demo/demo/templates/base.html index 9a0bad0..7fed9a6 100644 --- a/demo/demo/templates/base.html +++ b/demo/demo/templates/base.html @@ -95,7 +95,7 @@

{% block title %}{% endblock %}


diff --git a/demo/demo/templates/registration/activate.html b/demo/demo/templates/registration/activate.html index 2c14c02..8cb5081 100644 --- a/demo/demo/templates/registration/activate.html +++ b/demo/demo/templates/registration/activate.html @@ -4,17 +4,10 @@ {% block title %}{% trans 'Registration' %}{% endblock %} {% block content %} - -{% if account %} - -

{% trans "Account successfully activated" %}

- -

{% trans "Log in" %}

- -{% else %} - -

{% trans "Account activation failed" %}

- -{% endif %} - + {% if account %} +

{% trans "Account successfully activated" %}

+

{% trans "Log in" %}

+ {% else %} +

{% trans "Account activation failed" %}

+ {% endif %} {% endblock %} diff --git a/secureauth/auth_forms.py b/secureauth/auth_forms.py index c75467b..d5957bc 100644 --- a/secureauth/auth_forms.py +++ b/secureauth/auth_forms.py @@ -7,15 +7,7 @@ from defaults import CAPTCHA_ATTEMPT, CAPTCHA_ENABLED from captcha.fields import CaptchaField - - -AUTH_TYPES = ( - ('', '---'), - ('code', _('by code')), - ('token', _('by token')), - ('phone', _('by sms')), - ('question', _('by question')), -) +from models import AUTH_TYPES def get_available_auth_methods(user): @@ -55,12 +47,13 @@ def clean(self): if auth_type and code: from secureauth.backend import AuthBackend + backend = AuthBackend() self.user_cache = backend.auth(self.credentials, auth_type, code) if self.user_cache is None: raise forms.ValidationError(_("Please enter correct code")) elif not self.user_cache.is_active: - raise forms.ValidationError(_("This account is inactive.")) + raise forms.ValidationError(_("This account is inactive")) return self.cleaned_data def get_user(self): diff --git a/secureauth/defaults.py b/secureauth/defaults.py index 184695e..deff562 100644 --- a/secureauth/defaults.py +++ b/secureauth/defaults.py @@ -1,35 +1,38 @@ # -*- coding: utf-8 -*- +from django.utils.translation import ugettext as _ from django.conf import settings def get_settings(key, default): return getattr(settings, key, default) + SMS_FORCE = get_settings('AUTH_SMS_FORCE', False) SMS_BACKEND = get_settings('AUTH_SMS_BACKEND', 'Twilio') SMS_BACKEND_AUTH = get_settings('AUTH_SMS_BACKEND_AUTH', [ 'ACc73704107c6a5426b2157e279c485d32', 'a2949613dc22aa3df58ea813a6e0747f' ]) SMS_FROM = get_settings('AUTH_SMS_FROM', '+12242315966') -SMS_MESSAGE = get_settings('AUTH_SMS_MESSAGE', 'Your code is: %s') +SMS_MESSAGE = get_settings('AUTH_SMS_MESSAGE', _('Your code is: %s')) SMS_CODE_LEN = get_settings('AUTH_SMS_CODE_LEN', 4) SMS_AGE = get_settings('AUTH_SMS_AGE', 60) SMS_ASCII = get_settings('AUTH_SMS_ASCII', False) CODE_RANGES = get_settings('AUTH_CODE_RANGES', 20) CODE_LEN = get_settings('AUTH_CODE_LEN', 6) +TOTP_NAME = get_settings('AUTH_TOTP_NAME', "%(username)s@%(domain)s") # Available: code, token, phone, question DEFAULT_AUTH_TYPE = get_settings('AUTH_DEFAULT_TYPE', 'phone') # Notification when user is authenticated on site SMS_NOTIFICATION_SUBJECT = get_settings( - 'AUTH_SMS_NOTIFICATION_SUBJECT', 'Auth activity') + 'AUTH_SMS_NOTIFICATION_SUBJECT', _('Auth activity')) CODES_SUBJECT = get_settings( - 'AUTH_CODES_SUBJECT', 'Your security codes') + 'AUTH_CODES_SUBJECT', _('Your security codes')) SMS_NOTIFICATION_MESSAGE = get_settings( 'AUTH_SMS_NOTIFICATION_MESSAGE', - "Authorization was made. If it's not you, then contact with us.") + _("Authorization was made. If it's not you, then contact with us.")) USE_CELERY = get_settings( 'AUTH_USE_CELERY', 'djcelery' in settings.INSTALLED_APPS) diff --git a/secureauth/forms.py b/secureauth/forms.py index 7afc7b8..2a69623 100644 --- a/secureauth/forms.py +++ b/secureauth/forms.py @@ -35,7 +35,7 @@ def __init__(self, request, model, *args, **kwargs): def clean_current_password(self): current_password = self.cleaned_data.get('current_password', '') if not self.request.user.check_password(current_password): - raise forms.ValidationError(_(u'Invalid password!')) + raise forms.ValidationError(_(u'Invalid password')) def save(self): if not self.user: @@ -101,26 +101,32 @@ class QuestionForm(BasicForm): question = forms.CharField(label=_('Question'), required=True) code = forms.CharField(label=_('code'), required=True, max_length=16) - def __init__(self, *args, **kwargs): + def __init__(self, request, *args, **kwargs): self.decrypt('code', **kwargs) self.decrypt('question', **kwargs) - super(QuestionForm, self).__init__(*args, **kwargs) + super(QuestionForm, self).__init__(request, *args, **kwargs) - if kwargs.get('initial') or (args[2] and args[2].get('code')): - self.fields['code'].widget = forms.HiddenInput() self.fields['code'].label = _('Answer') + try: + UserAuthQuestion.objects.get(user=request.user) + self.fields.pop('code') + except UserAuthQuestion.DoesNotExist: + pass + def save(self): model = super(QuestionForm, self).save() return model.set_data( self.cleaned_data.get('question'), self.cleaned_data.get('code')) -class PasswordCheckForm(forms.ModelForm): +class BaseSettingsForm(forms.ModelForm): + enabled = forms.BooleanField(label=_('Enabled'), required=False) + def __init__(self, request, *args, **kwargs): self.request = request - super(PasswordCheckForm, self).__init__(*args, **kwargs) + super(BaseSettingsForm, self).__init__(*args, **kwargs) if CHECK_PASSWORD is True: self.fields['current_password'] = forms.CharField( @@ -129,16 +135,16 @@ def __init__(self, request, *args, **kwargs): def clean_current_password(self): current_password = self.cleaned_data.get('current_password', '') if not self.request.user.check_password(current_password): - raise forms.ValidationError(_(u'Invalid password!')) + raise forms.ValidationError(_(u'Invalid password')) -class NotificationForm(PasswordCheckForm): +class NotificationForm(BaseSettingsForm): class Meta: model = UserAuthNotification exclude = ('user',) -class LoggingForm(PasswordCheckForm): +class LoggingForm(BaseSettingsForm): class Meta: model = UserAuthLogging exclude = ('user',) @@ -184,7 +190,7 @@ def get_status(model): def clean_current_password(self): current_password = self.cleaned_data.get('current_password', '') if not self._request.user.check_password(current_password): - raise forms.ValidationError(_(u'Invalid password!')) + raise forms.ValidationError(_(u'Invalid password')) def save(self): def set_status(model, key): diff --git a/secureauth/locale/ru/LC_MESSAGES/django.mo b/secureauth/locale/ru/LC_MESSAGES/django.mo index 021c6e1..381e18b 100644 Binary files a/secureauth/locale/ru/LC_MESSAGES/django.mo and b/secureauth/locale/ru/LC_MESSAGES/django.mo differ diff --git a/secureauth/locale/ru/LC_MESSAGES/django.po b/secureauth/locale/ru/LC_MESSAGES/django.po index ca81571..731e18e 100644 --- a/secureauth/locale/ru/LC_MESSAGES/django.po +++ b/secureauth/locale/ru/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-11-24 04:00+0400\n" -"PO-Revision-Date: 2013-11-24 21:43+0230\n" +"PO-Revision-Date: 2014-06-04 13:04+0230\n" "Last-Translator: \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Language: \n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Translated-Using: django-rosetta 0.7.3\n" +"X-Translated-Using: django-rosetta 0.7.4\n" msgid "Russian" msgstr "Русский" @@ -125,7 +125,7 @@ msgstr "код" msgid "Please enter correct code" msgstr "Пожалуйста, введите правильный код" -msgid "This account is inactive." +msgid "This account is inactive" msgstr "Эта учетная запись неактивна." msgid "Enabled" @@ -373,7 +373,7 @@ msgid "SMS was sent!" msgstr "Код был выслан к Вам на мобильный телефон" msgid "Default backend can not be removed" -msgstr "Нельзя удалить настройки" +msgstr "Нельзя удалить настройки по умолчанию" msgid "Auth method was disabled" msgstr "Один из методов аутентификации был отключен" @@ -395,3 +395,39 @@ msgstr "Уведомления отключены. Если это не Вы, п msgid "Answer" msgstr "Ответ" + +msgid "Current password:" +msgstr "Текущий пароль:" + +msgid "Logging settings" +msgstr "Настройка логирования" + +msgid "Confirm Method" +msgstr "Метод подтверждения" + +msgid "Invalid password" +msgstr "Неверный пароль" + +msgid "Successfully saved" +msgstr "Успешно сохранено" + +msgid "Your settings has changed" +msgstr "Настройки изменились" + +msgid "Codes were sent to the email" +msgstr "Коды отправлены на email" + +msgid "Send codes to email" +msgstr "Отправить коды на почту" + +msgid "Your code is: %s" +msgstr "Ваш код: %s" + +msgid "Auth activity" +msgstr "Код авторизации" + +msgid "Your security codes" +msgstr "Ваши коды безопасности" + +msgid "Authorization was made. If it's not you, then contact with us." +msgstr "Был произведен вход. Если это не Вы, то свяжитесь с нами." diff --git a/secureauth/migrations/0006_auto__add_field_userauthactivity_confirm_method.py b/secureauth/migrations/0006_auto__add_field_userauthactivity_confirm_method.py new file mode 100644 index 0000000..51e3785 --- /dev/null +++ b/secureauth/migrations/0006_auto__add_field_userauthactivity_confirm_method.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'UserAuthActivity.confirm_method' + db.add_column(u'secureauth_userauthactivity', 'confirm_method', + self.gf('django.db.models.fields.CharField')(max_length=10, null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'UserAuthActivity.confirm_method' + db.delete_column(u'secureauth_userauthactivity', 'confirm_method') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'secureauth.userauthactivity': { + 'Meta': {'ordering': "('-id',)", 'object_name': 'UserAuthActivity'}, + 'agent': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'confirm_method': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'geo': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '40', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'secureauth.userauthattempt': { + 'Meta': {'object_name': 'UserAuthAttempt'}, + 'attempt': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}) + }, + u'secureauth.userauthcode': { + 'Meta': {'object_name': 'UserAuthCode'}, + 'code': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_verified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'number': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + }, + u'secureauth.userauthlogging': { + 'Meta': {'object_name': 'UserAuthLogging'}, + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + }, + u'secureauth.userauthnotification': { + 'Meta': {'object_name': 'UserAuthNotification'}, + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + }, + u'secureauth.userauthphone': { + 'Meta': {'object_name': 'UserAuthPhone'}, + 'code': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_verified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + }, + u'secureauth.userauthquestion': { + 'Meta': {'object_name': 'UserAuthQuestion'}, + 'code': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_verified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'question': ('django.db.models.fields.TextField', [], {}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + }, + u'secureauth.userauthtoken': { + 'Meta': {'object_name': 'UserAuthToken'}, + 'code': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_verified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + } + } + + complete_apps = ['secureauth'] \ No newline at end of file diff --git a/secureauth/models.py b/secureauth/models.py index bfcb382..3cf409a 100644 --- a/secureauth/models.py +++ b/secureauth/models.py @@ -7,6 +7,7 @@ from django.utils.translation import ugettext as _ from django.core.exceptions import ValidationError +from django.forms.models import model_to_dict from django.contrib.sites.models import Site from django.utils.timezone import now from django.contrib import messages @@ -24,7 +25,16 @@ from secureauth.defaults import ( SMS_MESSAGE, SMS_CODE_LEN, SMS_AGE, SMS_FROM, CODE_RANGES, CODE_LEN, SMS_NOTIFICATION_MESSAGE, SMS_NOTIFICATION_SUBJECT, - LOGIN_ATTEMPT, BAN_TIME, CHECK_ATTEMPT, CODES_SUBJECT) + LOGIN_ATTEMPT, BAN_TIME, CHECK_ATTEMPT, CODES_SUBJECT, TOTP_NAME) + + +AUTH_TYPES = ( + ('', '---'), + ('code', _('by code')), + ('token', _('by token')), + ('phone', _('by sms')), + ('question', _('by question')), +) class UserAuthAbstract(models.Model): @@ -115,11 +125,9 @@ def send_codes(cls, request): class UserAuthToken(UserAuthAbstract): def get_google_url(self): - return get_google_url( - Sign().unsign(self.code), - "%s@%s" % ( - self.user.get_full_name(), Site.objects.get_current().domain) - ) + data = model_to_dict(Site.objects.get_current()) + data.update(model_to_dict(self.user)) + return get_google_url(Sign().unsign(self.code), TOTP_NAME % data) def _code_is_valid(self, code): return check_seed(Sign().unsign(self.code), int(code)) @@ -204,12 +212,15 @@ class UserAuthActivity(models.Model): date = models.DateTimeField(_('Date'), auto_now_add=True) agent = models.CharField( _('Browser'), max_length=255, null=True, blank=True) + confirm_method = models.CharField( + _('Confirm method'), max_length=10, choices=AUTH_TYPES, + null=True, blank=True) class Meta: ordering = ('-id',) @classmethod - def log_auth(cls, request): + def log_auth(cls, request, confirm_method=''): ip = get_ip(request) user_agent = request.META.get('HTTP_USER_AGENT') if user_agent is not None: @@ -220,7 +231,7 @@ def log_auth(cls, request): browser.get('name', ""), browser.get('version', "")) cls.objects.create( user=request.user, ip=get_ip(request), geo=get_geo(ip), - agent=user_agent + agent=user_agent, confirm_method=confirm_method ) @classmethod diff --git a/secureauth/registration/views.py b/secureauth/registration/views.py index 1e0e2fd..9380a10 100644 --- a/secureauth/registration/views.py +++ b/secureauth/registration/views.py @@ -8,9 +8,9 @@ from registration.backends.default import views from secureauth.forms import ActivatePhoneForm, CodeForm +from secureauth.defaults import SMS_AGE, SMS_FORCE from secureauth.models import UserAuthPhone from secureauth.utils.sign import Sign -from secureauth.defaults import SMS_AGE def _get_user(**kwargs): @@ -83,3 +83,6 @@ def activate(self, request, activation_key): def get_success_url(self, request, user): return ('registration_activation_complete_view', (), {}) + + +ActivationView = ActivationView if SMS_FORCE else views.ActivationView diff --git a/secureauth/tables.py b/secureauth/tables.py index 35c7570..a6119d5 100644 --- a/secureauth/tables.py +++ b/secureauth/tables.py @@ -9,6 +9,6 @@ class UserAuthActivityTable(tables.Table): class Meta: model = UserAuthActivity - fields = ('ip', 'geo', 'date', 'agent') + fields = ('ip', 'geo', 'date', 'agent', 'confirm_method') order_by = ('id',) per_page = ACTIVITY_PER_PAGE diff --git a/secureauth/templates/secureauth/codes_settings/was_done.html b/secureauth/templates/secureauth/codes_settings/was_done.html index c94fcca..535ffc6 100644 --- a/secureauth/templates/secureauth/codes_settings/was_done.html +++ b/secureauth/templates/secureauth/codes_settings/was_done.html @@ -6,5 +6,5 @@ {% block content %} {% trans 'Codes authentication successfully enabled.' %}
- {% trans 'Send codes to email.' %} + {% trans 'Send codes to email' %} {% endblock content %} diff --git a/secureauth/views.py b/secureauth/views.py index cdf3e2a..df685bd 100644 --- a/secureauth/views.py +++ b/secureauth/views.py @@ -137,14 +137,18 @@ def login_confirmation(request, template_name='secureauth/confirmation.html', if UserAuthLogging.is_enabled(request): UserAuthActivity.check_location(request) - UserAuthActivity.log_auth(request) + UserAuthActivity.log_auth( + request, form.cleaned_data.get('auth_type')) UserAuthNotification.notify(request) UserAuthAttempt.remove(request) return HttpResponseRedirect(data.get('redirect_to')) else: - raise Http404('ERROR') + return HttpResponseBadRequest() + elif CHECK_ATTEMPT is True: + UserAuthAttempt.clean() + UserAuthAttempt.store(request) else: form = authentication_form(data) @@ -339,9 +343,13 @@ def codes_settings(request): @login_required @never_cache def send_codes(request): - if UserAuthCode.send_codes(request): + if request.session.get('step') != 3: + raise Http404 + elif UserAuthCode.send_codes(request): messages.info(request, _('Codes were sent to the email')) UserAuthNotification.notify(request, _('Codes were sent to the email')) + if request.session.get('step'): + del request.session['step'] return redirect('codes_settings') @@ -381,6 +389,7 @@ def _settings_view(request, model_class, form_class, template): request, _('Your settings has changed'), force=True) return render(request, template, {'form': form}) + @login_required @never_cache def notify_settings(request): diff --git a/setup.py b/setup.py index a7ecb8c..4afa23b 100644 --- a/setup.py +++ b/setup.py @@ -8,10 +8,10 @@ name='django-secure-auth', version=get_version(), description='Secure authentication by TOTP, SMS, Codes & Question', - keywords='django secure auth totp sms codes question', + keywords='django secure auth protection totp sms codes question', long_description=open('README.rst').read(), classifiers=[ - 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers',