summaryrefslogtreecommitdiffstats
path: root/python/tests/http.py
diff options
context:
space:
mode:
authorEmmanuel Raviart <eraviart@entrouvert.com>2004-08-09 16:19:45 +0000
committerEmmanuel Raviart <eraviart@entrouvert.com>2004-08-09 16:19:45 +0000
commita0a74c1a170f5e8e2c02bd52e43abfe598433a96 (patch)
treeca0de00de00271e46552e44b506965e7126c115b /python/tests/http.py
parent710b77536cf42d6c43fddd45e0d717704520dfe6 (diff)
downloadlasso-a0a74c1a170f5e8e2c02bd52e43abfe598433a96.tar.gz
lasso-a0a74c1a170f5e8e2c02bd52e43abfe598433a96.tar.xz
lasso-a0a74c1a170f5e8e2c02bd52e43abfe598433a96.zip
Updated Python tests. Not finished but Valos want it to debug Lasso.
Diffstat (limited to 'python/tests/http.py')
-rw-r--r--python/tests/http.py600
1 files changed, 386 insertions, 214 deletions
diff --git a/python/tests/http.py b/python/tests/http.py
index fca7eec7..301f0393 100644
--- a/python/tests/http.py
+++ b/python/tests/http.py
@@ -274,7 +274,7 @@ class HttpRequestHandlerMixin(abstractweb.HttpRequestHandlerMixin):
# requestline = None # Strange
# request_version = None # Strange
server_version = 'HttpRequestHandlerMixin/1.0'
- webServer = None # Class variable.
+ site = None # Class variable.
def handle(self):
"""Handle multiple requests if necessary."""
@@ -313,11 +313,186 @@ class HttpRequestHandlerMixin(abstractweb.HttpRequestHandlerMixin):
return
logger.info(self.raw_requestline.strip())
logger.debug(str(self.headers))
-## if hasattr(self.connection, 'get_peer_certificate'):
-## peerCertificate = self.connection.get_peer_certificate()
+
+###############
+
+ session = None
+ sessionToken = None
+ user = None
+
+ # Handle X.509 certificate authentication.
+ if hasattr(self.connection, 'get_peer_certificate'):
+ clientCertificate = self.connection.get_peer_certificate()
+ if clientCertificate:
+ user = self.site.authenticateX509User(clientCertificate)
+ if user is None:
+ logger.info('Unknown certificate (serial number = %s)'
+ % clientCertificate.get_serial_number())
+ else:
+ sessionToken = user.sessionToken
+ if sessionToken:
+ session = self.site.getSessionFromToken(sessionToken)
+ if session is None:
+ sessionToken = None
+ del user.sessionToken
+ # Don't call user.getDocument().save() now, because the session
+ # will be retrieved (from cookie or sessionToken query field)
+ # or created. So user/@sessionToken will be updated.
+ else:
+ # For security reasons, we want to minimize the publication of
+ # session token (it is better not to store it in a cookie or in
+ # URLs). The client need to send the certificate each time, for the
+ # session to continue.
+ if session.publishToken:
+ del session.publishToken
+
+ # Handle HTTP authentication.
+ authorization = self.httpRequest.headers.get('authorization')
+ if self.httpRequest.hasQueryField('login') and not authorization \
+ and rootDataHolder.getConfigBoolean('yep:useHttpAuthentication', default = False):
+ # Ask for HTTP authentication.
+ return self.outputErrorUnauthorized(httpPath)
+ if self.httpRequest.hasQueryField('logout') and authorization:
+ # Since HTTP authentication provides no way to logout, we send a status
+ # Unauthorized to force the user to press the cancel button. But instead of
+ # sending an error page immediately, we send the real page, so the user will see
+ # the page instead of an error message.
+ authorization = None
+ self.httpAuthenticationLogoutTrick = True
+ if authorization:
+ try:
+ authenticationScheme, credentials = authorization.split(None, 1)
+ except ValueError:
+ return self.outputErrorUnauthorized(httpPath)
+ authenticationScheme = authenticationScheme.lower()
+ if authenticationScheme == 'basic':
+ loginAndPassword = base64.decodestring(credentials)
+ try:
+ login, password = loginAndPassword.split(':', 1)
+ except:
+ login = loginAndPassword
+ password = ''
+ logs.debug('Basic authentication: login = "%s" / password = "%s"' % (
+ login, password))
+ if password:
+ user = self.site.authenticateLoginPasswordUser(login, password)
+ if user is None:
+ logger.info('Unknown user (login = "%s" / password = "%s")' % (
+ login, password))
+ return self.outputErrorUnauthorized(httpPath)
+ else:
+ sessionToken = user.sessionToken
+ if sessionToken:
+ session = sessions.retrieveSession(sessionToken)
+ if session is None:
+ sessionToken = None
+ user.deleteSessionToken()
+ # Don't call user.getDocument().save() now, because the session
+ # will be retrieved (from cookie or sessionToken query field)
+ # or created. So user/@sessionToken will be updated.
+ else:
+ # For security reasons, we want to minimize the publication of
+ # session token (it is better not to store it in a cookie or in
+ # URLs).
+ if session.publishToken:
+ del session.publishToken
+ elif login:
+ # No password was given. Assume login contains a session token.
+ # TODO: sanity chek on login
+ session = sessions.retrieveSession(login)
+ if session is not None:
+ account = session.getAccount()
+ if account is not None:
+ user = account.getUser()
+ if user is not None \
+ and user.getSessionToken() != session.getToken():
+ # Sanity check.
+ user.setSessionToken(session.getToken())
+ user.getDocument().save()
+ else:
+ logs.info('Unknown authentication scheme = %s' % authenticationScheme)
+ return self.outputErrorUnauthorized(httpPath)
+
+ # Handle use of cookies, session and user.
+ cookie = None
+ cookieContent = {}
+ if self.httpRequest.headers.has_key('Cookie'):
+ logs.debug('Cookie received:')
+ cookie = Cookie.SimpleCookie(
+ self.httpRequest.headers['Cookie'])
+ for k, v in cookie.items():
+ cookieContent[k] = v.value
+ logs.debug(' %s = %s' % (k, cookieContent[k]))
+ self.cookie = cookie
+
+ sessionToken = None
+ sessionTokenInCookie = False
+ if self.httpRequest.hasQueryField('sessionToken'):
+ sessionToken = self.httpRequest.getQueryField('sessionToken')
+ if not sessionToken:
+ sessionToken = None
+ if session is not None and sessionToken != session.token:
+ sessionToken = None
+ if cookieContent.has_key('sessionToken'):
+ cookieSessionToken = cookieContent['sessionToken']
+ if cookieSessionToken:
+ if session is None or cookieSessionToken == session.token:
+ if sessionToken is None:
+ sessionToken = cookieSessionToken
+ if cookieSessionToken == sessionToken:
+ sessionTokenInCookie = True
+ canUseCookie = True
+ if session is None and sessionToken is not None:
+ session = sessions.retrieveSession(sessionToken)
+ if session is None:
+ sessionToken = None
+ sessionTokenInCookie = False
+ else:
+ if user is None:
+ # import expression.modules.passwordaccounts as passwordaccounts
+ account = session.getAccount(acceptOnlyAccount = False)
+ if account is not None:
+ user = account.getUser()
+ if user is not None and user.getSessionToken() != sessionToken:
+ # Sanity check.
+ user.setSessionToken(session.getToken())
+ user.getDocument().save()
+ else:
+ # The user has been authenticated (using HTTP authentication), but the
+ # associated session didn't exist (or was too old, or...). So, update
+ # its sessionToken.
+ user.setSessionToken(session.getToken())
+ user.getDocument().save()
+ # For security reasons, we want to minimize the publication of session
+ # token (it is better not to store it in a cookie or in URLs).
+ if session.publishToken:
+ del session.publishToken
+ if session is None and user is not None:
+ # The user has been authenticated (using HTTP authentication), but the session
+ # doesn't exist yet (or was too old, or...). Create a new session.
+ session = sessions.getOrCreateSession()
+ # For security reasons, we want to minimize the publication of session
+ # token (it is better not to store it in a cookie or in URLs).
+ # session.publishToken = False # False is the default value.
+ session.setAccountAbsolutePath(account.getAbsolutePath())
+ user.setSessionToken(session.getToken())
+ user.getDocument().save()
+ self.user = user
+ if user is not None:
+ logs.debug('User: %s' % user.simpleLabel)
+ self.session = session
+ if session is not None:
+ if not sessionTokenInCookie:
+ # The sessionToken is valid but is not stored in the cookie. So, don't try to
+ # use cookie.
+ canUseCookie = False
+ logs.debug('Session: %s' % session.token)
+ self.canUseCookie = canUseCookie
+
+###############
try:
- self.webServer.handleHttpRequestHandler(self)
+ self.site.handleHttpRequestHandler(self)
except IOError:
logger.exception('An exception occured:')
path = self.path.split('?')[0]
@@ -332,146 +507,146 @@ class HttpRequestHandlerMixin(abstractweb.HttpRequestHandlerMixin):
logger.info('%s - - [%s] %s' % (
self.address_string(), self.log_date_time_string(), format % arguments))
- def outputAlert(self, data, title = None, url = None):
- import html
- if title is None:
- title = N_('Alert')
- # FIXME: Handle XSLT template.
- if url:
- buttonsBar = html.div(class_ = 'buttons-bar')
- actionButtonsBar = html.span(class_ = 'action-buttons-bar')
- buttonsBar.append(actionButtonsBar)
- actionButtonsBar.append(html.a(_('OK'), class_ = 'button', href = url))
- else:
- buttonsBar = None
- layout = html.html(
- html.head(html.title(_(title))),
- html.body(
- html.p(_(data), class_ = 'alert'),
- buttonsBar,
- ),
- )
- self.outputData(layout.serialize(), contentLocation = None, mimeType = 'text/html')
-
- def outputData(self, data, contentLocation = None, headers = None, mimeType = None,
- modificationTime = None, successCode = 200):
- # Session must be saved before responding. Otherwise, when the server is multitasked or
- # multithreaded, it may receive a new HTTP request before the session is saved.
- if self.session is not None and self.session.isDirty:
- self.session.save()
-
- if isinstance(data, basestring):
- dataFile = None
- dataSize = len(data)
- else:
- dataFile = data
- data = ''
- if hasattr(dataFile, 'fileno'):
- dataSize = os.fstat(dataFile.fileno())[6]
- else:
- # For StringIO and cStringIO classes.
- dataSize = len(dataFile.getvalue())
-
- if headers is None:
- headers = {}
- if time.time() > self.socketCreationTime + 300:
- headers['Connection'] = 'close'
- elif not self.close_connection:
- headers['Connection'] = 'Keep-Alive'
- if contentLocation is not None:
- headers['Content-Location'] = contentLocation
- if mimeType:
- headers['Content-Type'] = '%s; charset=utf-8' % mimeType
- if modificationTime:
- headers['Last-Modified'] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', modificationTime)
- # TODO: Could also output Content-MD5.
- ifModifiedSince = self.headers.get('If-Modified-Since')
- if modificationTime and ifModifiedSince:
- # We don't want to use bandwith if the file was not modified.
- try:
- ifModifiedSinceTime = time.strptime(ifModifiedSince[:25], '%a, %d %b %Y %H:%M:%S')
- if modificationTime[:8] <= ifModifiedSinceTime[:8]:
- self.send_response(304, 'Not Modified.')
- for key in ('Connection', 'Content-Location'):
- if key in headers:
- self.send_header(key, headers[key])
- self.setCookie()
- self.end_headers()
- return
- except (ValueError, KeyError):
- pass
- if dataFile is not None:
- assert not data
- data = dataFile.read(1048576) # Read first MB chunk
- if mimeType == 'text/html' and data.startswith('<?xml'):
- # Internet Explorer 6 renders the page differently when they start with <?xml...>, so
- # skip it.
- i = data.find('\n')
- if i > 0:
- data = data[i + 1:]
- else:
- i = data.find('>')
- if i > 0:
- data = data[i + 1:]
- dataSize -= i + 1
- # Compress data if possible and if data is not too big.
- acceptEncoding = self.headers.get('Accept-Encoding', '')
- if 0 < dataSize < 1048576 and 'gzip' in acceptEncoding \
- and 'gzip;q=0' not in acceptEncoding:
- # Since dataSize < 1 MB, the data is fully contained in string.
- zbuf = cStringIO.StringIO()
- zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf)
- zfile.write(data)
- zfile.close()
- data = zbuf.getvalue()
- dataSize = len(data)
- headers['Content-Encoding'] = 'gzip'
- headers['Content-Length'] = '%d' % dataSize
- successMessages = {
- 200: 'OK',
- 207: 'Multi-Status',
- }
- assert successCode in successMessages, 'Unknown success code %d.' % successCode
- if self.httpAuthenticationLogoutTrick and successCode == 200:
- successCode = 401
- successMessage = 'Access Unauthorized'
- headers['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
- else:
- successMessage = successMessages[successCode]
- self.send_response(successCode, successMessage)
- for key, value in headers.items():
- self.send_header(key, value)
- self.setCookie()
- self.end_headers()
- if self.httpRequest.method != 'HEAD' and dataSize > 0:
- outputFile = self.wfile
- if data:
- outputFile.write(data)
- if dataFile is not None:
- while True:
- chunk = dataFile.read(1048576) # 1 MB chunk
- if not chunk:
- break
- outputFile.write(chunk)
- return
-
- def outputErrorAccessForbidden(self, filePath):
- if filePath is None:
- message = 'Access Forbidden'
- else:
- message = 'Access to "%s" Forbidden.' % filePath
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- return self.send_error(403, message, data, setCookie = True)
-
- def outputErrorBadRequest(self, reason):
- if reason:
- message = 'Bad Request: %s' % reason
- else:
- message = 'Bad Request'
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- return self.send_error(400, message, data)
+## def outputAlert(self, data, title = None, url = None):
+## import html
+## if title is None:
+## title = N_('Alert')
+## # FIXME: Handle XSLT template.
+## if url:
+## buttonsBar = html.div(class_ = 'buttons-bar')
+## actionButtonsBar = html.span(class_ = 'action-buttons-bar')
+## buttonsBar.append(actionButtonsBar)
+## actionButtonsBar.append(html.a(_('OK'), class_ = 'button', href = url))
+## else:
+## buttonsBar = None
+## layout = html.html(
+## html.head(html.title(_(title))),
+## html.body(
+## html.p(_(data), class_ = 'alert'),
+## buttonsBar,
+## ),
+## )
+## self.outputData(layout.serialize(), contentLocation = None, mimeType = 'text/html')
+
+## def outputData(self, data, contentLocation = None, headers = None, mimeType = None,
+## modificationTime = None, successCode = 200):
+## # Session must be saved before responding. Otherwise, when the server is multitasked or
+## # multithreaded, it may receive a new HTTP request before the session is saved.
+## if self.session is not None and self.session.isDirty:
+## self.session.save()
+
+## if isinstance(data, basestring):
+## dataFile = None
+## dataSize = len(data)
+## else:
+## dataFile = data
+## data = ''
+## if hasattr(dataFile, 'fileno'):
+## dataSize = os.fstat(dataFile.fileno())[6]
+## else:
+## # For StringIO and cStringIO classes.
+## dataSize = len(dataFile.getvalue())
+
+## if headers is None:
+## headers = {}
+## if time.time() > self.socketCreationTime + 300:
+## headers['Connection'] = 'close'
+## elif not self.close_connection:
+## headers['Connection'] = 'Keep-Alive'
+## if contentLocation is not None:
+## headers['Content-Location'] = contentLocation
+## if mimeType:
+## headers['Content-Type'] = '%s; charset=utf-8' % mimeType
+## if modificationTime:
+## headers['Last-Modified'] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', modificationTime)
+## # TODO: Could also output Content-MD5.
+## ifModifiedSince = self.headers.get('If-Modified-Since')
+## if modificationTime and ifModifiedSince:
+## # We don't want to use bandwith if the file was not modified.
+## try:
+## ifModifiedSinceTime = time.strptime(ifModifiedSince[:25], '%a, %d %b %Y %H:%M:%S')
+## if modificationTime[:8] <= ifModifiedSinceTime[:8]:
+## self.send_response(304, 'Not Modified.')
+## for key in ('Connection', 'Content-Location'):
+## if key in headers:
+## self.send_header(key, headers[key])
+## self.setCookie()
+## self.end_headers()
+## return
+## except (ValueError, KeyError):
+## pass
+## if dataFile is not None:
+## assert not data
+## data = dataFile.read(1048576) # Read first MB chunk
+## if mimeType == 'text/html' and data.startswith('<?xml'):
+## # Internet Explorer 6 renders the page differently when they start with <?xml...>, so
+## # skip it.
+## i = data.find('\n')
+## if i > 0:
+## data = data[i + 1:]
+## else:
+## i = data.find('>')
+## if i > 0:
+## data = data[i + 1:]
+## dataSize -= i + 1
+## # Compress data if possible and if data is not too big.
+## acceptEncoding = self.headers.get('Accept-Encoding', '')
+## if 0 < dataSize < 1048576 and 'gzip' in acceptEncoding \
+## and 'gzip;q=0' not in acceptEncoding:
+## # Since dataSize < 1 MB, the data is fully contained in string.
+## zbuf = cStringIO.StringIO()
+## zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf)
+## zfile.write(data)
+## zfile.close()
+## data = zbuf.getvalue()
+## dataSize = len(data)
+## headers['Content-Encoding'] = 'gzip'
+## headers['Content-Length'] = '%d' % dataSize
+## successMessages = {
+## 200: 'OK',
+## 207: 'Multi-Status',
+## }
+## assert successCode in successMessages, 'Unknown success code %d.' % successCode
+## if self.httpAuthenticationLogoutTrick and successCode == 200:
+## successCode = 401
+## successMessage = 'Access Unauthorized'
+## headers['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
+## else:
+## successMessage = successMessages[successCode]
+## self.send_response(successCode, successMessage)
+## for key, value in headers.items():
+## self.send_header(key, value)
+## self.setCookie()
+## self.end_headers()
+## if self.httpRequest.method != 'HEAD' and dataSize > 0:
+## outputFile = self.wfile
+## if data:
+## outputFile.write(data)
+## if dataFile is not None:
+## while True:
+## chunk = dataFile.read(1048576) # 1 MB chunk
+## if not chunk:
+## break
+## outputFile.write(chunk)
+## return
+
+## def outputErrorAccessForbidden(self, filePath):
+## if filePath is None:
+## message = 'Access Forbidden'
+## else:
+## message = 'Access to "%s" Forbidden.' % filePath
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## return self.send_error(403, message, data, setCookie = True)
+
+## def outputErrorBadRequest(self, reason):
+## if reason:
+## message = 'Bad Request: %s' % reason
+## else:
+## message = 'Bad Request'
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## return self.send_error(400, message, data)
def outputErrorInternalServer(self):
message = 'Internal Server Error'
@@ -479,71 +654,71 @@ class HttpRequestHandlerMixin(abstractweb.HttpRequestHandlerMixin):
data = '<html><body>%s</body></html>' % message
return self.send_error(500, message, data)
- def outputErrorMethodNotAllowed(self, reason):
- if reason:
- message = 'Method Not Allowed: %s' % reason
- else:
- message = 'Method Not Allowed'
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- # This error doesn't need a pretty interface.
- # FIXME: Add an 'Allow' header containing a list of valid methods for the requested
- # resource.
- return self.send_error(405, message, data)
-
- def outputErrorNotFound(self, filePath):
- if filePath is None:
- message = 'Not Found'
- else:
- message = 'Path "%s" Not Found.' % filePath
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- return self.send_error(404, message, data, setCookie = True)
-
- def outputErrorUnauthorized(self, filePath):
- if filePath is None:
- message = 'Access Unauthorized'
- else:
- message = 'Access to "%s" Unauthorized.' % filePath
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- headers = {}
- return self.send_error(401, message, data, headers, setCookie = True)
-
- def outputInformationContinue(self):
- message = 'Continue'
- logger.debug(message)
- self.send_response(100, message)
-
- def outputSuccessCreated(self, filePath):
- if filePath is None:
- message = 'Created'
- else:
- message = 'File "%s" Created.' % filePath
- logger.debug(message)
- data = '<html><body>%s</body></html>' % message
- self.send_response(201, message)
- if time.time() > self.socketCreationTime + 300:
- self.send_header('Connection', 'close')
- elif not self.close_connection:
- self.send_header('Connection', 'Keep-Alive')
- self.send_header('Content-Type', 'text/html; charset=utf-8')
- self.send_header('Content-Length', '%d' % len(data))
- self.setCookie()
- self.end_headers()
- if self.httpRequest.method != 'HEAD':
- self.wfile.write(data)
-
- def outputSuccessNoContent(self):
- message = 'No Content'
- logger.debug(message)
- self.send_response(204, message)
- if time.time() > self.socketCreationTime + 300:
- self.send_header('Connection', 'close')
- elif not self.close_connection:
- self.send_header('Connection', 'Keep-Alive')
- self.setCookie()
- self.end_headers()
+## def outputErrorMethodNotAllowed(self, reason):
+## if reason:
+## message = 'Method Not Allowed: %s' % reason
+## else:
+## message = 'Method Not Allowed'
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## # This error doesn't need a pretty interface.
+## # FIXME: Add an 'Allow' header containing a list of valid methods for the requested
+## # resource.
+## return self.send_error(405, message, data)
+
+## def outputErrorNotFound(self, filePath):
+## if filePath is None:
+## message = 'Not Found'
+## else:
+## message = 'Path "%s" Not Found.' % filePath
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## return self.send_error(404, message, data, setCookie = True)
+
+## def outputErrorUnauthorized(self, filePath):
+## if filePath is None:
+## message = 'Access Unauthorized'
+## else:
+## message = 'Access to "%s" Unauthorized.' % filePath
+## logger.info(message)
+## data = '<html><body>%s</body></html>' % message
+## headers = {}
+## return self.send_error(401, message, data, headers, setCookie = True)
+
+## def outputInformationContinue(self):
+## message = 'Continue'
+## logger.debug(message)
+## self.send_response(100, message)
+
+## def outputSuccessCreated(self, filePath):
+## if filePath is None:
+## message = 'Created'
+## else:
+## message = 'File "%s" Created.' % filePath
+## logger.debug(message)
+## data = '<html><body>%s</body></html>' % message
+## self.send_response(201, message)
+## if time.time() > self.socketCreationTime + 300:
+## self.send_header('Connection', 'close')
+## elif not self.close_connection:
+## self.send_header('Connection', 'Keep-Alive')
+## self.send_header('Content-Type', 'text/html; charset=utf-8')
+## self.send_header('Content-Length', '%d' % len(data))
+## self.setCookie()
+## self.end_headers()
+## if self.httpRequest.method != 'HEAD':
+## self.wfile.write(data)
+
+## def outputSuccessNoContent(self):
+## message = 'No Content'
+## logger.debug(message)
+## self.send_response(204, message)
+## if time.time() > self.socketCreationTime + 300:
+## self.send_header('Connection', 'close')
+## elif not self.close_connection:
+## self.send_header('Connection', 'Keep-Alive')
+## self.setCookie()
+## self.end_headers()
def outputUnknownException(self):
import traceback, cStringIO
@@ -704,10 +879,7 @@ class HttpsRequestHandler(HttpRequestHandlerMixin, BaseHTTPSRequestHandler):
# not enough for a web server.
class HttpServer(SocketServer.ForkingMixIn, BaseHTTPServer.HTTPServer):
- uriScheme = 'http'
- webServer = None
-
+ pass
class HttpsServer(SocketServer.ForkingMixIn, BaseHTTPSServer):
- uriScheme = 'https' # Ob
- webServer = None
+ pass