From b3843f123fbcbbd8de2e69abe6418cde2904e0fc Mon Sep 17 00:00:00 2001 From: Serghei Mihai Date: Thu, 30 Apr 2015 18:15:50 +0200 Subject: [PATCH] local accounts authentication (#7066) --- uauth/backends.py | 14 ++++++++ uauth/settings.py | 1 + uauth/static/css/style.css | 4 +++ uauth/utils.py | 7 ++-- uauth/views.py | 71 +++++++++++++++++++++++--------------- 5 files changed, 68 insertions(+), 29 deletions(-) create mode 100644 uauth/backends.py diff --git a/uauth/backends.py b/uauth/backends.py new file mode 100644 index 0000000..1084afa --- /dev/null +++ b/uauth/backends.py @@ -0,0 +1,14 @@ +from .organization.models import LocalAccount +from datetime import datetime + +class LocalAccountPasswordBackend(object): + + def authenticate(self, login, password, organization): + try: + account = LocalAccount.objects.get(username=login, active=True, + organization=organization) + if account.password == password: + if not account.expired: + return account + except LocalAccount.DoesNotExist: + return None diff --git a/uauth/settings.py b/uauth/settings.py index d3ffa8c..4ff8a09 100644 --- a/uauth/settings.py +++ b/uauth/settings.py @@ -99,6 +99,7 @@ LDAP_CONF = { AUTHENTICATION_BACKENDS = global_settings.AUTHENTICATION_BACKENDS + ( 'mellon.backends.SAMLBackend', + 'uauth.backends.LocalAccountPasswordBackend', ) MELLON_ATTRIBUTE_MAPPING = { diff --git a/uauth/static/css/style.css b/uauth/static/css/style.css index 48a304f..760ffd6 100644 --- a/uauth/static/css/style.css +++ b/uauth/static/css/style.css @@ -35,6 +35,10 @@ ul.login li, #guest-login ul li, #voucher-login ul li, .loginbox li { border-bottom: 1px solid #bbb; } +#login label { + display: block; +} + .loginbox { width: 300px; margin: 0 auto; diff --git a/uauth/utils.py b/uauth/utils.py index 29e8fef..41f8590 100644 --- a/uauth/utils.py +++ b/uauth/utils.py @@ -1,6 +1,7 @@ import os import logging import json +from uuid import uuid4 try: import ldap @@ -42,7 +43,9 @@ def get_ldap_connection(conf=settings.LDAP_CONF): return return conn -def create_radius_user(username, password, **kwargs): +def create_radius_user(**kwargs): + username = uuid4().get_hex() + password = uuid4().get_hex() connection = get_ldap_connection() if connection: attrs = {'objectClass': ['radiusprofile', 'radiusObjectProfile'], @@ -54,6 +57,6 @@ def create_radius_user(username, password, **kwargs): dn = 'uid=%s,%s' % (username, settings.LDAP_CONF['dn']) logger.debug('creating new radius user: %s' % dn) connection.add_s(dn, ldif) - return True + return username, password else: return False diff --git a/uauth/views.py b/uauth/views.py index b222014..e9450be 100644 --- a/uauth/views.py +++ b/uauth/views.py @@ -1,17 +1,19 @@ import json -from uuid import uuid4 import requests from xml.etree import ElementTree from django.views.generic.base import TemplateView +from django.views.generic import FormView from django.views.decorators.csrf import csrf_exempt from django.shortcuts import render_to_response from django.core import signing from django.http.request import QueryDict +from django.contrib.auth import authenticate +from django.utils.translation import ugettext_lazy as _ from mellon.views import LoginView as MellonLoginView -from .organization.models import Organization +from .organization.models import Organization, LocalAccount from .forms import GuestLoginForm, VoucherLoginForm from .utils import create_radius_user, is_organization_idp, \ get_idp_list @@ -22,8 +24,29 @@ class HomeView(TemplateView): homepage = HomeView.as_view() +class LoginMixin(object): + def login(self, organization): + context = {'organization': organization} + result = create_radius_user() + if result: + username, password = result + params = QueryDict(self.request.session[organization.slug], mutable=True) + hotspot_url = organization.hotspot_url -class LoginView(MellonLoginView): + if 'login_url' in params: + hotspot_url = params.pop('login_url')[0] + + context.update({'params': params.urlencode(), + 'hotspot_url': hotspot_url, + 'data': {'username': username, + 'password': password} + }) + return render_to_response('uauth/%s_login_successful.html' % organization.hotspot_type, + context) + return render_to_response('uauth/login_failed.html', context) + + +class LoginView(LoginMixin, MellonLoginView): def authenticate(self, request, login, attributes): relayState = signing.loads(login.msgRelayState) @@ -41,44 +64,38 @@ class LoginView(MellonLoginView): eduPersonTargetedID_NameQualifier = attributes['issuer'] if is_organization_idp(eduPersonTargetedID_NameQualifier, organization): - username = uuid4().get_hex() - password = uuid4().get_hex() - context = {'organization': organization} - if create_radius_user(username, password): - params = QueryDict(self.request.session[organization.slug], mutable=True) - hotspot_url = organization.hotspot_url - - if 'login_url' in params: - hotspot_url = params.pop('login_url')[0] - - context.update({'params': params.urlencode(), - 'hotspot_url': hotspot_url, - 'data': {'username': username, - 'password': password - } - }) - return render_to_response('uauth/%s_login_successful.html' % organization.hotspot_type, - context) - return render_to_response('uauth/login_failed.html', context) + return self.login(organization) login = csrf_exempt(LoginView.as_view()) -class OrganizationPageView(TemplateView): +class OrganizationPageView(LoginMixin, FormView): + form_class = GuestLoginForm template_name = 'uauth/organization.html' def get_context_data(self, **kwargs): context = super(OrganizationPageView, self).get_context_data(**kwargs) idps = get_idp_list() - organization = Organization.objects.get(slug=kwargs['organization_slug']) + organization = Organization.objects.get(slug=self.kwargs['organization_slug']) self.request.session[organization.slug] = self.request.GET.urlencode() relay = signing.dumps({'organization': organization.slug}) context.update({'idps': idps, - 'guest_login_form': GuestLoginForm(), - 'voucher_login_form': VoucherLoginForm(), + 'guest_login_form': kwargs['form'], 'relay': relay, - 'organization': organization + 'organization': organization, + 'voucher_login_form': VoucherLoginForm() }) return context + def form_valid(self, form): + data = form.cleaned_data + organization = Organization.objects.get(slug=self.kwargs['organization_slug']) + data.update({'organization': organization}) + user = authenticate(**data) + if user: + return self.login(organization) + else: + form.add_error(None, _('Unknown or inactive user')) + return self.form_invalid(form) + organization = OrganizationPageView.as_view()