"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "swift/common/middleware/tempauth.py" between
swift-2.19.1.tar.gz and swift-2.21.0.tar.gz

About: OpenStack swift is software for creating redundant, scalable object storage using clusters of commodity servers to store terabytes or even petabytes of accessible data (now supporting storage policies).
The "Stein" series (latest release).

tempauth.py  (swift-2.19.1):tempauth.py  (swift-2.21.0)
skipping to change at line 185 skipping to change at line 185
from __future__ import print_function from __future__ import print_function
import json import json
from time import time from time import time
from traceback import format_exc from traceback import format_exc
from uuid import uuid4 from uuid import uuid4
import base64 import base64
from eventlet import Timeout from eventlet import Timeout
import six import six
from swift.common.swob import Response, Request from swift.common.swob import Response, Request, wsgi_to_str
from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \ from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
HTTPUnauthorized HTTPUnauthorized, HTTPMethodNotAllowed
from swift.common.request_helpers import get_sys_meta_prefix from swift.common.request_helpers import get_sys_meta_prefix
from swift.common.middleware.acl import ( from swift.common.middleware.acl import (
clean_acl, parse_acl, referrer_allowed, acls_from_account_info) clean_acl, parse_acl, referrer_allowed, acls_from_account_info)
from swift.common.utils import cache_from_env, get_logger, \ from swift.common.utils import cache_from_env, get_logger, \
split_path, config_true_value, register_swift_info split_path, config_true_value, register_swift_info
from swift.common.utils import config_read_reseller_options, quote from swift.common.utils import config_read_reseller_options, quote
from swift.proxy.controllers.base import get_account_info from swift.proxy.controllers.base import get_account_info
DEFAULT_TOKEN_LIFE = 86400 DEFAULT_TOKEN_LIFE = 86400
skipping to change at line 240 skipping to change at line 240
for conf_key in conf: for conf_key in conf:
if conf_key.startswith(('user_', 'user64_')): if conf_key.startswith(('user_', 'user64_')):
account, username = conf_key.split('_', 1)[1].split('_') account, username = conf_key.split('_', 1)[1].split('_')
if conf_key.startswith('user64_'): if conf_key.startswith('user64_'):
# Because trailing equal signs would screw up config file # Because trailing equal signs would screw up config file
# parsing, we auto-pad with '=' chars. # parsing, we auto-pad with '=' chars.
account += '=' * (len(account) % 4) account += '=' * (len(account) % 4)
account = base64.b64decode(account) account = base64.b64decode(account)
username += '=' * (len(username) % 4) username += '=' * (len(username) % 4)
username = base64.b64decode(username) username = base64.b64decode(username)
if not six.PY2:
account = account.decode('utf8')
username = username.decode('utf8')
values = conf[conf_key].split() values = conf[conf_key].split()
if not values: if not values:
raise ValueError('%s has no key set' % conf_key) raise ValueError('%s has no key set' % conf_key)
key = values.pop(0) key = values.pop(0)
if values and ('://' in values[-1] or '$HOST' in values[-1]): if values and ('://' in values[-1] or '$HOST' in values[-1]):
url = values.pop() url = values.pop()
else: else:
url = '$HOST/v1/%s%s' % ( url = '$HOST/v1/%s%s' % (
self.reseller_prefix, quote(account)) self.reseller_prefix, quote(account))
self.users[account + ':' + username] = { self.users[account + ':' + username] = {
skipping to change at line 276 skipping to change at line 279
middleware overrides it. middleware overrides it.
Alternatively, if the request matches the self.auth_prefix, the request Alternatively, if the request matches the self.auth_prefix, the request
will be routed through the internal auth request handler (self.handle). will be routed through the internal auth request handler (self.handle).
This is to handle granting tokens, etc. This is to handle granting tokens, etc.
""" """
if self.allow_overrides and env.get('swift.authorize_override', False): if self.allow_overrides and env.get('swift.authorize_override', False):
return self.app(env, start_response) return self.app(env, start_response)
if env.get('PATH_INFO', '').startswith(self.auth_prefix): if env.get('PATH_INFO', '').startswith(self.auth_prefix):
return self.handle(env, start_response) return self.handle(env, start_response)
s3 = env.get('s3api.auth_details') s3 = env.get('s3api.auth_details') or env.get('swift3.auth_details')
token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
service_token = env.get('HTTP_X_SERVICE_TOKEN') service_token = env.get('HTTP_X_SERVICE_TOKEN')
if s3 or (token and token.startswith(self.reseller_prefix)): if s3 or (token and token.startswith(self.reseller_prefix)):
# Note: Empty reseller_prefix will match all tokens. # Note: Empty reseller_prefix will match all tokens.
groups = self.get_groups(env, token) groups = self.get_groups(env, token)
if service_token: if service_token:
service_groups = self.get_groups(env, service_token) service_groups = self.get_groups(env, service_token)
if groups and service_groups: if groups and service_groups:
groups += ',' + service_groups groups += ',' + service_groups
if groups: if groups:
skipping to change at line 439 skipping to change at line 442
groups = None groups = None
memcache_client = cache_from_env(env) memcache_client = cache_from_env(env)
if not memcache_client: if not memcache_client:
raise Exception('Memcache required') raise Exception('Memcache required')
memcache_token_key = '%s/token/%s' % (self.reseller_prefix, token) memcache_token_key = '%s/token/%s' % (self.reseller_prefix, token)
cached_auth_data = memcache_client.get(memcache_token_key) cached_auth_data = memcache_client.get(memcache_token_key)
if cached_auth_data: if cached_auth_data:
expires, groups = cached_auth_data expires, groups = cached_auth_data
if expires < time(): if expires < time():
groups = None groups = None
else: elif six.PY2:
groups = groups.encode('utf8') groups = groups.encode('utf8')
s3_auth_details = env.get('s3api.auth_details') s3_auth_details = env.get('s3api.auth_details') or\
env.get('swift3.auth_details')
if s3_auth_details: if s3_auth_details:
if 'check_signature' not in s3_auth_details: if 'check_signature' not in s3_auth_details:
self.logger.warning( self.logger.warning(
'Swift3 did not provide a check_signature function; ' 'Swift3 did not provide a check_signature function; '
'upgrade Swift3 if you want to use it with tempauth') 'upgrade Swift3 if you want to use it with tempauth')
return None return None
account_user = s3_auth_details['access_key'] account_user = s3_auth_details['access_key']
if account_user not in self.users: if account_user not in self.users:
return None return None
user = self.users[account_user] user = self.users[account_user]
skipping to change at line 498 skipping to change at line 502
% e2) % e2)
return None return None
return acls return acls
def extract_acl_and_report_errors(self, req): def extract_acl_and_report_errors(self, req):
""" """
Return a user-readable string indicating the errors in the input ACL, Return a user-readable string indicating the errors in the input ACL,
or None if there are no errors. or None if there are no errors.
""" """
acl_header = 'x-account-access-control' acl_header = 'x-account-access-control'
acl_data = req.headers.get(acl_header) acl_data = wsgi_to_str(req.headers.get(acl_header))
result = parse_acl(version=2, data=acl_data) result = parse_acl(version=2, data=acl_data)
if result is None: if result is None:
return 'Syntax error in input (%r)' % acl_data return 'Syntax error in input (%r)' % acl_data
tempauth_acl_keys = 'admin read-write read-only'.split() tempauth_acl_keys = 'admin read-write read-only'.split()
for key in result: for key in result:
# While it is possible to construct auth systems that collaborate # While it is possible to construct auth systems that collaborate
# on ACLs, TempAuth is not such an auth system. At this point, # on ACLs, TempAuth is not such an auth system. At this point,
# it thinks it is authoritative. # it thinks it is authoritative.
if key not in tempauth_acl_keys: if key not in tempauth_acl_keys:
return "Key %s not recognized" % json.dumps( return "Key %s not recognized" % json.dumps(key)
key).encode('ascii')
for key in tempauth_acl_keys: for key in tempauth_acl_keys:
if key not in result: if key not in result:
continue continue
if not isinstance(result[key], list): if not isinstance(result[key], list):
return "Value for key %s must be a list" % json.dumps( return "Value for key %s must be a list" % json.dumps(key)
key).encode('ascii')
for grantee in result[key]: for grantee in result[key]:
if not isinstance(grantee, six.string_types): if not isinstance(grantee, six.string_types):
return "Elements of %s list must be strings" % json.dumps( return "Elements of %s list must be strings" % json.dumps(
key).encode('ascii') key)
# Everything looks fine, no errors found # Everything looks fine, no errors found
internal_hdr = get_sys_meta_prefix('account') + 'core-access-control' internal_hdr = get_sys_meta_prefix('account') + 'core-access-control'
req.headers[internal_hdr] = req.headers.pop(acl_header) req.headers[internal_hdr] = req.headers.pop(acl_header)
return None return None
def authorize(self, req): def authorize(self, req):
""" """
Returns None if the request is authorized to continue or a standard Returns None if the request is authorized to continue or a standard
WSGI response callable if not. WSGI response callable if not.
skipping to change at line 568 skipping to change at line 570
account_user = user_groups[1] if len(user_groups) > 1 else None account_user = user_groups[1] if len(user_groups) > 1 else None
if '.reseller_admin' in user_groups and \ if '.reseller_admin' in user_groups and \
account not in self.reseller_prefixes and \ account not in self.reseller_prefixes and \
not self._dot_account(account): not self._dot_account(account):
req.environ['swift_owner'] = True req.environ['swift_owner'] = True
self.logger.debug("User %s has reseller admin authorizing." self.logger.debug("User %s has reseller admin authorizing."
% account_user) % account_user)
return None return None
if account in user_groups and \ if wsgi_to_str(account) in user_groups and \
(req.method not in ('DELETE', 'PUT') or container): (req.method not in ('DELETE', 'PUT') or container):
# The user is admin for the account and is not trying to do an # The user is admin for the account and is not trying to do an
# account DELETE or PUT # account DELETE or PUT
account_prefix = self._get_account_prefix(account) account_prefix = self._get_account_prefix(account)
require_group = self.account_rules.get(account_prefix).get( require_group = self.account_rules.get(account_prefix).get(
'require_group') 'require_group')
if require_group and require_group in user_groups: if require_group and require_group in user_groups:
req.environ['swift_owner'] = True req.environ['swift_owner'] = True
self.logger.debug("User %s has admin and %s group." self.logger.debug("User %s has admin and %s group."
" Authorizing." % (account_user, " Authorizing." % (account_user,
skipping to change at line 687 skipping to change at line 689
def handle_request(self, req): def handle_request(self, req):
""" """
Entry point for auth requests (ones that match the self.auth_prefix). Entry point for auth requests (ones that match the self.auth_prefix).
Should return a WSGI-style callable (such as swob.Response). Should return a WSGI-style callable (such as swob.Response).
:param req: swob.Request object :param req: swob.Request object
""" """
req.start_time = time() req.start_time = time()
handler = None handler = None
if req.method != 'GET':
req.response = HTTPMethodNotAllowed(request=req)
return req.response
try: try:
version, account, user, _junk = split_path(req.path_info, version, account, user, _junk = split_path(req.path_info,
1, 4, True) 1, 4, True)
except ValueError: except ValueError:
self.logger.increment('errors') self.logger.increment('errors')
return HTTPNotFound(request=req) return HTTPNotFound(request=req)
if version in ('v1', 'v1.0', 'auth'): if version in ('v1', 'v1.0', 'auth'):
if req.method == 'GET': if req.method == 'GET':
handler = self.handle_get_token handler = self.handle_get_token
if not handler: if not handler:
skipping to change at line 744 skipping to change at line 749
account = pathsegs[1] account = pathsegs[1]
user = req.headers.get('x-storage-user') user = req.headers.get('x-storage-user')
if not user: if not user:
user = req.headers.get('x-auth-user') user = req.headers.get('x-auth-user')
if not user or ':' not in user: if not user or ':' not in user:
self.logger.increment('token_denied') self.logger.increment('token_denied')
auth = 'Swift realm="%s"' % account auth = 'Swift realm="%s"' % account
return HTTPUnauthorized(request=req, return HTTPUnauthorized(request=req,
headers={'Www-Authenticate': auth}) headers={'Www-Authenticate': auth})
account2, user = user.split(':', 1) account2, user = user.split(':', 1)
if account != account2: if wsgi_to_str(account) != account2:
self.logger.increment('token_denied') self.logger.increment('token_denied')
auth = 'Swift realm="%s"' % account auth = 'Swift realm="%s"' % account
return HTTPUnauthorized(request=req, return HTTPUnauthorized(request=req,
headers={'Www-Authenticate': auth}) headers={'Www-Authenticate': auth})
key = req.headers.get('x-storage-pass') key = req.headers.get('x-storage-pass')
if not key: if not key:
key = req.headers.get('x-auth-key') key = req.headers.get('x-auth-key')
elif pathsegs[0] in ('auth', 'v1.0'): elif pathsegs[0] in ('auth', 'v1.0'):
user = req.headers.get('x-auth-user') user = req.headers.get('x-auth-user')
if not user: if not user:
skipping to change at line 800 skipping to change at line 805
# See if a token already exists and hasn't expired # See if a token already exists and hasn't expired
token = None token = None
memcache_user_key = '%s/user/%s' % (self.reseller_prefix, account_user) memcache_user_key = '%s/user/%s' % (self.reseller_prefix, account_user)
candidate_token = memcache_client.get(memcache_user_key) candidate_token = memcache_client.get(memcache_user_key)
if candidate_token: if candidate_token:
memcache_token_key = \ memcache_token_key = \
'%s/token/%s' % (self.reseller_prefix, candidate_token) '%s/token/%s' % (self.reseller_prefix, candidate_token)
cached_auth_data = memcache_client.get(memcache_token_key) cached_auth_data = memcache_client.get(memcache_token_key)
if cached_auth_data: if cached_auth_data:
expires, old_groups = cached_auth_data expires, old_groups = cached_auth_data
old_groups = [group.encode('utf8') old_groups = [group.encode('utf8') if six.PY2 else group
for group in old_groups.split(',')] for group in old_groups.split(',')]
new_groups = self._get_user_groups(account, account_user, new_groups = self._get_user_groups(account, account_user,
account_id) account_id)
if expires > time() and \ if expires > time() and \
set(old_groups) == set(new_groups.split(',')): set(old_groups) == set(new_groups.split(',')):
token = candidate_token token = candidate_token
# Create a new token if one didn't exist # Create a new token if one didn't exist
if not token: if not token:
# Generate new token # Generate new token
 End of changes. 14 change blocks. 
14 lines changed or deleted 19 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)