staticweb: work in progress
This commit is contained in:
parent
6639b0ec0c
commit
469c287b5b
@ -58,6 +58,10 @@ def clean_acl(name, value):
|
||||
.r:
|
||||
.r:-
|
||||
|
||||
By default, allowing read access via .r will allow listing objects in the
|
||||
container as well as retrieving objects from the container. To turn off
|
||||
listings, use the .rnolisting directive.
|
||||
|
||||
Also, .r designations aren't allowed in headers whose names include the
|
||||
word 'write'.
|
||||
|
||||
@ -71,6 +75,7 @@ def clean_acl(name, value):
|
||||
``bob,,,sue`` ``bob,sue``
|
||||
``.referrer : *`` ``.r:*``
|
||||
``.ref:*.example.com`` ``.r:.example.com``
|
||||
``.r:*, .rnolisting`` ``.r:*,.rnolisting``
|
||||
====================== ======================
|
||||
|
||||
:param name: The name of the header being cleaned, such as X-Container-Read
|
||||
|
@ -21,36 +21,37 @@ except ImportError:
|
||||
import cgi
|
||||
import urllib
|
||||
|
||||
from webob import Response
|
||||
from webob import Response, Request
|
||||
from webob.exc import HTTPMovedPermanently, HTTPNotFound, HTTPUnauthorized
|
||||
|
||||
from swift.common.utils import split_path
|
||||
from swift.common.utils import split_path, TRUE_VALUES
|
||||
|
||||
|
||||
# To use:
|
||||
# Put the staticweb filter just after the auth filter.
|
||||
# Make the container publicly readable:
|
||||
# st post -r '.r:*' container
|
||||
# You should be able to get objects now, but not do listings.
|
||||
# Set an index file:
|
||||
# You should be able to get objects and do direct container listings
|
||||
# now, though they'll be in the REST API format.
|
||||
# Set an index file directive:
|
||||
# st post -m 'index:index.html' container
|
||||
# You should be able to hit path's that have an index.html without needing to
|
||||
# type the index.html part, but still not do listings.
|
||||
# Turn on listings:
|
||||
# st post -m 'index:allow_listings,index.html' container
|
||||
# You should be able to hit paths that have an index.html without
|
||||
# needing to type the index.html part and listings will now be HTML.
|
||||
# Turn off listings:
|
||||
# st post -r '.r:*,.rnolisting' container
|
||||
# Set an error file:
|
||||
# st post -m 'error:error.html' container
|
||||
# Now 404's should load 404error.html
|
||||
|
||||
# TODO: Tests
|
||||
# TODO: Docs
|
||||
# TODO: Make a disallow_listings to restrict direct container listings. These
|
||||
# have to stay on by default because of swauth and any other similar
|
||||
# middleware. The lower level indirect listings can stay disabled by
|
||||
# default.
|
||||
# TODO: Accept header negotiation: make static web work with authed as well
|
||||
# TODO: get_container_info can be memcached
|
||||
# TODO: Blueprint
|
||||
# Now 401's should load s 401error.html, 404's should load
|
||||
# 404error.html, etc.
|
||||
#
|
||||
# This mode is normally only active for anonymous requests. If you
|
||||
# want to use it with authenticated requests, set the X-Web-Mode:
|
||||
# true header.
|
||||
#
|
||||
# TODO: Tests.
|
||||
# TODO: Docs.
|
||||
# TODO: get_container_info can be memcached.
|
||||
# TODO: Blueprint.
|
||||
|
||||
class StaticWeb(object):
|
||||
|
||||
@ -68,12 +69,22 @@ class StaticWeb(object):
|
||||
start_response(self.response_status, self.response_headers,
|
||||
self.response_exc_info)
|
||||
return response
|
||||
save_response_status = self.response_status
|
||||
save_response_headers = self.response_headers
|
||||
save_response_exc_info = self.response_exc_info
|
||||
tmp_env = dict(env)
|
||||
self.strip_ifs(tmp_env)
|
||||
tmp_env['PATH_INFO'] = '/%s/%s/%s/%s%s' % (self.version, self.account,
|
||||
self.container, self.get_status_int(), self.error)
|
||||
tmp_env['REQUEST_METHOD'] = 'GET'
|
||||
return self.app(tmp_env, start_response)
|
||||
resp = self.app(tmp_env, self.start_response)
|
||||
if self.get_status_int() // 100 == 2:
|
||||
start_response(self.response_status, self.response_headers,
|
||||
self.response_exc_info)
|
||||
return resp
|
||||
start_response(save_response_status, save_response_headers,
|
||||
save_response_exc_info)
|
||||
return response
|
||||
|
||||
def get_status_int(self):
|
||||
return int(self.response_status.split(' ', 1)[0])
|
||||
@ -90,26 +101,16 @@ class StaticWeb(object):
|
||||
|
||||
def get_container_info(self, env, start_response):
|
||||
self.index = self.error = None
|
||||
self.index_allow_listings = False
|
||||
tmp_env = dict(env)
|
||||
self.strip_ifs(tmp_env)
|
||||
tmp_env['REQUEST_METHOD'] = 'HEAD'
|
||||
tmp_env['PATH_INFO'] = \
|
||||
'/%s/%s/%s' % (self.version, self.account, self.container)
|
||||
resp = self.app(tmp_env, self.start_response)
|
||||
if self.get_status_int() // 100 != 2:
|
||||
return
|
||||
self.index = self.get_header(self.response_headers,
|
||||
'x-container-meta-index', '').strip()
|
||||
self.error = self.get_header(self.response_headers,
|
||||
'x-container-meta-error', '').strip()
|
||||
if not self.index:
|
||||
return
|
||||
if self.index.lower() == 'allow_listings':
|
||||
self.index_allow_listings = True
|
||||
elif self.index.lower().startswith('allow_listings,'):
|
||||
self.index = self.index[len('allow_listings,'):]
|
||||
self.index_allow_listings = True
|
||||
tmp_env = {'REQUEST_METHOD': 'HEAD', 'HTTP_USER_AGENT': 'StaticWeb'}
|
||||
for name in ('swift.cache', 'HTTP_X_CF_TRANS_ID'):
|
||||
if name in env:
|
||||
tmp_env[name] = env[name]
|
||||
req = Request.blank('/%s/%s/%s' % (self.version, self.account,
|
||||
self.container), environ=tmp_env)
|
||||
resp = req.get_response(self.app)
|
||||
if resp.status_int // 100 == 2:
|
||||
self.index = resp.headers.get('x-container-meta-index', '').strip()
|
||||
self.error = resp.headers.get('x-container-meta-error', '').strip()
|
||||
|
||||
def listing(self, env, start_response, prefix=None):
|
||||
tmp_env = dict(env)
|
||||
@ -152,18 +153,16 @@ class StaticWeb(object):
|
||||
|
||||
def handle_container(self, env, start_response):
|
||||
self.get_container_info(env, start_response)
|
||||
if not self.index and not self.index_allow_listings:
|
||||
if not self.index:
|
||||
return self.app(env, start_response)
|
||||
if env['PATH_INFO'][-1] != '/':
|
||||
return HTTPMovedPermanently(
|
||||
location=env['PATH_INFO'] + '/')(env, start_response)
|
||||
if not self.index and self.index_allow_listings:
|
||||
return self.listing(env, start_response)
|
||||
tmp_env = dict(env)
|
||||
tmp_env['PATH_INFO'] += self.index
|
||||
resp = self.app(tmp_env, self.start_response)
|
||||
status_int = self.get_status_int()
|
||||
if status_int == 404 and self.index_allow_listings:
|
||||
if status_int == 404:
|
||||
return self.listing(env, start_response)
|
||||
elif self.get_status_int() // 100 not in (2, 3):
|
||||
return self.error_response(resp, env, start_response)
|
||||
@ -182,44 +181,43 @@ class StaticWeb(object):
|
||||
if status_int != 404:
|
||||
return self.error_response(resp, env, start_response)
|
||||
self.get_container_info(env, start_response)
|
||||
if not self.index and not self.index_allow_listings:
|
||||
if not self.index:
|
||||
return self.app(env, start_response)
|
||||
if self.index:
|
||||
tmp_env = dict(env)
|
||||
if tmp_env['PATH_INFO'][-1] != '/':
|
||||
tmp_env['PATH_INFO'] += '/'
|
||||
tmp_env['PATH_INFO'] += self.index
|
||||
resp = self.app(tmp_env, self.start_response)
|
||||
status_int = self.get_status_int()
|
||||
if status_int // 100 in (2, 3):
|
||||
if env['PATH_INFO'][-1] != '/':
|
||||
return HTTPMovedPermanently(
|
||||
location=env['PATH_INFO'] + '/')(env, start_response)
|
||||
start_response(self.response_status, self.response_headers,
|
||||
self.response_exc_info)
|
||||
return resp
|
||||
elif status_int == 404 and self.index_allow_listings:
|
||||
if env['PATH_INFO'][-1] != '/':
|
||||
tmp_env = dict(env)
|
||||
self.strip_ifs(tmp_env)
|
||||
tmp_env['REQUEST_METHOD'] = 'GET'
|
||||
tmp_env['PATH_INFO'] = '/%s/%s/%s' % (self.version,
|
||||
self.account, self.container)
|
||||
tmp_env['QUERY_STRING'] = 'limit=1&format=json&delimiter' \
|
||||
'=/&limit=1&prefix=%s' % urllib.quote(self.obj + '/')
|
||||
resp = self.app(tmp_env, self.start_response)
|
||||
if self.get_status_int() // 100 != 2 or \
|
||||
not json.loads(''.join(resp)):
|
||||
resp = HTTPNotFound()(env, self.start_response)
|
||||
return self.error_response(resp, env, start_response)
|
||||
return HTTPMovedPermanently(location=env['PATH_INFO'] +
|
||||
'/')(env, start_response)
|
||||
return self.listing(env, start_response, self.obj)
|
||||
return self.app(env, start_response)
|
||||
tmp_env = dict(env)
|
||||
if tmp_env['PATH_INFO'][-1] != '/':
|
||||
tmp_env['PATH_INFO'] += '/'
|
||||
tmp_env['PATH_INFO'] += self.index
|
||||
resp = self.app(tmp_env, self.start_response)
|
||||
status_int = self.get_status_int()
|
||||
if status_int // 100 in (2, 3):
|
||||
if env['PATH_INFO'][-1] != '/':
|
||||
return HTTPMovedPermanently(
|
||||
location=env['PATH_INFO'] + '/')(env, start_response)
|
||||
start_response(self.response_status, self.response_headers,
|
||||
self.response_exc_info)
|
||||
return resp
|
||||
elif status_int == 404:
|
||||
if env['PATH_INFO'][-1] != '/':
|
||||
tmp_env = dict(env)
|
||||
self.strip_ifs(tmp_env)
|
||||
tmp_env['REQUEST_METHOD'] = 'GET'
|
||||
tmp_env['PATH_INFO'] = '/%s/%s/%s' % (self.version,
|
||||
self.account, self.container)
|
||||
tmp_env['QUERY_STRING'] = 'limit=1&format=json&delimiter' \
|
||||
'=/&limit=1&prefix=%s' % urllib.quote(self.obj + '/')
|
||||
resp = self.app(tmp_env, self.start_response)
|
||||
if self.get_status_int() // 100 != 2 or \
|
||||
not json.loads(''.join(resp)):
|
||||
resp = HTTPNotFound()(env, self.start_response)
|
||||
return self.error_response(resp, env, start_response)
|
||||
return HTTPMovedPermanently(location=env['PATH_INFO'] +
|
||||
'/')(env, start_response)
|
||||
return self.listing(env, start_response, self.obj)
|
||||
|
||||
def __call__(self, env, start_response):
|
||||
if env.get('REMOTE_USER') or \
|
||||
env['REQUEST_METHOD'] not in ('HEAD', 'GET'):
|
||||
if env['REQUEST_METHOD'] not in ('HEAD', 'GET') or \
|
||||
(env.get('REMOTE_USER') and
|
||||
not env.get('HTTP_X_WEB_MODE', '') in TRUE_VALUES):
|
||||
return self.app(env, start_response)
|
||||
(self.version, self.account, self.container, self.obj) = \
|
||||
split_path(env['PATH_INFO'], 2, 4, True)
|
||||
|
@ -277,6 +277,8 @@ class Swauth(object):
|
||||
return None
|
||||
referrers, groups = parse_acl(getattr(req, 'acl', None))
|
||||
if referrer_allowed(req.referer, referrers):
|
||||
if not obj and '.rnolisting' in groups:
|
||||
return HTTPUnauthorized(request=req)
|
||||
return None
|
||||
if not req.remote_user:
|
||||
return self.denied_response(req)
|
||||
@ -1179,7 +1181,7 @@ class Swauth(object):
|
||||
|
||||
:returns: webob.Request object
|
||||
"""
|
||||
newenv = {'REQUEST_METHOD': method}
|
||||
newenv = {'REQUEST_METHOD': method, 'HTTP_USER_AGENT': 'Swauth'}
|
||||
for name in ('swift.cache', 'HTTP_X_CF_TRANS_ID'):
|
||||
if name in env:
|
||||
newenv[name] = env[name]
|
||||
|
Loading…
x
Reference in New Issue
Block a user