diff --git a/server/src/uds/REST/methods/tickets.py b/server/src/uds/REST/methods/tickets.py index e1309dfc..74572e7c 100644 --- a/server/src/uds/REST/methods/tickets.py +++ b/server/src/uds/REST/methods/tickets.py @@ -37,7 +37,7 @@ from uds.REST import RequestError from uds.models import Authenticator from uds.models import DeployedService from uds.models import Transport -from django.contrib.sessions.backends.db import SessionStore +from uds.core.util.Ticket import Ticket import datetime @@ -134,7 +134,7 @@ class Tickets(Handler): transport = None if servicePool is not None: - servicePool = DeployedService.objects.get(uuid=servicePool.upper()).uuid + servicePool = DeployedService.objects.get(uuid=servicePool.upper()) transport = self._params.get('transport', None) if transport is not None: @@ -149,11 +149,9 @@ class Tickets(Handler): if transport is None: logger.error('Service pool {} does not has transports') raise Exception('Service pool does not has any assigned transports') - transport = transport.uuid - # backUrl = self._params.get('exitUrl', None) - # Groups will be checked on user login stage, and invalid groups will be simply ignored - # If user is no part of ANY group, access will be denied + servicePool = servicePool.uuid + transport = transport.uuid except Authenticator.DoesNotExist: return Tickets.result(error='Authenticator does not exists') @@ -164,15 +162,16 @@ class Tickets(Handler): except Exception as e: return Tickets.result(error=six.text_type(e)) - store = SessionStore() - store.set_expiry(time) - store['username'] = username - store['password'] = password - store['realname'] = realname - store['groups'] = groups - store['auth'] = auth.uuid - store['servicePool'] = servicePool - store['transport'] = transport - store.save() + data = {} + data['username'] = username + data['password'] = password + data['realname'] = realname + data['groups'] = groups + data['auth'] = auth.uuid + data['servicePool'] = servicePool + data['transport'] = transport - return Tickets.result(store.session_key) + ticket = Ticket() + ticket.save(data, time) + + return Tickets.result(ticket.key) diff --git a/server/src/uds/core/auths/auth.py b/server/src/uds/core/auths/auth.py index b831d9b1..06de81a8 100644 --- a/server/src/uds/core/auths/auth.py +++ b/server/src/uds/core/auths/auth.py @@ -61,6 +61,9 @@ ROOT_ID = -20091204 # Any negative number will do the trick def getUDSCookie(request, response=None, force=False): + ''' + Generates a random cookie for uds, used, for example, to encript things + ''' if 'uds' not in request.COOKIES: import random import string @@ -316,12 +319,13 @@ def authLogLogin(request, authenticator, userName, java, os, logStr=''): authLogger.info('|'.join([authenticator.name, userName, javaStr, os['OS'], logStr, request.META.get('HTTP_USER_AGENT', 'Undefined')])) level = (logStr == 'Logged in') and log.INFO or log.ERROR log.doLog(authenticator, level, 'user {0} has {1} from {2} {3} java and os is {4}'.format(userName, logStr, - request.ip, java and 'has' or 'has NOT', os['OS']), log.WEB) + request.ip, java and 'has' or 'has NOT', os['OS']), log.WEB) try: user = authenticator.users.get(name=userName) - log.doLog(user, level, '{0} from {1} {2} java and os is {3}'.format(logStr, - request.ip, java and 'has' or 'has NOT', os['OS']), log.WEB) + log.doLog(user, level, + '{0} from {1} {2} java and os is {3}'.format(logStr, request.ip, java and 'has' or 'has NOT', os['OS']), log.WEB + ) except: pass diff --git a/server/src/uds/core/util/Cache.py b/server/src/uds/core/util/Cache.py index bc841099..80ec139b 100644 --- a/server/src/uds/core/util/Cache.py +++ b/server/src/uds/core/util/Cache.py @@ -62,13 +62,17 @@ class Cache(object): expired = now > c.created + timedelta(seconds=c.validity) if expired: return defValue - val = cPickle.loads(c.value.decode(Cache.CODEC).encode('utf-8')) + val = cPickle.loads(c.value.decode(Cache.CODEC)) return val except dbCache.DoesNotExist: # @UndefinedVariable logger.debug('key not found: {}'.format(skey)) return defValue def remove(self, skey): + ''' + Removes an stored cached item + If cached item does not exists, nothing happens (no exception thrown) + ''' # logger.debug('Removing key "%s" for uService "%s"' % (skey, self._owner)) try: key = self.__getKey(skey) diff --git a/server/src/uds/core/util/Ticket.py b/server/src/uds/core/util/Ticket.py new file mode 100644 index 00000000..09fcc2b5 --- /dev/null +++ b/server/src/uds/core/util/Ticket.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2014 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com +''' +from __future__ import unicode_literals + +from uds.core.util.Cache import Cache +from uds.core.managers import cryptoManager + +TICKET_OWNER = 'e6242ba4-62fa-11e4-b7ec-10feed05884b' + + +class Ticket(object): + ''' + Manages tickets & ticketing save/loading + Right now, uses cache as backend + ''' + + def __init__(self, key=None): + self.uuidGenerator = cryptoManager().uuid + self.cache = Cache(TICKET_OWNER) + self.data = None + self.key = key + if key is not None: + self.load() + else: + self.key = self.uuidGenerator() + + def save(self, data, validity): + ''' + Stores data inside ticket, and make data persistent (store in db) + ''' + self.data = data + self.cache.put(self.key, self.data, validity) + return self.key + + def load(self): + ''' + Load data (if still valid) for a ticket + ''' + self.data = self.cache.get(self.key, None) + return self.data + + def delete(self): + ''' + Removes a ticket from storage (db) + ''' + self.cache.remove(self.key) diff --git a/server/src/uds/urls.py b/server/src/uds/urls.py index 225e2bda..7ab41d0c 100644 --- a/server/src/uds/urls.py +++ b/server/src/uds/urls.py @@ -71,7 +71,7 @@ urlpatterns = patterns( (r'^authinfo/(?P.+)', 'web.views.authInfo'), (r'^about', 'web.views.about'), # Ticket authentication - (r'^tkauth/(?P.+)/(?P.*)', 'web.views.ticketAuth'), + url(r'^tkauth/(?P.+)$', 'web.views.ticketAuth', name='TicketAuth'), # XMLRPC Processor diff --git a/server/src/uds/web/views.py b/server/src/uds/web/views.py index de2c5183..b779296f 100644 --- a/server/src/uds/web/views.py +++ b/server/src/uds/web/views.py @@ -30,7 +30,7 @@ ''' from __future__ import unicode_literals -from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden +from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, HttpResponsePermanentRedirect from django.views.decorators.csrf import csrf_exempt from django.shortcuts import render_to_response from django.shortcuts import render @@ -40,7 +40,6 @@ from django.utils.translation import ugettext as _ from django.core.urlresolvers import reverse from django.views.decorators.http import last_modified from django.views.i18n import javascript_catalog -from django.contrib.sessions.backends.db import SessionStore from django.utils import timezone from uds.core.auths.auth import webLogin, webLogout, webLoginRequired, authenticate, webPassword, authenticateViaCallback, authLogLogin, authLogLogout, getUDSCookie @@ -53,6 +52,7 @@ from uds.core.util.Config import GlobalConfig from uds.core.util.Cache import Cache from uds.core.util import OsDetector from uds.core.util import log +from uds.core.util.Ticket import Ticket from uds.core.util.State import State from uds.core.ui import theme from uds.core.auths.Exceptions import InvalidUserException @@ -465,23 +465,24 @@ def ticketAuth(request, ticketId): ''' Used to authenticate an user via a ticket ''' - session = SessionStore(session_key=ticketId) + ticket = Ticket(ticketId) try: try: - # Extract data from session storage, and remove it if success - username = session['username'] - groups = session['groups'] - auth = session['auth'] - realname = session['realname'] - servicePool = session['servicePool'] - password = session['password'] - transport = session['transport'] + # Extract ticket.data from ticket.data storage, and remove it if success + username = ticket.data['username'] + groups = ticket.data['groups'] + auth = ticket.data['auth'] + realname = ticket.data['realname'] + servicePool = ticket.data['servicePool'] + password = ticket.data['password'] + transport = ticket.data['transport'] except: logger.error('Ticket stored is not valid') raise InvalidUserException() - session.delete() + # Remove ticket + ticket.delete() auth = Authenticator.objects.get(uuid=auth) # If user does not exists in DB, create it right now @@ -506,26 +507,26 @@ def ticketAuth(request, ticketId): # Right now, we assume that user supports java, let's see how this works request.session['java'] = True - request['OS'] = OsDetector.getOsFromUA(request.META.get('HTTP_USER_AGENT')) + request.session['OS'] = OsDetector.getOsFromUA(request.META.get('HTTP_USER_AGENT')) + request.user = usr # Temporaly store this user as "authenticated" user, next requests will be done using session # Force cookie generation - getUDSCookie(request) + webLogin(request, None, usr, password) # Check if servicePool is part of the ticket if servicePool is not None: servicePool = DeployedService.objects.get(uuid=servicePool) - # Check if servicepool can't be accessed by groups + # Check if service pool can't be accessed by groups servicePool.validateUser(usr) transport = Transport.objects.get(uuid=transport) - response = service(request, servicePool.id, transport.id) + response = service(request, 'F' + servicePool.uuid, transport.uuid) # 'A' Indicates 'assigned service' else: - response = HttpResponseRedirect(reverse('uds.web.views.index')) + response = HttpResponsePermanentRedirect(reverse('uds.web.views.index')) - # Now ensure cookie is at response + # Now ensure uds cookie is at response getUDSCookie(request, response, True) - - webLogin(request, response, usr, password) # Password is passed in by ticket, and probably will be empty + return response except Authenticator.DoesNotExist: logger.error('Ticket has an non existing authenticator') @@ -534,6 +535,7 @@ def ticketAuth(request, ticketId): logger.error('Ticket has an invalid Service Pool') return error(request, InvalidServiceException()) except Exception as e: + logger.exception('Exception') return errors.exceptionView(request, e) return HttpResponse(ticketId, content_type='text/plain')