Commit d3940b31 authored by Xavier Thompson's avatar Xavier Thompson

simplehttpserver: Remove root-path option

parent 3c5efa14
...@@ -41,29 +41,18 @@ def issubpathof(subpath, path): ...@@ -41,29 +41,18 @@ def issubpathof(subpath, path):
class Recipe(GenericBaseRecipe): class Recipe(GenericBaseRecipe):
def __init__(self, buildout, name, options):
base_path = options['base-path']
root_path = options.get('root-path')
if root_path:
if not issubpathof(root_path, base_path):
raise UserError("root-path must be a subpath of base-path")
else:
root_path = base_path
self.server_parameters = {
'host': options['host'],
'port': int(options['port']),
'cwd': base_path,
'log-file': options['log-file'],
'cert-file': options.get('cert-file', ''),
'key-file': options.get('key-file', ''),
'root-path': root_path,
'allow-write': bool_option(options, 'allow-write', 'false')
}
return GenericBaseRecipe.__init__(self, buildout, name, options)
def install(self): def install(self):
parameters = {
'host': self.options['host'],
'port': int(self.options['port']),
'cwd': self.options['base-path'],
'log-file': self.options['log-file'],
'cert-file': self.options.get('cert-file', ''),
'key-file': self.options.get('key-file', ''),
'allow-write': bool_option(self.options, 'allow-write', 'false')
}
return self.createPythonScript( return self.createPythonScript(
self.options['wrapper'].strip(), self.options['wrapper'].strip(),
__name__ + '.simplehttpserver.run', __name__ + '.simplehttpserver.run',
(self.server_parameters,) (parameters,)
) )
...@@ -15,8 +15,6 @@ from . import issubpathof ...@@ -15,8 +15,6 @@ from . import issubpathof
class ServerHandler(SimpleHTTPRequestHandler): class ServerHandler(SimpleHTTPRequestHandler):
base_path = None # set by run base_path = None # set by run
root_path = None # set by run
restrict_path = False # set by run
restrict_write = True # set by run restrict_write = True # set by run
def respond(self, code=200, type='text/html'): def respond(self, code=200, type='text/html'):
...@@ -24,15 +22,7 @@ class ServerHandler(SimpleHTTPRequestHandler): ...@@ -24,15 +22,7 @@ class ServerHandler(SimpleHTTPRequestHandler):
self.send_header("Content-type", type) self.send_header("Content-type", type)
self.end_headers() self.end_headers()
def restrictedAccess(self): def restrictedWriteAccess(self):
if self.restrict_path:
path = os.path.join(self.base_path, os.path.relpath(self.path, '/'))
if not issubpathof(path, self.root_path):
logging.info('TOTO %s %s', path, self.root_path)
# no access outside root path
self.respond(403)
self.wfile.write(b"Forbidden")
return True
if self.restrict_write and self.command not in ('GET', 'HEAD'): if self.restrict_write and self.command not in ('GET', 'HEAD'):
# no write access # no write access
self.respond(403) self.respond(403)
...@@ -42,8 +32,6 @@ class ServerHandler(SimpleHTTPRequestHandler): ...@@ -42,8 +32,6 @@ class ServerHandler(SimpleHTTPRequestHandler):
def do_GET(self): def do_GET(self):
logging.info('%s - GET: %s \n%s' % (self.client_address[0], self.path, self.headers)) logging.info('%s - GET: %s \n%s' % (self.client_address[0], self.path, self.headers))
if self.restrictedAccess():
return
SimpleHTTPRequestHandler.do_GET(self) SimpleHTTPRequestHandler.do_GET(self)
def do_POST(self): def do_POST(self):
...@@ -57,7 +45,7 @@ class ServerHandler(SimpleHTTPRequestHandler): ...@@ -57,7 +45,7 @@ class ServerHandler(SimpleHTTPRequestHandler):
request can be encoded as application/x-www-form-urlencoded or multipart/form-data request can be encoded as application/x-www-form-urlencoded or multipart/form-data
""" """
logging.info('%s - POST: %s \n%s' % (self.client_address[0], self.path, self.headers)) logging.info('%s - POST: %s \n%s' % (self.client_address[0], self.path, self.headers))
if self.restrictedAccess(): if self.restrictedWriteAccess():
return return
form = cgi.FieldStorage( form = cgi.FieldStorage(
...@@ -80,9 +68,9 @@ class ServerHandler(SimpleHTTPRequestHandler): ...@@ -80,9 +68,9 @@ class ServerHandler(SimpleHTTPRequestHandler):
self.writeFile(file_path, file_content, file_open_mode) self.writeFile(file_path, file_content, file_open_mode)
def writeFile(self, filename, content, method='ab'): def writeFile(self, filename, content, method='ab'):
file_path = os.path.abspath(os.path.join(self.root_path, filename)) file_path = os.path.abspath(os.path.join(self.base_path, filename))
# Check writing there is allowed # Check writing there is allowed
if not issubpathof(file_path, self.root_path): if not issubpathof(file_path, self.base_path):
self.respond(403, 'text/plain') self.respond(403, 'text/plain')
self.wfile.write(b"Forbidden") self.wfile.write(b"Forbidden")
return return
...@@ -119,14 +107,11 @@ def run(args): ...@@ -119,14 +107,11 @@ def run(args):
port = args['port'] port = args['port']
host = args['host'] host = args['host']
cwd = args['cwd'] cwd = args['cwd']
root_path = args['root-path']
os.chdir(cwd) os.chdir(cwd)
Handler = ServerHandler Handler = ServerHandler
Handler.base_path = cwd Handler.base_path = cwd
Handler.root_path = root_path
Handler.restrict_path = (root_path != cwd)
Handler.restrict_write = not args['allow-write'] Handler.restrict_write = not args['allow-write']
if valid_ipv6(host): if valid_ipv6(host):
......
...@@ -73,13 +73,6 @@ class SimpleHTTPServerTest(unittest.TestCase): ...@@ -73,13 +73,6 @@ class SimpleHTTPServerTest(unittest.TestCase):
self.process.communicate() # close pipes self.process.communicate() # close pipes
self.process = None self.process = None
def test_options_valid_root_path(self):
self.assertRaises(
simplehttpserver.UserError,
self.setUpRecipe,
{'root-path': '/'}
)
def write_should_fail(self, url, hack_path, hack_content): def write_should_fail(self, url, hack_path, hack_content):
# post with multipart/form-data encoding # post with multipart/form-data encoding
resp = requests.post( resp = requests.post(
...@@ -114,39 +107,6 @@ class SimpleHTTPServerTest(unittest.TestCase): ...@@ -114,39 +107,6 @@ class SimpleHTTPServerTest(unittest.TestCase):
self.write_should_fail(server_base_url, hack_path, hack_content) self.write_should_fail(server_base_url, hack_path, hack_content)
self.assertFalse(os.path.exists(os.path.dirname(hack_path))) self.assertFalse(os.path.exists(os.path.dirname(hack_path)))
def test_write_outside_root_path_should_fail(self):
root_path = os.path.join(self.base_path, 'forbidden', 'allowed')
os.makedirs(root_path)
self.setUpRecipe({'allow-write': 'true', 'root-path': root_path})
server_base_url = self.startServer()
# A file outside the server's root directory
hack_path = os.path.join(self.base_path, 'forbidden', 'hack.txt')
hack_content = "You should not be able to write to hack.txt"
self.write_should_fail(server_base_url, hack_path, hack_content)
def test_access_outside_root_path_should_fail(self):
root_path = os.path.join(self.base_path, 'forbidden', 'allowed')
os.makedirs(root_path)
self.setUpRecipe({'root-path': root_path})
server_base_url = self.startServer()
forbiddenurl = os.path.join(server_base_url, 'forbidden')
resp = requests.get(forbiddenurl)
self.assertEqual(resp.status_code, requests.codes.forbidden)
self.assertEqual(resp.text, 'Forbidden')
resp = requests.post(
forbiddenurl,
files={
'path': 'index.txt',
'content': 'Not forbidden after all',
},
)
self.assertEqual(resp.status_code, requests.codes.forbidden)
def test_write(self): def test_write(self):
self.setUpRecipe({'allow-write': 'true'}) self.setUpRecipe({'allow-write': 'true'})
server_base_url = self.startServer() server_base_url = self.startServer()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment