# - Mike Warot's capabilities demo version 0.012
# Copyright (C) 2008 Michael A. Warot, all rights reserved.
# 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 3 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, see .
#
#
# Based on webserver.py Copyright Jon Berg , turtlemeat.com
# version - new capabilites
# 001 - moved to port 81 to avoid built in IIS in XP, etc.
# 002 - added code from Rogier Steehouder to allow for keyboard interrupt of the server when running from the command line
# 003 - add code to display requested path
# 004 - get a random number and display it if the request is /token
# turns out that Mersenne Twister is an available random number generator... close enough to cryptographic for a while. 8)
# 005 - keep a list of tokens we hand out, display on the main page
# 006 - allow deletion of a token from the list
# 007 - after deleting token, gets you back to the main page
# 008 - added GPL language (version 3 of the gpl, don't want this to end up in a Tivo) and startup hint in the command window
# 009 - Try to get more documentation working, and be a bit friendlier
# 010 - get rid of /token handler
# 011 - add code to update content
# 012 - move admin to /admin
# default page is now content only
#
import string,cgi,time, socket, random
from os import curdir, sep
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
validtokens = [] # start with no tokens
version = '0.012'
versiondate = 'May 15, 2008'
protectedcontent = 'The rain in Spain stays mainly in the plain.'
# code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/499376 which allows for keyboard interrupt of the server when running from command line+
class Server(HTTPServer):
"""HTTPServer class with timeout."""
def get_request(self):
"""Get the request and client address from the socket."""
# 10 second timeout
self.socket.settimeout(10.0)
result = None
while result is None:
try:
result = self.socket.accept()
except socket.timeout:
pass
# Reset timeout on the new socket
result[0].settimeout(None)
return result
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
global validtokens
try:
if self.path.endswith(".html"):
f = open(curdir + sep + self.path) #self.path has /test.html
#note that this potentially makes every file on your computer readable by the internet
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(f.read())
f.close()
return
# default out to a welcome page
if self.path.endswith("admin"):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write("Welcome to Mike Warot's little capabilities demo, version "+version+"
")
self.wfile.write('This is the administration page.
')
self.wfile.write('
Here is the capabilities protected content: ['+protectedcontent+']
')
self.wfile.write('
Here is a list of the valid access tokens:
')
for i in validtokens:
self.wfile.write('')
if (len(validtokens) > 0):
self.wfile.write('
You can revoke any of the outstanding capabilities with the corresponding revoke button.
')
self.wfile.write('
You can also grant permissions, which creates a new token, using the button below.
')
self.wfile.write('")
self.wfile.write('This is the user page.
')
self.wfile.write('')
return
except IOError:
self.send_error(404,'File Not Found: %s' % self.path)
def do_POST(self):
global validtokens
global protectedcontent
# Parse the form data posted
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={'REQUEST_METHOD':'POST',
'CONTENT_TYPE':self.headers['Content-Type'],
})
if self.path.endswith("grant"):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
x = "%032x" % (random.getrandbits(128))
validtokens = validtokens + [ x ]
self.wfile.write(x)
return
if self.path.endswith("write"):
cap = form["capability"].value
if (cap in validtokens):
protectedcontent = form["content"].value
self.send_response(200)
self.send_header('Refresh','2; url=/') # 2 seconds gives us time to debug this thing, and see the output
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write('Content has been successfully updated. Please return to the index page')
return
else:
self.send_response(200)
self.send_header('Refresh','20; url=/') # 20 seconds gives us time to debug this thing, and see the output
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write('Update failed, perhaps you wish to return to the index page')
return
cap = form["capability"].value
if (cap in validtokens) and self.path.endswith("delete"):
validtokens.remove(cap)
self.send_response(200)
self.send_header('Refresh','2; url=/') # 2 seconds gives us time to debug this thing, and see the output
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write('Please return to the index page')
pass
else:
# Begin the response if we didn't have a valid delete token request
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write('Client: %s\n' % str(self.client_address))
self.wfile.write('Path: %s\n' % self.path)
self.wfile.write('Form data:\n')
# Echo back information about what was posted in the form
for field in form.keys():
field_item = form[field]
if field_item.filename:
# The field contains an uploaded file
file_data = field_item.file.read()
file_len = len(file_data)
del file_data
self.wfile.write('\tUploaded %s (%d bytes)\n' % (field,file_len))
else:
# Regular form value
self.wfile.write('\t%s=%s\n' % (field, form[field].value))
pass
def main():
try:
server = Server(('', 81), MyHandler) # port 81 to avoid IIS, Apache, etc.
print "Welcome to Mike Warot's capability based security demo web server, version "+version
print 'You can access it at http://127.0.0.1:81'
print 'Use control-c to tell it to shut down, which may take up to 10 seconds'
print 'started httpserver...'
server.serve_forever()
except KeyboardInterrupt:
print '^C received, shutting down server'
server.socket.close()
if __name__ == '__main__':
main()