From cad5a8f5fad872c172f7a77f33722630202b526a Mon Sep 17 00:00:00 2001 From: Dmitry Novikov Date: Fri, 24 Aug 2018 13:57:14 +0300 Subject: [PATCH] Make auth by request meta --- accounts_app/locale/ru/LC_MESSAGES/django.po | 3 + accounts_app/templates/accounts/login.html | 35 +++++------ accounts_app/templatetags/acc_tags.py | 15 +++++ accounts_app/urls.py | 6 +- accounts_app/views.py | 66 +++++++++----------- djing/lib/auth_backends.py | 26 +++++++- djing/settings.py | 3 +- 7 files changed, 94 insertions(+), 60 deletions(-) diff --git a/accounts_app/locale/ru/LC_MESSAGES/django.po b/accounts_app/locale/ru/LC_MESSAGES/django.po index 515069c..078323b 100644 --- a/accounts_app/locale/ru/LC_MESSAGES/django.po +++ b/accounts_app/locale/ru/LC_MESSAGES/django.po @@ -296,3 +296,6 @@ msgstr "Ответственность за группы" msgid "Profile" msgstr "Учётная запись" + +msgid "Login by location" +msgstr "Войти по местоположению" diff --git a/accounts_app/templates/accounts/login.html b/accounts_app/templates/accounts/login.html index 30e94a7..da95408 100644 --- a/accounts_app/templates/accounts/login.html +++ b/accounts_app/templates/accounts/login.html @@ -1,4 +1,4 @@ -{% load i18n %} +{% load i18n %}{% load acc_tags %}{% load bootstrap3 %} @@ -28,33 +28,26 @@
{% csrf_token %} - {% if errmsg %} -
- - {{ errmsg }} -
- {% endif %} + {% include '_messages.html' %} -
- -
- - -
-
+ {# Username #} + {% bootstrap_icon 'user' as ic %} + {% bootstrap_field form.username addon_before=ic %} -
- -
- - -
-
+ {# Password #} + {% bootstrap_icon 'lock' as ic %} + {% bootstrap_field form.password addon_before=ic %}
+ {% can_login_by_location request as can_lbl %} + {% if can_lbl %} + + {% trans 'Login by location' %} + + {% endif %}
diff --git a/accounts_app/templatetags/acc_tags.py b/accounts_app/templatetags/acc_tags.py index 821835a..d91a879 100644 --- a/accounts_app/templatetags/acc_tags.py +++ b/accounts_app/templatetags/acc_tags.py @@ -1,6 +1,9 @@ +from ipaddress import ip_address, AddressValueError + from django import template from django.db.models import Model from django.apps import apps +from ip_pool.models import IpLeaseModel from six import string_types, class_types register = template.Library() @@ -16,3 +19,15 @@ def klass_name(klass): else: return 'Type not detected' return kl._meta.verbose_name + + +@register.simple_tag +def can_login_by_location(request): + try: + remote_ip = ip_address(request.META.get('REMOTE_ADDR')) + if remote_ip.version == 4: + has_leases = IpLeaseModel.objects.filter(ip=str(remote_ip)).exists() + return has_leases + except AddressValueError: + pass + return False diff --git a/accounts_app/urls.py b/accounts_app/urls.py index cb2882f..9f37d72 100644 --- a/accounts_app/urls.py +++ b/accounts_app/urls.py @@ -1,4 +1,5 @@ from django.urls import path, re_path +from django.contrib.auth.views import LogoutView from . import views @@ -6,8 +7,9 @@ app_name = 'account_app' urlpatterns = [ path('', views.AccountsListView.as_view(), name='accounts_list'), - path('login/', views.to_signin, name='login'), - path('logout/', views.SignOut.as_view(), name='logout'), + path('login/', views.CustomLoginView.as_view(), name='login'), + path('logout/', LogoutView.as_view(next_page='acc_app:login'), name='logout'), + path('login_by_location/', views.location_login, name='llogin'), path('me/', views.profile_show, name='profile'), diff --git a/accounts_app/views.py b/accounts_app/views.py index bb3bd56..5e36685 100644 --- a/accounts_app/views.py +++ b/accounts_app/views.py @@ -1,13 +1,14 @@ from django.contrib.auth.decorators import login_required -from django.contrib.auth import authenticate, login, logout +from django.contrib.auth import logout, login, authenticate +from django.contrib.auth.views import LoginView from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect -from django.urls import NoReverseMatch, reverse_lazy from django.shortcuts import render, redirect, get_object_or_404, resolve_url from django.contrib import messages +from django.urls import NoReverseMatch from django.utils.decorators import method_decorator from django.utils.translation import ugettext as _ -from django.views.generic import ListView, UpdateView, RedirectView +from django.views.generic import ListView, UpdateView from django.conf import settings from group_app.models import Group @@ -20,45 +21,36 @@ from guardian.decorators import permission_required_or_403 as permission_require from guardian.shortcuts import get_objects_for_user, assign_perm, remove_perm -class BaseAccListView(ListView): - http_method_names = ('get',) - paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) +class CustomLoginView(LoginView): + template_name = 'accounts/login.html' + + def form_invalid(self, form): + messages.error(self.request, _('Wrong login or password, please try again')) + return super().form_invalid(form) + + def get_success_url(self): + if self.request.user.is_staff: + return resolve_url('acc_app:profile') + return resolve_url('client_side:home') -def to_signin(request): +def location_login(request): nextl = request.GET.get('next') nextl = '' if nextl == 'None' or nextl is None or nextl.isspace() else nextl - try: - if request.POST: - auser = authenticate(username=request.POST.get('login'), password=request.POST.get('password')) - if auser: - login(request, auser) - if nextl == 'None' or nextl is None or nextl == '': - if request.user.is_staff: - return redirect('acc_app:profile') - - return redirect('client_side:home') - - return redirect(nextl) - - return render(request, 'accounts/login.html', { - 'next': nextl, - 'errmsg': _('Wrong login or password, please try again') - }) + auser = authenticate(request=request, byip=None) + if auser: + login(request, auser) + if nextl == 'None' or nextl is None or nextl == '': + if request.user.is_staff: + return redirect('acc_app:profile') + return redirect('client_side:home') + return redirect(nextl) return render(request, 'accounts/login.html', { 'next': nextl }) except NoReverseMatch: - return redirect('acc_app:profile') - - -class SignOut(RedirectView): - url = reverse_lazy('acc_app:login') - - def get(self, request, *args, **kwargs): - logout(request) - return super(SignOut, self).get(request, *args, **kwargs) + return redirect('client_side:home') @login_required @@ -181,7 +173,9 @@ def delete_profile(request, uid): @method_decorator((login_required, only_admins), name='dispatch') -class AccountsListView(BaseAccListView): +class AccountsListView(ListView): + http_method_names = 'get', + paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) template_name = 'accounts/acc_list.html' context_object_name = 'users' @@ -209,7 +203,9 @@ def perms(request, uid): @method_decorator(login_required, name='dispatch') -class PermissionClassListView(BaseAccListView): +class PermissionClassListView(ListView): + http_method_names = 'get', + paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) template_name = 'accounts/perms/objects_of_type.html' context_object_name = 'objects' diff --git a/djing/lib/auth_backends.py b/djing/lib/auth_backends.py index 4517b04..84c9a76 100644 --- a/djing/lib/auth_backends.py +++ b/djing/lib/auth_backends.py @@ -1,6 +1,9 @@ +from ipaddress import ip_address, AddressValueError + from django.contrib.auth.backends import ModelBackend from accounts_app.models import BaseAccount, UserProfile from abonapp.models import Abon +from ip_pool.models import IpLeaseModel class CustomAuthBackend(ModelBackend): @@ -29,5 +32,26 @@ class CustomAuthBackend(ModelBackend): else: user = Abon._default_manager.get(pk=user_id) except BaseAccount.DoesNotExist: - return None + return + return user if self.user_can_authenticate(user) else None + + +class LocationAuthBackend(ModelBackend): + def authenticate(self, request, byip, **kwargs): + try: + remote_ip = ip_address(request.META.get('REMOTE_ADDR')) + lease = IpLeaseModel.objects.filter(ip=str(remote_ip)).first() + if lease is None: + return + user = Abon.objects.get(ip_addresses=lease) + if self.user_can_authenticate(user): + return user + except (AddressValueError, Abon.DoesNotExist): + return + + def get_user(self, user_id): + try: + user = Abon.objects.get(pk=user_id) + except Abon.DoesNotExist: + return return user if self.user_can_authenticate(user) else None diff --git a/djing/settings.py b/djing/settings.py index a52adc4..2ac33ab 100644 --- a/djing/settings.py +++ b/djing/settings.py @@ -27,7 +27,8 @@ ALLOWED_HOSTS = local_settings.ALLOWED_HOSTS AUTHENTICATION_BACKENDS = ( 'djing.lib.auth_backends.CustomAuthBackend', # 'django.contrib.auth.backends.ModelBackend', # default - 'guardian.backends.ObjectPermissionBackend' + 'guardian.backends.ObjectPermissionBackend', + 'djing.lib.auth_backends.LocationAuthBackend' ) # Application definition