Merge "Added discoverable capabilities."
This commit is contained in:
commit
34b4bf34d2
@ -15,7 +15,7 @@
|
||||
import hmac
|
||||
from hashlib import sha1
|
||||
from os.path import basename
|
||||
from sys import argv, exit
|
||||
from sys import argv, exit, stderr
|
||||
from time import time
|
||||
|
||||
|
||||
@ -59,9 +59,11 @@ if __name__ == '__main__':
|
||||
# have '/'s.
|
||||
if len(parts) != 5 or parts[0] or parts[1] != 'v1' or not parts[2] or \
|
||||
not parts[3] or not parts[4].strip('/'):
|
||||
print '<path> must point to an object.'
|
||||
print 'For example: /v1/account/container/object'
|
||||
exit(1)
|
||||
stderr.write(
|
||||
'WARNING: "%s" does not refer to an object '
|
||||
'(e.g. /v1/account/container/object).\n' % path)
|
||||
stderr.write(
|
||||
'WARNING: Non-object paths will be rejected by tempurl.\n')
|
||||
sig = hmac.new(key, '%s\n%s\n%s' % (method, expires, path),
|
||||
sha1).hexdigest()
|
||||
print '%s?temp_url_sig=%s&temp_url_expires=%s' % (path, sig, expires)
|
||||
|
@ -216,3 +216,22 @@ List Endpoints
|
||||
.. automodule:: swift.common.middleware.list_endpoints
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
Discoverability
|
||||
===============
|
||||
|
||||
Swift can optionally be configured to provide clients with an interface
|
||||
providing details about the installation. If configured, a GET request to
|
||||
/info will return configuration data in JSON format. An example
|
||||
response::
|
||||
|
||||
{"swift": {"version": "1.8.1"}, "staticweb": {}, "tempurl": {}}
|
||||
|
||||
This would signify to the client that swift version 1.8.1 is running and that
|
||||
staticweb and tempurl are available in this installation.
|
||||
|
||||
There may be administrator-only information available via /info. To
|
||||
retrieve it, one must use an HMAC-signed request, similar to TempURL.
|
||||
The signature may be produced like so:
|
||||
|
||||
swift-temp-url GET 3600 /info secret 2>/dev/null | sed s/temp_url/swiftinfo/g
|
||||
|
@ -5,7 +5,20 @@
|
||||
# backlog = 4096
|
||||
# swift_dir = /etc/swift
|
||||
# user = swift
|
||||
|
||||
# Enables exposing configuration settings via HTTP GET /info.
|
||||
# expose_info = true
|
||||
|
||||
# Key to use for admin calls that are HMAC signed. Default is empty,
|
||||
# which will disable admin calls to /info.
|
||||
# admin_key = secret_admin_key
|
||||
#
|
||||
# Allows the ability to withhold sections from showing up in the public
|
||||
# calls to /info. The following would cause the sections 'container_quotas'
|
||||
# and 'tempurl' to not be listed. Default is empty, allowing all registered
|
||||
# fetures to be listed via HTTP GET /info.
|
||||
# disallowed_sections = container_quotas, tempurl
|
||||
|
||||
# Use an integer to override the number of pre-forked processes that will
|
||||
# accept connections. Should default to the number of effective cpu
|
||||
# cores in the system. It's worth noting that individual workers will
|
||||
|
@ -48,6 +48,7 @@ post -m quota-bytes:
|
||||
|
||||
from swift.common.swob import HTTPForbidden, HTTPRequestEntityTooLarge, \
|
||||
HTTPBadRequest, wsgify
|
||||
from swift.common.utils import register_swift_info
|
||||
from swift.proxy.controllers.base import get_account_info, get_object_info
|
||||
|
||||
|
||||
@ -132,6 +133,8 @@ class AccountQuotaMiddleware(object):
|
||||
|
||||
def filter_factory(global_conf, **local_conf):
|
||||
"""Returns a WSGI filter app for use with paste.deploy."""
|
||||
register_swift_info('account_quotas')
|
||||
|
||||
def account_quota_filter(app):
|
||||
return AccountQuotaMiddleware(app)
|
||||
return account_quota_filter
|
||||
|
@ -22,7 +22,7 @@ from swift.common.swob import Request, HTTPBadGateway, \
|
||||
HTTPCreated, HTTPBadRequest, HTTPNotFound, HTTPUnauthorized, HTTPOk, \
|
||||
HTTPPreconditionFailed, HTTPRequestEntityTooLarge, HTTPNotAcceptable, \
|
||||
HTTPLengthRequired, HTTPException, HTTPServerError, wsgify
|
||||
from swift.common.utils import json, get_logger
|
||||
from swift.common.utils import json, get_logger, register_swift_info
|
||||
from swift.common.constraints import check_utf8, MAX_FILE_SIZE
|
||||
from swift.common.http import HTTP_UNAUTHORIZED, HTTP_NOT_FOUND
|
||||
from swift.common.constraints import MAX_OBJECT_NAME_LENGTH, \
|
||||
@ -542,6 +542,7 @@ class Bulk(object):
|
||||
def filter_factory(global_conf, **local_conf):
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
register_swift_info('bulk')
|
||||
|
||||
def bulk_filter(app):
|
||||
return Bulk(app, conf)
|
||||
|
@ -43,8 +43,9 @@ set:
|
||||
"""
|
||||
|
||||
from swift.common.http import is_success
|
||||
from swift.proxy.controllers.base import get_container_info, get_object_info
|
||||
from swift.common.swob import Response, HTTPBadRequest, wsgify
|
||||
from swift.common.utils import register_swift_info
|
||||
from swift.proxy.controllers.base import get_container_info, get_object_info
|
||||
|
||||
|
||||
class ContainerQuotaMiddleware(object):
|
||||
@ -113,6 +114,8 @@ class ContainerQuotaMiddleware(object):
|
||||
|
||||
|
||||
def filter_factory(global_conf, **local_conf):
|
||||
register_swift_info('container_quotas')
|
||||
|
||||
def container_quota_filter(app):
|
||||
return ContainerQuotaMiddleware(app)
|
||||
return container_quota_filter
|
||||
|
@ -14,6 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
from swift.common.swob import Request, Response
|
||||
from swift.common.utils import register_swift_info
|
||||
|
||||
|
||||
class CrossDomainMiddleware(object):
|
||||
@ -84,6 +85,7 @@ class CrossDomainMiddleware(object):
|
||||
def filter_factory(global_conf, **local_conf):
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
register_swift_info('crossdomain')
|
||||
|
||||
def crossdomain_filter(app):
|
||||
return CrossDomainMiddleware(app, conf)
|
||||
|
@ -110,7 +110,7 @@ from time import time
|
||||
from urllib import quote
|
||||
|
||||
from swift.common.middleware.tempurl import get_tempurl_keys_from_metadata
|
||||
from swift.common.utils import streq_const_time
|
||||
from swift.common.utils import streq_const_time, register_swift_info
|
||||
from swift.common.wsgi import make_pre_authed_env
|
||||
from swift.common.swob import HTTPUnauthorized
|
||||
from swift.proxy.controllers.base import get_account_info
|
||||
@ -502,4 +502,5 @@ def filter_factory(global_conf, **local_conf):
|
||||
"""Returns the WSGI filter for use with paste.deploy."""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
register_swift_info('formpost')
|
||||
return lambda app: FormPost(app, conf)
|
||||
|
@ -15,6 +15,7 @@
|
||||
from swift.common import utils as swift_utils
|
||||
from swift.common.middleware import acl as swift_acl
|
||||
from swift.common.swob import HTTPNotFound, HTTPForbidden, HTTPUnauthorized
|
||||
from swift.common.utils import register_swift_info
|
||||
|
||||
|
||||
class KeystoneAuth(object):
|
||||
@ -334,6 +335,7 @@ def filter_factory(global_conf, **local_conf):
|
||||
"""Returns a WSGI filter app for use with paste.deploy."""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
register_swift_info('keystoneauth')
|
||||
|
||||
def auth_filter(app):
|
||||
return KeystoneAuth(app, conf)
|
||||
|
@ -17,7 +17,7 @@ from swift import gettext_ as _
|
||||
|
||||
import eventlet
|
||||
|
||||
from swift.common.utils import cache_from_env, get_logger
|
||||
from swift.common.utils import cache_from_env, get_logger, register_swift_info
|
||||
from swift.proxy.controllers.base import get_container_memcache_key
|
||||
from swift.common.memcached import MemcacheConnectionError
|
||||
from swift.common.swob import Request, Response
|
||||
@ -274,6 +274,7 @@ def filter_factory(global_conf, **local_conf):
|
||||
"""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
register_swift_info('ratelimit')
|
||||
|
||||
def limit_filter(app):
|
||||
return RateLimitMiddleware(app, conf)
|
||||
|
@ -143,7 +143,8 @@ from swift.common.swob import Request, HTTPBadRequest, HTTPServerError, \
|
||||
HTTPMethodNotAllowed, HTTPRequestEntityTooLarge, HTTPLengthRequired, \
|
||||
HTTPOk, HTTPPreconditionFailed, HTTPException, HTTPNotFound, \
|
||||
HTTPUnauthorized
|
||||
from swift.common.utils import json, get_logger, config_true_value
|
||||
from swift.common.utils import (json, get_logger, config_true_value,
|
||||
register_swift_info)
|
||||
from swift.common.constraints import check_utf8, MAX_BUFFERED_SLO_SEGMENTS
|
||||
from swift.common.http import HTTP_NOT_FOUND, HTTP_UNAUTHORIZED
|
||||
from swift.common.wsgi import WSGIContext
|
||||
@ -461,6 +462,7 @@ class StaticLargeObject(object):
|
||||
def filter_factory(global_conf, **local_conf):
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
register_swift_info('slo')
|
||||
|
||||
def slo_filter(app):
|
||||
return StaticLargeObject(app, conf)
|
||||
|
@ -120,7 +120,7 @@ import cgi
|
||||
import time
|
||||
|
||||
from swift.common.utils import human_readable, split_path, config_true_value, \
|
||||
json, quote, get_valid_utf8_str
|
||||
json, quote, get_valid_utf8_str, register_swift_info
|
||||
from swift.common.wsgi import make_pre_authed_env, WSGIContext
|
||||
from swift.common.http import is_success, is_redirection, HTTP_NOT_FOUND
|
||||
from swift.common.swob import Response, HTTPMovedPermanently, HTTPNotFound
|
||||
@ -468,6 +468,7 @@ def filter_factory(global_conf, **local_conf):
|
||||
"""Returns a Static Web WSGI filter for use with paste.deploy."""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
register_swift_info('staticweb')
|
||||
|
||||
def staticweb_filter(app):
|
||||
return StaticWeb(app, conf)
|
||||
|
@ -28,7 +28,7 @@ from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
|
||||
|
||||
from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed
|
||||
from swift.common.utils import cache_from_env, get_logger, \
|
||||
split_path, config_true_value
|
||||
split_path, config_true_value, register_swift_info
|
||||
|
||||
|
||||
class TempAuth(object):
|
||||
@ -510,6 +510,7 @@ def filter_factory(global_conf, **local_conf):
|
||||
"""Returns a WSGI filter app for use with paste.deploy."""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
register_swift_info('tempauth')
|
||||
|
||||
def auth_filter(app):
|
||||
return TempAuth(app, conf)
|
||||
|
@ -89,8 +89,6 @@ __all__ = ['TempURL', 'filter_factory',
|
||||
'DEFAULT_OUTGOING_ALLOW_HEADERS']
|
||||
|
||||
|
||||
import hmac
|
||||
from hashlib import sha1
|
||||
from os.path import basename
|
||||
from time import time
|
||||
from urllib import urlencode
|
||||
@ -98,7 +96,8 @@ from urlparse import parse_qs
|
||||
|
||||
from swift.proxy.controllers.base import get_account_info
|
||||
from swift.common.swob import HeaderKeyDict, HTTPUnauthorized
|
||||
from swift.common.utils import split_path, get_valid_utf8_str
|
||||
from swift.common.utils import split_path, get_valid_utf8_str, \
|
||||
register_swift_info, get_hmac
|
||||
|
||||
|
||||
#: Default headers to remove from incoming requests. Simply a whitespace
|
||||
@ -389,31 +388,10 @@ class TempURL(object):
|
||||
:param keys: Key strings, from the X-Account-Meta-Temp-URL-Key[-2] of
|
||||
the account.
|
||||
"""
|
||||
return [self._get_hmac(env, expires, key, request_method)
|
||||
for key in keys]
|
||||
|
||||
def _get_hmac(self, env, expires, key, request_method=None):
|
||||
"""
|
||||
Returns the hexdigest string of the HMAC-SHA1 (RFC 2104) for
|
||||
the request.
|
||||
|
||||
:param env: The WSGI environment for the request.
|
||||
:param expires: Unix timestamp as an int for when the URL
|
||||
expires.
|
||||
:param key: Key str, from the X-Account-Meta-Temp-URL-Key of
|
||||
the account.
|
||||
:param request_method: Optional override of the request in
|
||||
the WSGI env. For example, if a HEAD
|
||||
does not match, you may wish to
|
||||
override with GET to still allow the
|
||||
HEAD.
|
||||
:returns: hexdigest str of the HMAC-SHA1 for the request.
|
||||
"""
|
||||
if not request_method:
|
||||
request_method = env['REQUEST_METHOD']
|
||||
return hmac.new(
|
||||
key, '%s\n%s\n%s' % (request_method, expires,
|
||||
env['PATH_INFO']), sha1).hexdigest()
|
||||
return [get_hmac(
|
||||
request_method, env['PATH_INFO'], expires, key) for key in keys]
|
||||
|
||||
def _invalid(self, env, start_response):
|
||||
"""
|
||||
@ -492,4 +470,5 @@ def filter_factory(global_conf, **local_conf):
|
||||
"""Returns the WSGI filter for use with paste.deploy."""
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
register_swift_info('tempurl')
|
||||
return lambda app: TempURL(app, conf)
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
import errno
|
||||
import fcntl
|
||||
import hmac
|
||||
import operator
|
||||
import os
|
||||
import pwd
|
||||
@ -26,7 +27,7 @@ import threading as stdlib_threading
|
||||
import time
|
||||
import uuid
|
||||
import functools
|
||||
from hashlib import md5
|
||||
from hashlib import md5, sha1
|
||||
from random import random, shuffle
|
||||
from urllib import quote as _quote
|
||||
from contextlib import contextmanager, closing
|
||||
@ -100,6 +101,78 @@ if hash_conf.read('/etc/swift/swift.conf'):
|
||||
pass
|
||||
|
||||
|
||||
def get_hmac(request_method, path, expires, key):
|
||||
"""
|
||||
Returns the hexdigest string of the HMAC-SHA1 (RFC 2104) for
|
||||
the request.
|
||||
|
||||
:param request_method: Request method to allow.
|
||||
:param path: The path to the resource to allow access to.
|
||||
:param expires: Unix timestamp as an int for when the URL
|
||||
expires.
|
||||
:param key: HMAC shared secret.
|
||||
|
||||
:returns: hexdigest str of the HMAC-SHA1 for the request.
|
||||
"""
|
||||
return hmac.new(
|
||||
key, '%s\n%s\n%s' % (request_method, expires, path), sha1).hexdigest()
|
||||
|
||||
|
||||
# Used by get_swift_info and register_swift_info to store information about
|
||||
# the swift cluster.
|
||||
_swift_info = {}
|
||||
_swift_admin_info = {}
|
||||
|
||||
|
||||
def get_swift_info(admin=False, disallowed_sections=None):
|
||||
"""
|
||||
Returns information about the swift cluster that has been previously
|
||||
registered with the register_swift_info call.
|
||||
|
||||
:param admin: boolean value, if True will additionally return an 'admin'
|
||||
section with information previously registered as admin
|
||||
info.
|
||||
:param disallowed_sections: list of section names to be withheld from the
|
||||
information returned.
|
||||
:returns: dictionary of information about the swift cluster.
|
||||
"""
|
||||
disallowed_sections = disallowed_sections or []
|
||||
info = {}
|
||||
for section in _swift_info:
|
||||
if section in disallowed_sections:
|
||||
continue
|
||||
info[section] = dict(_swift_info[section].items())
|
||||
if admin:
|
||||
info['admin'] = dict(_swift_admin_info)
|
||||
info['admin']['disallowed_sections'] = list(disallowed_sections)
|
||||
return info
|
||||
|
||||
|
||||
def register_swift_info(name='swift', admin=False, **kwargs):
|
||||
"""
|
||||
Registers information about the swift cluster to be retrieved with calls
|
||||
to get_swift_info.
|
||||
|
||||
:param name: string, the section name to place the information under.
|
||||
:param admin: boolean, if True, information will be registered to an
|
||||
admin section which can optionally be withheld when
|
||||
requesting the information.
|
||||
:param kwargs: key value arguments representing the information to be
|
||||
added.
|
||||
"""
|
||||
if name == 'admin' or name == 'disallowed_sections':
|
||||
raise ValueError('\'{0}\' is reserved name.'.format(name))
|
||||
|
||||
if admin:
|
||||
dict_to_use = _swift_admin_info
|
||||
else:
|
||||
dict_to_use = _swift_info
|
||||
if name not in dict_to_use:
|
||||
dict_to_use[name] = {}
|
||||
for key, val in kwargs.iteritems():
|
||||
dict_to_use[name][key] = val
|
||||
|
||||
|
||||
def backward(f, blocksize=4096):
|
||||
"""
|
||||
A generator returning lines from a file starting with the last line,
|
||||
|
@ -12,6 +12,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
from swift.proxy.controllers.base import Controller
|
||||
from swift.proxy.controllers.info import InfoController
|
||||
from swift.proxy.controllers.obj import ObjectController
|
||||
from swift.proxy.controllers.account import AccountController
|
||||
from swift.proxy.controllers.container import ContainerController
|
||||
@ -20,5 +21,6 @@ __all__ = [
|
||||
'AccountController',
|
||||
'ContainerController',
|
||||
'Controller',
|
||||
'InfoController',
|
||||
'ObjectController',
|
||||
]
|
||||
|
100
swift/proxy/controllers/info.py
Normal file
100
swift/proxy/controllers/info.py
Normal file
@ -0,0 +1,100 @@
|
||||
# Copyright (c) 2010-2012 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from time import time
|
||||
|
||||
from swift.common.utils import public, get_hmac, get_swift_info, json
|
||||
from swift.proxy.controllers.base import Controller, delay_denial
|
||||
from swift.common.swob import HTTPOk, HTTPForbidden, HTTPUnauthorized
|
||||
|
||||
|
||||
class InfoController(Controller):
|
||||
"""WSGI controller for info requests"""
|
||||
server_type = 'Info'
|
||||
|
||||
def __init__(self, app, version, expose_info, disallowed_sections,
|
||||
admin_key):
|
||||
Controller.__init__(self, app)
|
||||
self.expose_info = expose_info
|
||||
self.disallowed_sections = disallowed_sections
|
||||
self.admin_key = admin_key
|
||||
self.allowed_hmac_methods = {
|
||||
'HEAD': ['HEAD', 'GET'],
|
||||
'GET': ['GET']}
|
||||
|
||||
@public
|
||||
@delay_denial
|
||||
def GET(self, req):
|
||||
return self.GETorHEAD(req)
|
||||
|
||||
@public
|
||||
@delay_denial
|
||||
def HEAD(self, req):
|
||||
return self.GETorHEAD(req)
|
||||
|
||||
@public
|
||||
@delay_denial
|
||||
def OPTIONS(self, req):
|
||||
return HTTPOk(request=req, headers={'Allow': 'HEAD, GET, OPTIONS'})
|
||||
|
||||
def GETorHEAD(self, req):
|
||||
"""Handler for HTTP GET/HEAD requests."""
|
||||
"""
|
||||
Handles requests to /info
|
||||
Should return a WSGI-style callable (such as swob.Response).
|
||||
|
||||
:param req: swob.Request object
|
||||
"""
|
||||
if not self.expose_info:
|
||||
return HTTPForbidden(request=req)
|
||||
|
||||
admin_request = False
|
||||
sig = req.params.get('swiftinfo_sig', '')
|
||||
expires = req.params.get('swiftinfo_expires', '')
|
||||
|
||||
if sig != '' or expires != '':
|
||||
admin_request = True
|
||||
if not self.admin_key:
|
||||
return HTTPForbidden(request=req)
|
||||
try:
|
||||
expires = int(expires)
|
||||
except ValueError:
|
||||
return HTTPUnauthorized(request=req)
|
||||
if expires < time():
|
||||
return HTTPUnauthorized(request=req)
|
||||
|
||||
valid_sigs = []
|
||||
for method in self.allowed_hmac_methods[req.method]:
|
||||
valid_sigs.append(get_hmac(method,
|
||||
'/info',
|
||||
expires,
|
||||
self.admin_key))
|
||||
|
||||
if sig not in valid_sigs:
|
||||
return HTTPUnauthorized(request=req)
|
||||
|
||||
headers = {}
|
||||
if 'Origin' in req.headers:
|
||||
headers['Access-Control-Allow-Origin'] = req.headers['Origin']
|
||||
headers['Access-Control-Expose-Headers'] = ', '.join(
|
||||
['x-trans-id'])
|
||||
|
||||
info = json.dumps(get_swift_info(
|
||||
admin=admin_request, disallowed_sections=self.disallowed_sections))
|
||||
|
||||
return HTTPOk(request=req,
|
||||
headers=headers,
|
||||
body=info,
|
||||
content_type='application/json; charset=UTF-8')
|
@ -23,13 +23,15 @@ import itertools
|
||||
|
||||
from eventlet import Timeout
|
||||
|
||||
from swift import __canonical_version__ as swift_version
|
||||
from swift.common.ring import Ring
|
||||
from swift.common.utils import cache_from_env, get_logger, \
|
||||
get_remote_client, split_path, config_true_value, generate_trans_id, \
|
||||
affinity_key_function, affinity_locality_predicate
|
||||
affinity_key_function, affinity_locality_predicate, list_from_csv, \
|
||||
register_swift_info
|
||||
from swift.common.constraints import check_utf8
|
||||
from swift.proxy.controllers import AccountController, ObjectController, \
|
||||
ContainerController
|
||||
ContainerController, InfoController
|
||||
from swift.common.swob import HTTPBadRequest, HTTPForbidden, \
|
||||
HTTPMethodNotAllowed, HTTPNotFound, HTTPPreconditionFailed, \
|
||||
HTTPServerError, HTTPException, Request
|
||||
@ -164,6 +166,12 @@ class Application(object):
|
||||
# ** Because it affects the client as well, currently, we use the
|
||||
# client chunk size as the govenor and not the object chunk size.
|
||||
socket._fileobject.default_bufsize = self.client_chunk_size
|
||||
self.expose_info = config_true_value(
|
||||
conf.get('expose_info', 'yes'))
|
||||
self.disallowed_sections = list_from_csv(
|
||||
conf.get('disallowed_sections'))
|
||||
self.admin_key = conf.get('admin_key', None)
|
||||
register_swift_info(version=swift_version)
|
||||
|
||||
def get_controller(self, path):
|
||||
"""
|
||||
@ -174,6 +182,13 @@ class Application(object):
|
||||
|
||||
:raises: ValueError (thrown by split_path) if given invalid path
|
||||
"""
|
||||
if path == '/info':
|
||||
d = dict(version=None,
|
||||
expose_info=self.expose_info,
|
||||
disallowed_sections=self.disallowed_sections,
|
||||
admin_key=self.admin_key)
|
||||
return InfoController, d
|
||||
|
||||
version, account, container, obj = split_path(path, 1, 4, True)
|
||||
d = dict(version=version,
|
||||
account_name=account,
|
||||
|
@ -687,17 +687,17 @@ class TestTempURL(unittest.TestCase):
|
||||
s, e)}),
|
||||
(s, 0, None, None))
|
||||
|
||||
def test_get_hmac(self):
|
||||
def test_get_hmacs(self):
|
||||
self.assertEquals(
|
||||
self.tempurl._get_hmac(
|
||||
self.tempurl._get_hmacs(
|
||||
{'REQUEST_METHOD': 'GET', 'PATH_INFO': '/v1/a/c/o'},
|
||||
1, 'abc'),
|
||||
'026d7f7cc25256450423c7ad03fc9f5ffc1dab6d')
|
||||
1, ['abc']),
|
||||
['026d7f7cc25256450423c7ad03fc9f5ffc1dab6d'])
|
||||
self.assertEquals(
|
||||
self.tempurl._get_hmac(
|
||||
self.tempurl._get_hmacs(
|
||||
{'REQUEST_METHOD': 'HEAD', 'PATH_INFO': '/v1/a/c/o'},
|
||||
1, 'abc', request_method='GET'),
|
||||
'026d7f7cc25256450423c7ad03fc9f5ffc1dab6d')
|
||||
1, ['abc'], request_method='GET'),
|
||||
['026d7f7cc25256450423c7ad03fc9f5ffc1dab6d'])
|
||||
|
||||
def test_invalid(self):
|
||||
|
||||
|
@ -1662,6 +1662,159 @@ log_name = %(yarr)s'''
|
||||
self.assertEquals('abc_%EF%BF%BD%EF%BF%BD%EC%BC%9D%EF%BF%BD',
|
||||
utils.quote(invalid_utf8_str))
|
||||
|
||||
def test_get_hmac(self):
|
||||
self.assertEquals(
|
||||
utils.get_hmac('GET', '/path', 1, 'abc'),
|
||||
'b17f6ff8da0e251737aa9e3ee69a881e3e092e2f')
|
||||
|
||||
|
||||
class TestSwiftInfo(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
utils._swift_info = {}
|
||||
utils._swift_admin_info = {}
|
||||
|
||||
def test_register_swift_info(self):
|
||||
utils.register_swift_info(foo='bar')
|
||||
utils.register_swift_info(lorem='ipsum')
|
||||
utils.register_swift_info('cap1', cap1_foo='cap1_bar')
|
||||
utils.register_swift_info('cap1', cap1_lorem='cap1_ipsum')
|
||||
|
||||
self.assertTrue('swift' in utils._swift_info)
|
||||
self.assertTrue('foo' in utils._swift_info['swift'])
|
||||
self.assertEqual(utils._swift_info['swift']['foo'], 'bar')
|
||||
self.assertTrue('lorem' in utils._swift_info['swift'])
|
||||
self.assertEqual(utils._swift_info['swift']['lorem'], 'ipsum')
|
||||
|
||||
self.assertTrue('cap1' in utils._swift_info)
|
||||
self.assertTrue('cap1_foo' in utils._swift_info['cap1'])
|
||||
self.assertEqual(utils._swift_info['cap1']['cap1_foo'], 'cap1_bar')
|
||||
self.assertTrue('cap1_lorem' in utils._swift_info['cap1'])
|
||||
self.assertEqual(utils._swift_info['cap1']['cap1_lorem'], 'cap1_ipsum')
|
||||
|
||||
self.assertRaises(ValueError,
|
||||
utils.register_swift_info, 'admin', foo='bar')
|
||||
|
||||
self.assertRaises(ValueError,
|
||||
utils.register_swift_info, 'disallowed_sections',
|
||||
disallowed_sections=None)
|
||||
|
||||
def test_get_swift_info(self):
|
||||
utils._swift_info = {'swift': {'foo': 'bar'},
|
||||
'cap1': {'cap1_foo': 'cap1_bar'}}
|
||||
utils._swift_admin_info = {'admin_cap1': {'ac1_foo': 'ac1_bar'}}
|
||||
|
||||
info = utils.get_swift_info()
|
||||
|
||||
self.assertTrue('admin' not in info)
|
||||
|
||||
self.assertTrue('swift' in info)
|
||||
self.assertTrue('foo' in info['swift'])
|
||||
self.assertEqual(utils._swift_info['swift']['foo'], 'bar')
|
||||
|
||||
self.assertTrue('cap1' in info)
|
||||
self.assertTrue('cap1_foo' in info['cap1'])
|
||||
self.assertEqual(utils._swift_info['cap1']['cap1_foo'], 'cap1_bar')
|
||||
|
||||
def test_get_swift_info_with_disallowed_sections(self):
|
||||
utils._swift_info = {'swift': {'foo': 'bar'},
|
||||
'cap1': {'cap1_foo': 'cap1_bar'},
|
||||
'cap2': {'cap2_foo': 'cap2_bar'},
|
||||
'cap3': {'cap3_foo': 'cap3_bar'}}
|
||||
utils._swift_admin_info = {'admin_cap1': {'ac1_foo': 'ac1_bar'}}
|
||||
|
||||
info = utils.get_swift_info(disallowed_sections=['cap1', 'cap3'])
|
||||
|
||||
self.assertTrue('admin' not in info)
|
||||
|
||||
self.assertTrue('swift' in info)
|
||||
self.assertTrue('foo' in info['swift'])
|
||||
self.assertEqual(info['swift']['foo'], 'bar')
|
||||
|
||||
self.assertTrue('cap1' not in info)
|
||||
|
||||
self.assertTrue('cap2' in info)
|
||||
self.assertTrue('cap2_foo' in info['cap2'])
|
||||
self.assertEqual(info['cap2']['cap2_foo'], 'cap2_bar')
|
||||
|
||||
self.assertTrue('cap3' not in info)
|
||||
|
||||
def test_register_swift_admin_info(self):
|
||||
utils.register_swift_info(admin=True, admin_foo='admin_bar')
|
||||
utils.register_swift_info(admin=True, admin_lorem='admin_ipsum')
|
||||
utils.register_swift_info('cap1', admin=True, ac1_foo='ac1_bar')
|
||||
utils.register_swift_info('cap1', admin=True, ac1_lorem='ac1_ipsum')
|
||||
|
||||
self.assertTrue('swift' in utils._swift_admin_info)
|
||||
self.assertTrue('admin_foo' in utils._swift_admin_info['swift'])
|
||||
self.assertEqual(
|
||||
utils._swift_admin_info['swift']['admin_foo'], 'admin_bar')
|
||||
self.assertTrue('admin_lorem' in utils._swift_admin_info['swift'])
|
||||
self.assertEqual(
|
||||
utils._swift_admin_info['swift']['admin_lorem'], 'admin_ipsum')
|
||||
|
||||
self.assertTrue('cap1' in utils._swift_admin_info)
|
||||
self.assertTrue('ac1_foo' in utils._swift_admin_info['cap1'])
|
||||
self.assertEqual(
|
||||
utils._swift_admin_info['cap1']['ac1_foo'], 'ac1_bar')
|
||||
self.assertTrue('ac1_lorem' in utils._swift_admin_info['cap1'])
|
||||
self.assertEqual(
|
||||
utils._swift_admin_info['cap1']['ac1_lorem'], 'ac1_ipsum')
|
||||
|
||||
self.assertTrue('swift' not in utils._swift_info)
|
||||
self.assertTrue('cap1' not in utils._swift_info)
|
||||
|
||||
def test_get_swift_admin_info(self):
|
||||
utils._swift_info = {'swift': {'foo': 'bar'},
|
||||
'cap1': {'cap1_foo': 'cap1_bar'}}
|
||||
utils._swift_admin_info = {'admin_cap1': {'ac1_foo': 'ac1_bar'}}
|
||||
|
||||
info = utils.get_swift_info(admin=True)
|
||||
|
||||
self.assertTrue('admin' in info)
|
||||
self.assertTrue('admin_cap1' in info['admin'])
|
||||
self.assertTrue('ac1_foo' in info['admin']['admin_cap1'])
|
||||
self.assertEqual(info['admin']['admin_cap1']['ac1_foo'], 'ac1_bar')
|
||||
|
||||
self.assertTrue('swift' in info)
|
||||
self.assertTrue('foo' in info['swift'])
|
||||
self.assertEqual(utils._swift_info['swift']['foo'], 'bar')
|
||||
|
||||
self.assertTrue('cap1' in info)
|
||||
self.assertTrue('cap1_foo' in info['cap1'])
|
||||
self.assertEqual(utils._swift_info['cap1']['cap1_foo'], 'cap1_bar')
|
||||
|
||||
def test_get_swift_admin_info_with_disallowed_sections(self):
|
||||
utils._swift_info = {'swift': {'foo': 'bar'},
|
||||
'cap1': {'cap1_foo': 'cap1_bar'},
|
||||
'cap2': {'cap2_foo': 'cap2_bar'},
|
||||
'cap3': {'cap3_foo': 'cap3_bar'}}
|
||||
utils._swift_admin_info = {'admin_cap1': {'ac1_foo': 'ac1_bar'}}
|
||||
|
||||
info = utils.get_swift_info(
|
||||
admin=True, disallowed_sections=['cap1', 'cap3'])
|
||||
|
||||
self.assertTrue('admin' in info)
|
||||
self.assertTrue('admin_cap1' in info['admin'])
|
||||
self.assertTrue('ac1_foo' in info['admin']['admin_cap1'])
|
||||
self.assertEqual(info['admin']['admin_cap1']['ac1_foo'], 'ac1_bar')
|
||||
self.assertTrue('disallowed_sections' in info['admin'])
|
||||
self.assertTrue('cap1' in info['admin']['disallowed_sections'])
|
||||
self.assertTrue('cap2' not in info['admin']['disallowed_sections'])
|
||||
self.assertTrue('cap3' in info['admin']['disallowed_sections'])
|
||||
|
||||
self.assertTrue('swift' in info)
|
||||
self.assertTrue('foo' in info['swift'])
|
||||
self.assertEqual(info['swift']['foo'], 'bar')
|
||||
|
||||
self.assertTrue('cap1' not in info)
|
||||
|
||||
self.assertTrue('cap2' in info)
|
||||
self.assertTrue('cap2_foo' in info['cap2'])
|
||||
self.assertEqual(info['cap2']['cap2_foo'], 'cap2_bar')
|
||||
|
||||
self.assertTrue('cap3' not in info)
|
||||
|
||||
|
||||
class TestFileLikeIter(unittest.TestCase):
|
||||
|
||||
|
293
test/unit/proxy/controllers/test_info.py
Normal file
293
test/unit/proxy/controllers/test_info.py
Normal file
@ -0,0 +1,293 @@
|
||||
# Copyright (c) 2010-2012 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
import time
|
||||
from mock import Mock
|
||||
|
||||
from swift.proxy.controllers import InfoController
|
||||
from swift.proxy.server import Application as ProxyApp
|
||||
from swift.common import utils
|
||||
from swift.common.utils import json
|
||||
from swift.common.swob import Request, HTTPException
|
||||
|
||||
|
||||
class TestInfoController(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
utils._swift_info = {}
|
||||
utils._swift_admin_info = {}
|
||||
|
||||
def get_controller(self, expose_info=None, disallowed_sections=None,
|
||||
admin_key=None):
|
||||
disallowed_sections = disallowed_sections or []
|
||||
|
||||
app = Mock(spec=ProxyApp)
|
||||
return InfoController(app, None, expose_info,
|
||||
disallowed_sections, admin_key)
|
||||
|
||||
def start_response(self, status, headers):
|
||||
self.got_statuses.append(status)
|
||||
for h in headers:
|
||||
self.got_headers.append({h[0]: h[1]})
|
||||
|
||||
def test_disabled_info(self):
|
||||
controller = self.get_controller(expose_info=False)
|
||||
|
||||
req = Request.blank(
|
||||
'/info', environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('403 Forbidden', str(resp))
|
||||
|
||||
def test_get_info(self):
|
||||
controller = self.get_controller(expose_info=True)
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
req = Request.blank(
|
||||
'/info', environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('200 OK', str(resp))
|
||||
info = json.loads(resp.body)
|
||||
self.assertTrue('admin' not in info)
|
||||
self.assertTrue('foo' in info)
|
||||
self.assertTrue('bar' in info['foo'])
|
||||
self.assertEqual(info['foo']['bar'], 'baz')
|
||||
|
||||
def test_options_info(self):
|
||||
controller = self.get_controller(expose_info=True)
|
||||
|
||||
req = Request.blank(
|
||||
'/info', environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.OPTIONS(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('200 OK', str(resp))
|
||||
self.assertTrue('Allow' in resp.headers)
|
||||
|
||||
def test_get_info_cors(self):
|
||||
controller = self.get_controller(expose_info=True)
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
req = Request.blank(
|
||||
'/info', environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'Origin': 'http://example.com'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('200 OK', str(resp))
|
||||
info = json.loads(resp.body)
|
||||
self.assertTrue('admin' not in info)
|
||||
self.assertTrue('foo' in info)
|
||||
self.assertTrue('bar' in info['foo'])
|
||||
self.assertEqual(info['foo']['bar'], 'baz')
|
||||
self.assertTrue('Access-Control-Allow-Origin' in resp.headers)
|
||||
self.assertTrue('Access-Control-Expose-Headers' in resp.headers)
|
||||
|
||||
def test_head_info(self):
|
||||
controller = self.get_controller(expose_info=True)
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
req = Request.blank(
|
||||
'/info', environ={'REQUEST_METHOD': 'HEAD'})
|
||||
resp = controller.HEAD(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('200 OK', str(resp))
|
||||
|
||||
def test_disallow_info(self):
|
||||
controller = self.get_controller(expose_info=True,
|
||||
disallowed_sections=['foo2'])
|
||||
utils._swift_info = {'foo': {'bar': 'baz'},
|
||||
'foo2': {'bar2': 'baz2'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
req = Request.blank(
|
||||
'/info', environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('200 OK', str(resp))
|
||||
info = json.loads(resp.body)
|
||||
self.assertTrue('foo' in info)
|
||||
self.assertTrue('bar' in info['foo'])
|
||||
self.assertEqual(info['foo']['bar'], 'baz')
|
||||
self.assertTrue('foo2' not in info)
|
||||
|
||||
def test_disabled_admin_info(self):
|
||||
controller = self.get_controller(expose_info=True, admin_key='')
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
expires = int(time.time() + 86400)
|
||||
sig = utils.get_hmac('GET', '/info', expires, '')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('403 Forbidden', str(resp))
|
||||
|
||||
def test_get_admin_info(self):
|
||||
controller = self.get_controller(expose_info=True,
|
||||
admin_key='secret-admin-key')
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
expires = int(time.time() + 86400)
|
||||
sig = utils.get_hmac('GET', '/info', expires, 'secret-admin-key')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('200 OK', str(resp))
|
||||
info = json.loads(resp.body)
|
||||
self.assertTrue('admin' in info)
|
||||
self.assertTrue('qux' in info['admin'])
|
||||
self.assertTrue('quux' in info['admin']['qux'])
|
||||
self.assertEqual(info['admin']['qux']['quux'], 'corge')
|
||||
|
||||
def test_head_admin_info(self):
|
||||
controller = self.get_controller(expose_info=True,
|
||||
admin_key='secret-admin-key')
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
expires = int(time.time() + 86400)
|
||||
sig = utils.get_hmac('GET', '/info', expires, 'secret-admin-key')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'HEAD'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('200 OK', str(resp))
|
||||
|
||||
expires = int(time.time() + 86400)
|
||||
sig = utils.get_hmac('HEAD', '/info', expires, 'secret-admin-key')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'HEAD'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('200 OK', str(resp))
|
||||
|
||||
def test_get_admin_info_invalid_method(self):
|
||||
controller = self.get_controller(expose_info=True,
|
||||
admin_key='secret-admin-key')
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
expires = int(time.time() + 86400)
|
||||
sig = utils.get_hmac('HEAD', '/info', expires, 'secret-admin-key')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('401 Unauthorized', str(resp))
|
||||
|
||||
def test_get_admin_info_invalid_expires(self):
|
||||
controller = self.get_controller(expose_info=True,
|
||||
admin_key='secret-admin-key')
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
expires = 1
|
||||
sig = utils.get_hmac('GET', '/info', expires, 'secret-admin-key')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('401 Unauthorized', str(resp))
|
||||
|
||||
expires = 'abc'
|
||||
sig = utils.get_hmac('GET', '/info', expires, 'secret-admin-key')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('401 Unauthorized', str(resp))
|
||||
|
||||
def test_get_admin_info_invalid_path(self):
|
||||
controller = self.get_controller(expose_info=True,
|
||||
admin_key='secret-admin-key')
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
expires = int(time.time() + 86400)
|
||||
sig = utils.get_hmac('GET', '/foo', expires, 'secret-admin-key')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('401 Unauthorized', str(resp))
|
||||
|
||||
def test_get_admin_info_invalid_key(self):
|
||||
controller = self.get_controller(expose_info=True,
|
||||
admin_key='secret-admin-key')
|
||||
utils._swift_info = {'foo': {'bar': 'baz'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
expires = int(time.time() + 86400)
|
||||
sig = utils.get_hmac('GET', '/foo', expires, 'invalid-admin-key')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('401 Unauthorized', str(resp))
|
||||
|
||||
def test_admin_disallow_info(self):
|
||||
controller = self.get_controller(expose_info=True,
|
||||
disallowed_sections=['foo2'],
|
||||
admin_key='secret-admin-key')
|
||||
utils._swift_info = {'foo': {'bar': 'baz'},
|
||||
'foo2': {'bar2': 'baz2'}}
|
||||
utils._swift_admin_info = {'qux': {'quux': 'corge'}}
|
||||
|
||||
expires = int(time.time() + 86400)
|
||||
sig = utils.get_hmac('GET', '/info', expires, 'secret-admin-key')
|
||||
path = '/info?swiftinfo_sig={sig}&swiftinfo_expires={expires}'.format(
|
||||
sig=sig, expires=expires)
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'GET'})
|
||||
resp = controller.GET(req)
|
||||
self.assertTrue(isinstance(resp, HTTPException))
|
||||
self.assertEqual('200 OK', str(resp))
|
||||
info = json.loads(resp.body)
|
||||
self.assertTrue('foo2' not in info)
|
||||
self.assertTrue('admin' in info)
|
||||
self.assertTrue('disallowed_sections' in info['admin'])
|
||||
self.assertTrue('foo2' in info['admin']['disallowed_sections'])
|
||||
self.assertTrue('qux' in info['admin'])
|
||||
self.assertTrue('quux' in info['admin']['qux'])
|
||||
self.assertEqual(info['admin']['qux']['quux'], 'corge')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -650,6 +650,34 @@ class TestProxyServer(unittest.TestCase):
|
||||
{'region': 2, 'zone': 1, 'ip': '127.0.0.1'}]
|
||||
self.assertEquals(exp_sorted, app_sorted)
|
||||
|
||||
def test_info_defaults(self):
|
||||
app = proxy_server.Application({}, FakeMemcache(),
|
||||
account_ring=FakeRing(),
|
||||
container_ring=FakeRing(),
|
||||
object_ring=FakeRing())
|
||||
|
||||
self.assertTrue(app.expose_info)
|
||||
self.assertTrue(isinstance(app.disallowed_sections, list))
|
||||
self.assertEqual(0, len(app.disallowed_sections))
|
||||
self.assertTrue(app.admin_key is None)
|
||||
|
||||
def test_get_info_controller(self):
|
||||
path = '/info'
|
||||
app = proxy_server.Application({}, FakeMemcache(),
|
||||
account_ring=FakeRing(),
|
||||
container_ring=FakeRing(),
|
||||
object_ring=FakeRing())
|
||||
|
||||
controller, path_parts = app.get_controller(path)
|
||||
|
||||
self.assertTrue('version' in path_parts)
|
||||
self.assertTrue(path_parts['version'] is None)
|
||||
self.assertTrue('disallowed_sections' in path_parts)
|
||||
self.assertTrue('expose_info' in path_parts)
|
||||
self.assertTrue('admin_key' in path_parts)
|
||||
|
||||
self.assertEqual(controller.__name__, 'InfoController')
|
||||
|
||||
|
||||
class TestObjectController(unittest.TestCase):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user