diff --git a/simple_sso/sso_client/client.py b/simple_sso/sso_client/client.py index f651305..435da6d 100644 --- a/simple_sso/sso_client/client.py +++ b/simple_sso/sso_client/client.py @@ -1,12 +1,15 @@ # -*- coding: utf-8 -*- from django.conf.urls import url -from django.contrib.auth import login +from django.contrib.auth import login, logout from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User from django.http import HttpResponseRedirect +from django.shortcuts import redirect from django.views.generic import View from itsdangerous import URLSafeTimedSerializer from webservices.sync import SyncConsumer +from django.contrib.auth import get_user_model +from django.conf import settings from ..compat import ( NoReverseMatch, @@ -17,6 +20,8 @@ urlencode, ) +User = get_user_model() + class LoginView(View): client = None @@ -29,7 +34,42 @@ def get(self, request): path = reverse('simple-sso-authenticate') redirect_to = urlunparse((scheme, netloc, path, '', query, '')) request_token = self.client.get_request_token(redirect_to) - host = urljoin(self.client.server_url, 'authorize/') + if request_token != "": + host = urljoin(self.client.server_url, 'authorize/') + url = '%s?%s' % (host, urlencode([('token', request_token)])) + return HttpResponseRedirect(url) + else: + return redirect(settings.SSO_CLIENT) + + def get_next(self): + """ + Given a request, returns the URL where a user should be redirected to + after login. Defaults to '/' + """ + next = self.request.GET.get('next', None) + if not next: + return settings.SSO_DASHBOARD_REDIRECT + netloc = urlparse(next)[1] + # Heavier security check -- don't allow redirection to a different + # host. + # Taken from django.contrib.auth.views.login + if netloc and netloc != self.request.get_host(): + return settings.SSO_DASHBOARD_REDIRECT + return next + + +class LogoutView(View): + client = None + + def get(self, request): + next = self.get_next() + scheme = 'https' if request.is_secure() else 'http' + query = urlencode([('next', next)]) + netloc = request.get_host() + path = reverse('simple-sso-logoutauth') + redirect_to = urlunparse((scheme, netloc, path, '', query, '')) + request_token = self.client.get_request_token(redirect_to) + host = urljoin(self.client.server_url, 'logout/') url = '%s?%s' % (host, urlencode([('token', request_token)])) return HttpResponseRedirect(url) @@ -40,13 +80,13 @@ def get_next(self): """ next = self.request.GET.get('next', None) if not next: - return '/' + return settings.SSO_HOME_REDIRECT netloc = urlparse(next)[1] # Heavier security check -- don't allow redirection to a different # host. # Taken from django.contrib.auth.views.login if netloc and netloc != self.request.get_host(): - return '/' + return settings.SSO_HOME_REDIRECT return next @@ -63,9 +103,20 @@ def get(self, request): return HttpResponseRedirect(next) +class LogoutAuthView(LogoutView): + client = None + + def get(self, request): + logout(request) + next = self.get_next() + return HttpResponseRedirect(next) + + class Client(object): login_view = LoginView authenticate_view = AuthenticateView + logout_auth_view = LogoutAuthView + logout_view = LogoutView backend = "%s.%s" % (ModelBackend.__module__, ModelBackend.__name__) user_extra_data = None @@ -95,7 +146,10 @@ def get_request_token(self, redirect_to): except NoReverseMatch: # thisisfine url = '/request-token/' - return self.consumer.consume(url, {'redirect_to': redirect_to})['request_token'] + try: + return self.consumer.consume(url, {'redirect_to': redirect_to}) + except: + return {"request_token": ""} def get_user(self, access_token): data = {'access_token': access_token} @@ -116,7 +170,7 @@ def build_user(self, user_data): user = User.objects.get(username=user_data['username']) except User.DoesNotExist: user = User(**user_data) - user.set_unusable_password() + # user.set_unusable_password() user.save() return user @@ -124,4 +178,6 @@ def get_urls(self): return [ url(r'^$', self.login_view.as_view(client=self), name='simple-sso-login'), url(r'^authenticate/$', self.authenticate_view.as_view(client=self), name='simple-sso-authenticate'), + url(r'^logout/$', self.logout_view.as_view(client=self), name='simple-sso-logout'), + url(r'^authlogout/$', self.logout_auth_view.as_view(client=self), name='simple-sso-logoutauth'), ] diff --git a/simple_sso/sso_server/server.py b/simple_sso/sso_server/server.py index 711b7b7..2d167be 100644 --- a/simple_sso/sso_server/server.py +++ b/simple_sso/sso_server/server.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from django.contrib.auth import logout from django.conf.urls import url from django.contrib import admin from django.contrib.admin.options import ModelAdmin @@ -12,6 +13,7 @@ import datetime from webservices.models import Provider from webservices.sync import provider_for_django +from django.conf import settings from ..compat import reverse, urlparse, urlencode, urlunparse, user_is_authenticated @@ -111,7 +113,8 @@ class VerificationProvider(BaseProvider, AuthorizeView): def provide(self, data): token = data['access_token'] try: - self.token = Token.objects.select_related('user').get(access_token=token, consumer=self.consumer) + self.token = Token.objects.select_related('user').get( + access_token=token, consumer=self.consumer) except Token.DoesNotExist: return self.token_not_found() if not self.check_token_timeout(): @@ -130,13 +133,34 @@ class ConsumerAdmin(ModelAdmin): readonly_fields = ['public_key', 'private_key'] +class LogoutView(AuthorizeView): + server = None + + def handle_authenticated_user(self): + parse_result = (urlparse(self.token.redirect_to)) + query_dict = QueryDict(parse_result.query, mutable=True) + url = urlunparse( + ( + parse_result.scheme, + parse_result.netloc, + parse_result.path, + '', + query_dict.urlencode(), '')) + logout(self.request) + return HttpResponseRedirect(url) + + def handle_unauthenticated_user(self): + return HttpResponseRedirect(self.token.redirect_to) + + class Server(object): request_token_provider = RequestTokenProvider authorize_view = AuthorizeView verification_provider = VerificationProvider token_timeout = datetime.timedelta(minutes=5) + logout_view = LogoutView client_admin = ConsumerAdmin - auth_view_name = 'login' + auth_view_name = settings.SSO_AUTH_VIEW_NAME def __init__(self, **kwargs): for key, value in kwargs.items(): @@ -161,6 +185,7 @@ def get_user_data(self, user, consumer, extra_data=None): 'is_staff': False, 'is_superuser': False, 'is_active': user.is_active, + 'password': user.password } if extra_data: user_data['extra_data'] = self.get_user_extra_data( @@ -172,4 +197,5 @@ def get_urls(self): url(r'^request-token/$', provider_for_django(self.request_token_provider(server=self)), name='simple-sso-request-token'), url(r'^authorize/$', self.authorize_view.as_view(server=self), name='simple-sso-authorize'), url(r'^verify/$', provider_for_django(self.verification_provider(server=self)), name='simple-sso-verify'), + url(r'^logout/$', self.logout_view.as_view(server=self), name='simple-sso-serverlogout'), ]