# TOMUSS: The Online Multi User Simple Spreadsheet
# Copyright (C) 2008-2013 Thierry EXCOFFIER, Universite Claude Bernard
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Contact: Thierry.EXCOFFIER@bat710.univ-lyon1.fr
import time
import socket
from . import utilities
from . import inscrits
from . import ticket
from . import sender
from . import plugin
from . import configuration
from . import document
warn = utilities.warn
# To synchronize with JavaScript encode_uri and decode_uri_option
def canonize(s):
return (s.replace("$", "$24").replace('?','$3F').replace('#','$23')
.replace("/", "$2F").replace("&", '$26').replace(".", '$2E')
.replace("+", '$2B').replace(" ", "$20").replace("\n", "$0A")
.replace("\r", "$0D")
)
def redirect(server, url):
server.send_response(307)
server.send_header('Location', url)
server.end_headers()
server.close_connection_now()
def ticket_login_name(ticket_key, service, server=None):
return configuration.authenticator.login_from_ticket(ticket_key,
service, server)
def ticket_ask(server, dummy_server_url, service):
redirect(server, configuration.authenticator.redirection(service, server))
return None, None
def get_path(server, server_url):
"""
If the user is connected, the function returns the ticket object
and a clean path.
If the user is not connected, it is redirected to the
authentication service.
"""
ticket_key, path = ticket.get_ticket_string(server)
escaped_path = '/'.join(canonize(i) for i in path)
ticket_object = ticket.get_ticket_objet(ticket_key, server)
# Ticket OK
if ticket_object:
warn('fast ticket:%s' % str(ticket_object)[:-1], what='auth')
path = server_url + '/=' + ticket_object.ticket +'/'+ escaped_path
warn('fast path: %s' % str(path), what='auth')
return ticket_object, path
if ticket_object is not None:
ticket_key = None # Because this ticket is not fine
# 2.8.10
service = server_url + '/' + escaped_path.split('?')[0] + '?unsafe=1'
warn('SERVICE: %s TICKET: %s' % (service, ticket_key), what="auth")
if ticket_key != None:
sender.send_live_status('<script>b("/CAS");</script>\n')
s = time.time()
user_name = ticket_login_name(ticket_key, service, server)
s = time.time() - s
sender.send_live_status(
'<script>d("%s","/CAS","",%6.4f,"%6.4fs","","","");</script>\n' %
(configuration.cas, s, s))
warn('username: %s' % user_name, what="auth")
if user_name:
t = ticket.add_ticket(
ticket_key, user_name, ticket.client_ip(server),
server.headers["user-agent"],
language=server.headers.get('accept-language',''))
if path and path[0] == 'allow':
warn('allow request for ticket : ' + path[1], what="auth")
if path[1] not in ticket.tickets \
or ( ticket.tickets[path[1]].user_name == user_name
and ticket.tickets[path[1]].user_browser
== server.headers["user-agent"]
):
# Update the old ticket
ticket.tickets[path[1]] = ticket.clone(path[1], t)
redirect(server, configuration.url_files
+ '/auth_close.html')
else:
redirect(server, configuration.url_files
+ '/allow_error.html')
utilities.send_backtrace('bad allow request',
exception=False)
return None, None
return t, path
warn("No or bad ticket: Redirect the browser", what="auth")
ticket_ask(server, server_url, service)
return None, None
authentication_requests = []
authentication_redirect = None
def update_ticket(tick):
if tick.is_member_of('staff'):
tick.password_ok = inscrits.L_fast.password_ok(tick.user_name)
tick.set_language(document.get_preferences(tick.user_name,
create_pref=False,
the_ticket=tick)["language"]
)
else:
tick.password_ok = True
def authentication_thread():
"""The send_response 307 (redirection) is yet done"""
ticket.remove_old_files()
while True:
time.sleep(0.1)
while len(authentication_requests):
x = authentication_requests.pop()
while not x.wfile.closed or not x.rfile.closed:
time.sleep(0.01)
# now it is safe because the Handler has closed the file
x.restore_connection()
redirect_loc = authentication_redirect
try:
if not x.ticket or not x.ticket.is_fine(x):
x.ticket, dummy_the_path = get_path(x, redirect_loc)
if x.ticket == None:
x.log_time('redirection')
continue # Redirection done
update_ticket(x.ticket)
# The request can be executed
try:
plugin.dispatch_request(x)
except AttributeError:
utilities.send_backtrace(str(x.ticket)[:-1],
subject = 'Authentication aborted:' + x.path)
if not x.please_do_not_close:
x.close_connection_now()
except (AttributeError, IOError, socket.error):
utilities.send_backtrace(
'', subject = 'AUTH '+ str(x.ticket)[:-1])
def run_authentication():
utilities.start_new_thread_immortal(authentication_thread, ())
def ok(server):
# Don't want to be blocked by authentication
if server.ticket and hasattr(server.ticket, 'password_ok'):
return True
# Problem with the request with an ever changing IP
if server.ticket is False:
redirect(server, configuration.url_files + '/ip_error.html')
return
warn('Append to authentication queue', what="auth")
# The Connection:close is sent by send_response to please HTTP/1.1
server.do_not_close_connection()
authentication_requests.append(server)