Ticket system seems to work

This commit is contained in:
Adolfo Gómez García 2014-11-03 05:14:25 +01:00
parent dd5b35b354
commit c2243b8f48
6 changed files with 127 additions and 42 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -71,7 +71,7 @@ urlpatterns = patterns(
(r'^authinfo/(?P<authName>.+)', 'web.views.authInfo'),
(r'^about', 'web.views.about'),
# Ticket authentication
(r'^tkauth/(?P<ticketId>.+)/(?P<serviceId>.*)', 'web.views.ticketAuth'),
url(r'^tkauth/(?P<ticketId>.+)$', 'web.views.ticketAuth', name='TicketAuth'),
# XMLRPC Processor

View File

@ -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')