def authorize_anonymous(self, req):
        """
        Authorize an anonymous request.

        :returns: None if authorization is granted, an error page otherwise.
        """
        try:
            part = req.split_path(1, 4, True)
            version, account, container, obj = part
        except ValueError:
            return HTTPNotFound(request=req)

        #allow OPTIONS requests to proceed as normal
        if req.method == 'OPTIONS':
            return

        is_authoritative_authz = (account
                                  and account.startswith(self.reseller_prefix))
        if not is_authoritative_authz:
            return self.denied_response(req)

        referrers, roles = osd_acl.parse_acl(getattr(req, 'acl', None))
        authorized = self._authorize_unconfirmed_identity(
            req, obj, referrers, roles)
        if not authorized:
            return self.denied_response(req)
 def get(self, filesystem, acc_dir, cont_dir, account, container, req):
     """
     Handles GET request for container
     param:filesystem: Filesystem name
     param:account: Account name
     param:container: Container name
     param:req: HTTP Request object
     """
     try:
         # create path
         path = self.create_path(filesystem, acc_dir, cont_dir, account, container)
         self.logger.debug(('GET container called for path: %(path)s'),
             {'path' : path})
         # get headers for response
         headers = self.__head_or_get(path)
         if headers == HTTP_INTERNAL_SERVER_ERROR:
             self.logger.debug('Internal error raised from library in HEAD')
             raise HTTPInternalServerError(request=req)
         elif headers == HTTP_NOT_FOUND:
             self.logger.debug('File not found error raised from '
                 'library in HEAD')
             raise HTTPNotFound(request=req)
         else:
             out_content_type = get_listing_content_type(req)
             headers['Content-Type'] = out_content_type
         # get response to be send
         ret = self.__get_cont_list(path, container, req)
         if ret == OsdExceptionCode.OSD_INTERNAL_ERROR:
             self.logger.debug('Internal error raised from library in GET')
             raise HTTPInternalServerError(request=req)
         elif ret == OsdExceptionCode.OSD_NOT_FOUND:
             self.logger.debug('File not found error raised from '
                 'library in GET')
             raise HTTPNotFound(request=req)
         else:
             pass
         return ret, headers
     except HTTPException as error:
         self.logger.exception(error)
         raise error
     except Exception as err:
         self.logger.error(('GET request failed for '
             'container: %(container)s '),
             {'container' : container})
         self.logger.exception(err)
         raise err
 def head(self, filesystem, acc_dir, cont_dir, account, container, req):
     """
     Handles HEAD request for container
     param:filesystem: Filesystem name
     param:account: Account name
     param:container: Container name
     param:req: HTTP Request object
     """
     try:
         # create path
         path, headers = '', ''
         if 'x-updater-request' in req.headers:
             path = self.create_updater_path(filesystem, acc_dir, cont_dir, account, container)
             self.logger.debug(('HEAD container called for path: %(path)s'),
                 {'path' : path})
             # get headers for updater request
             headers = self.__updater_headers(path , True) #pass True in updater request
         else:
             path = self.create_path(filesystem, acc_dir, cont_dir, account, container)
             self.logger.debug(('HEAD container called for path: %(path)s'),
                 {'path' : path})
             # get headers for request
             headers = self.__head_or_get(path)
         if headers == HTTP_INTERNAL_SERVER_ERROR:
             self.logger.debug('Internal error raised from library')
             raise HTTPInternalServerError(request=req)
         if headers == HTTP_NOT_FOUND and 'x-updater-request' in req.headers:
             self.logger.debug('File not found error raised from library: updater case')
             raise HTTPNotFound(request=req)
         elif headers == HTTP_NOT_FOUND:
             self.logger.debug('File not found error raised from library')
             raise HTTPNotFound(request=req)
         else:
             out_content_type = get_listing_content_type(req)
             headers['Content-Type'] = out_content_type
         return headers
     except HTTPException as error:
         self.logger.exception(error)
         raise error
     except Exception as err:
         self.logger.error \
             (('HEAD request failed for container: %(container)s '),
             {'container' : container})
         self.logger.exception(err)
         raise err
示例#4
0
    def BULK_DELETE(self, req):
        """HTTP BULK_DELETE request handler."""
        self.app.logger.debug("In BULK_DELETE____")
        account_node, account_filesystem, account_directory, container_count, \
        account_component_number, head_status = self.account_info(\
                                               self.account_name, req)
        if not account_node and not head_status:
            return HTTPInternalServerError(request=req)
        if head_status and int(str(head_status).split()[0]) == 503:
            #TODO need to check why head_status is int or sometimes str
            self.app.logger.info("account HEAD returning 503 service " \
                "unavailable error due to which this request got failed")
            return HTTPServiceUnavailable(request=req)
        if not account_node:
            return HTTPNotFound(request=req)
        container_node, container_filesystem, container_directory, \
        global_map_version, component_number = \
            self.app.container_ring.get_node( \
            self.account_name, self.container_name)
        if not len(container_node):
            self.app.logger.error(
                _('%(msg)s %(method)s %(path)s'), {
                    'msg': _('ERROR Wrong ring file content'),
                    'method': 'BULK_DELETE',
                    'path': req.path
                })
            return HTTPInternalServerError(request=req)
        self.app.logger.debug("Going for backend call______")
        try:
            headers = self._backend_requests(req, len(container_node),
                account_node, account_filesystem, account_directory, \
                global_map_version, component_number, account_component_number)
        except ZeroDivisionError:
            self.app.logger.error(
                _('%(msg)s %(method)s %(path)s'), {
                    'msg': _('ERROR Wrong ring file content'),
                    'method': 'BULK_DELETE',
                    'path': req.path
                })
            return HTTPInternalServerError(request=req)

        clear_info_cache(self.app, req.environ, self.account_name,
                         self.container_name)
        self.app.logger.debug("Now going to make request_____")
        resp = self.make_request_for_bulk_delete(
            req, self.app.container_ring, container_node, container_filesystem,
            container_directory, 'BULK_DELETE', req.swift_entity_path, headers)
        return resp
示例#5
0
    def handle_request(self, req):
        """
        Entry point for auth requests (ones that match the self.auth_prefix).
        Should return a WSGI-style callable (such as swob.Response).

        :param req: swob.Request object
        """
        req.start_time = time()
        handler = None
        try:
            version, account, user, _junk = req.split_path(1, 4, True)
        except ValueError:
            self.logger.increment('errors')
            return HTTPNotFound(request=req)
        if version in ('v1', 'v1.0', 'auth'):
            if req.method == 'GET':
                handler = self.handle_get_token
        if not handler:
            self.logger.increment('errors')
            req.response = HTTPBadRequest(request=req)
        else:
            req.response = handler(req)
        return req.response
示例#6
0
    def get_slo_segments(self, obj_name, req):
        """
        Performs a swob.Request and returns the SLO manifest's segments.

        :raises HTTPServerError: on unable to load obj_name or
                                 on unable to load the SLO manifest data.
        :raises HTTPBadRequest: on not an SLO manifest
        :raises HTTPNotFound: on SLO manifest not found
        :returns: SLO manifest's segments
        """
        vrs, account, _junk = req.split_path(2, 3, True)
        new_env = req.environ.copy()
        new_env['REQUEST_METHOD'] = 'GET'
        del (new_env['wsgi.input'])
        new_env['QUERY_STRING'] = 'multipart-manifest=get'
        new_env['CONTENT_LENGTH'] = 0
        new_env['HTTP_USER_AGENT'] = \
            '%s MultipartDELETE' % new_env.get('HTTP_USER_AGENT')
        new_env['swift.source'] = 'SLO'
        new_env['PATH_INFO'] = (
            '/%s/%s/%s' % (vrs, account, obj_name.lstrip('/'))).encode('utf-8')
        resp = Request.blank('', new_env).get_response(self.app)

        if resp.is_success:
            if config_true_value(resp.headers.get('X-Static-Large-Object')):
                try:
                    return json.loads(resp.body)
                except ValueError:
                    raise HTTPServerError('Unable to load SLO manifest')
            else:
                raise HTTPBadRequest('Not an SLO manifest')
        elif resp.status_int == HTTP_NOT_FOUND:
            raise HTTPNotFound('SLO manifest not found')
        elif resp.status_int == HTTP_UNAUTHORIZED:
            raise HTTPUnauthorized('401 Unauthorized')
        else:
            raise HTTPServerError('Unable to load SLO manifest or segment.')
示例#7
0
class AccountServiceInterface:
    """An interface class between account service and library."""
    account_lib_obj = libaccountLib.AccountLibraryImpl()
    asyn_helper = AsyncLibraryHelper(account_lib_obj)

    @classmethod
    def start_event_wait_func(cls, logger):
        logger.info('wait_event called!')
        AccountServiceInterface.asyn_helper.wait_event()

    @classmethod
    def stop_event_wait_func(cls, logger):
        logger.info('wait_event_stop called!')
        AccountServiceInterface.asyn_helper.wait_event_stop()

    @classmethod
    def get_metadata(self, req, logger):
        """
        Creates metadata string from request object and
        container stat if present
        param: Request object
        Returns metadata dictionary
        """
        try:
            new_meta = {}
            metadata = {}
            # get metadata from request headers
            metadata.update((key.lower(), value)
                            for key, value in req.headers.iteritems()
                            if key.lower() in HEADERS
                            or is_sys_or_user_meta('account', key))
            for key, value in metadata.iteritems():
                if key == 'x-account-read':
                    new_meta.update({'r-': value})
                elif key == 'x-account-write':
                    new_meta.update({'w-': value})
                else:
                    ser_key = key.split('-')[2]
                    if ser_key == 'meta':
                        new_key = '%s-%s' % ('m', key.split('-', 3)[-1])
                        new_meta.update({new_key: value})
                    elif ser_key == 'sysmeta':
                        new_key = '%s-%s' % ('sm', key.split('-', 3)[-1])
                        new_meta.update({new_key: value})
                    else:
                        logger.debug('Expected metadata not found')
            return new_meta
        except Exception as err:
            logger.error(
                ('get_metadata failed ', 'close failure: %(exc)s : %(stack)s'),
                {
                    'exc': err,
                    'stack': ''.join(traceback.format_stack())
                })
            raise err

    @classmethod
    def create_AccountStat_object(cls, info):
        """An interface to create an object of AccountStat, from info map"""
        if 'account' not in info:
            info['account'] = '-1'
        if 'created_at' not in info:
            info['created_at'] = '0'
        if 'put_timestamp' not in info:
            info['put_timestamp'] = '0'
        if 'delete_timestamp' not in info:
            info['delete_timestamp'] = '0'
        if 'container_count' not in info:
            info['container_count'] = 0
        if 'object_count' not in info:
            info['object_count'] = 0
        if 'bytes_used' not in info:
            info['bytes_used'] = 0
        if 'hash' not in info:
            info['hash'] = '-1'
        if 'id' not in info:
            info['id'] = '-1'
        if 'status' not in info:
            info['status'] = '-1'
        if 'status_changed_at' not in info:
            info['status_changed_at'] = '0'
        if 'metadata' not in info:
            info['metadata'] = {}
        return libaccountLib.AccountStat(
                                    info['account'],
                                    info['created_at'],
                                    normalize_timestamp(\
                                        info['put_timestamp']),
                                    normalize_timestamp(\
                                        info['delete_timestamp']),
                                    info['container_count'],
                                    info['object_count'],
                                    info['bytes_used'],
                                    info['hash'],
                                    info['id'],
                                    info['status'],
                                    info['status_changed_at'],
                                    info['metadata']
                                    )

    @classmethod
    def create_container_record(cls, name, hash, info, deleted):
        """An interface to create an object of ContainerRecord, from info map"""
        if 'x-object-count' not in info:
            info['x-object-count'] = 0
        if not info['x-put-timestamp']:
            info['x-put-timestamp'] = '0'
        if not info['x-delete-timestamp']:
            info['x-delete-timestamp'] = '0'
        return libaccountLib.ContainerRecord(0, name, hash, \
                normalize_timestamp(str(info['x-put-timestamp'])), \
                normalize_timestamp(str(info['x-delete-timestamp'])), \
                long(info['x-object-count']), \
                long(info['x-bytes-used']), deleted)

    @classmethod
    def create_container_record_for_updater(cls, name, hash, info):
        """An interface to create an object of ContainerRecord for account \
           updater, from info map
        """
        if 'x-object-count' not in info:
            info['x-object-count'] = 0
        if not info['put_timestamp']:
            info['put_timestamp'] = '0'
        if not info['delete_timestamp']:
            info['delete_timestamp'] = '0'
        return libaccountLib.ContainerRecord(0, name, hash, \
                normalize_timestamp(str(info['put_timestamp'])), \
                normalize_timestamp(str(info['delete_timestamp'])), \
                long(info['object_count']), \
                long(info['bytes_used']), \
                info['deleted'])

    @classmethod
    def list_account(cls, temp_path, account_path, out_content_type, req, limit, marker, \
        end_marker, prefix, delimiter, logger):
        """An interface to list the containers in account."""
        logger.debug("Get account stats for path: %s" % account_path)
        container_record_list = []
        resp = libaccountLib.AccountStatWithStatus()
        AccountServiceInterface.__get_account_stat(resp, temp_path,
                                                   account_path, logger)
        logger.info("Account library responded with: %s for get_account_stat \
            in GET" % resp.get_return_status())
        if resp.get_return_status() == INFO_FILE_OPERATION_SUCCESS:
            resp_headers = {
                'X-Account-Container-Count': \
                    resp.get_account_stat().get_container_count(),
                'X-Account-Object-Count': \
                    resp.get_account_stat().get_object_count(),
                'X-Account-Bytes-Used':
                    resp.get_account_stat().get_bytes_used(),
                'X-Timestamp': resp.get_account_stat().get_created_at(),
                'X-PUT-Timestamp': resp.get_account_stat().get_put_timestamp()
                }
            modified_meta = {}
            for key, value in resp.get_account_stat().get_metadata().iteritems(
            ):
                if key == 'r-':
                    modified_meta.update({'x-account-read': value})
                elif key == 'w-':
                    modified_meta.update({'x-account-write': value})
                else:
                    ser_key = key.split('-')[0]
                    if ser_key == 'm':
                        key = 'x-account-meta-' + key.split('-', 1)[1]
                    modified_meta.update({key: value})

            resp_headers.update(
                (key, value) for (key, value) in modified_meta.iteritems())

            if limit:
                logger.debug("Calling list_container")
                resp = libaccountLib.ListContainerWithStatus()
                try:
                    AccountServiceInterface.asyn_helper.call("list_container", \
                            resp, account_path, limit, marker, \
                            end_marker, prefix, delimiter)
                except Exception, err:
                    logger.error(('list_container for %(ac_dir)s failed ' \
                        'close failure: %(exc)s : %(stack)s'), {'ac_dir' : \
                        account_path, 'exc': err, 'stack': \
                        ''.join(traceback.format_stack())})
                    raise err
                logger.info("Account library responded with: %s for \
                    list_container in GET" % resp.get_return_status())
                if resp.get_return_status() == INFO_FILE_OPERATION_SUCCESS:
                    container_record_list = resp.get_container_record()
                    if delimiter:
                        container_list_new = []
                        for obj in container_record_list:
                            name = obj.get_name()
                            if prefix:
                                match = re.match("^" + prefix + ".*", name)
                                if match:
                                    replace = re.sub("^" + prefix, '', name)
                                    replace = replace.split(delimiter)
                                    if len(replace) >= 2:
                                        obj.set_name(prefix + replace[0] +
                                                     delimiter)
                                        if marker != obj.get_name(
                                        ) or marker > obj.get_name():
                                            container_list_new.append((obj, (0, 1)[delimiter in obj.get_name() and \
                                                                   obj.get_name().endswith(delimiter)]))
                                    else:
                                        obj.set_name(prefix + replace[0])
                                        if marker != obj.get_name(
                                        ) or marker > obj.get_name():
                                            container_list_new.append((obj, 0))
                            else:
                                replace = name.split(delimiter)
                                if len(replace) >= 2:
                                    obj.set_name(replace[0] + delimiter)
                                if marker != obj.get_name(
                                ) or marker > obj.get_name():
                                    container_list_new.append((obj, (0, 1)[delimiter in obj.get_name() and \
                                                           obj.get_name().endswith(delimiter)]))
                        container_record_list = container_list_new
                    else:
                        container_record_list = [
                            (record, 0) for record in container_record_list
                        ]
        if resp.get_return_status() == INFO_FILE_OPERATION_SUCCESS:
            logger.debug("Populating container list")
        elif resp.get_return_status() == INFO_FILE_NOT_FOUND:
            return HTTPNotFound(request=req, charset='utf-8')
        elif resp.get_return_status() == INFO_FILE_DELETED:
            headers = {'X-Account-Status': 'Deleted'}
            return HTTPNotFound(request=req,
                                headers=headers,
                                charset='utf-8',
                                body='')
        else:
            return HTTPInternalServerError(request=req)
        if out_content_type == 'application/json':
            data = []
            for (container_record, is_subdir) in container_record_list:
                if is_subdir:
                    data.append({'subdir': container_record.get_name()})
                else:
                    data.append({
                        'name':
                        container_record.get_name(),
                        'created_at':
                        time.strftime(
                            "%a, %d %b %Y %H:%M:%S GMT",
                            time.gmtime(
                                float(container_record.get_put_timestamp()))),
                        'count':
                        container_record.get_object_count(),
                        'bytes':
                        container_record.get_bytes_used()
                    })
            container_record_list = json.dumps(data)
        elif out_content_type.endswith('/xml'):
            #Directly used req.path to get account name.
            output_list = ['<?xml version="1.0" encoding="UTF-8"?>',
                           '<account name=%s>' % \
                           saxutils.quoteattr(req.path.split('/')[3])]
            for (container_record, is_subdir) in container_record_list:
                if is_subdir:
                    output_list.append(
                        '<subdir name=%s />' %
                        saxutils.quoteattr(container_record.get_name()))
                else:
                    item = '<container><name>%s</name><created_at>%s</created_at><count>%s</count>' \
                            '<bytes>%s</bytes></container>' % \
                            (saxutils.escape(container_record.get_name()), \
    time.strftime("%a, %d %b %Y %H:%M:%S GMT",time.gmtime(float(container_record.get_put_timestamp()))), \
                                container_record.get_object_count(), \
                                container_record.get_bytes_used()
                            )
                    output_list.append(item)
            output_list.append('</account>')
            container_record_list = '\n'.join(output_list)
        else:
            if not container_record_list:
                logger.debug("No any container in account")
                resp = HTTPNoContent(request=req, headers=resp_headers)
                resp.content_type = out_content_type
                resp.charset = 'utf-8'
                return resp
            container_record_list = '\n'.join(container_record.get_name() for \
            (container_record, is_subdir) in container_record_list) + '\n'
        ret = HTTPOk(body=container_record_list,
                     request=req,
                     headers=resp_headers)
        ret.content_type = out_content_type
        ret.charset = 'utf-8'
        return ret
示例#8
0
    def handle_request(self, req):
        """
        Entry point for proxy server.
        Should return a WSGI-style callable (such as swob.Response).

        :param req: swob.Request object
        """
        try:
            if 'swift.trans_id' not in req.environ:
                # if this wasn't set by an earlier middleware, set it now
                trans_id = generate_trans_id(self.trans_id_suffix)
                req.environ['swift.trans_id'] = trans_id
                self.logger.txn_id = trans_id
            self.logger.info(
                'Proxy service received request for %(method)s '
                '%(path)s with %(headers)s (txn: %(trans_id)s)', {
                    'method': req.method,
                    'path': req.path,
                    'headers': req.headers,
                    'trans_id': self.logger.txn_id
                })
            req.headers['x-trans-id'] = req.environ['swift.trans_id']
            self.logger.set_statsd_prefix('proxy-server')
            #if self.stop_service_flag:
            #    self.logger.info('Proxy is going to stop therefore no more request')
            #    return HTTPForbidden(request=req, body='Proxy is going to stop\
            #        therefore no more request')		#TODO would it be HTTPForbidden or something else
            if req.content_length and req.content_length < 0:
                self.logger.increment('errors')
                return HTTPBadRequest(request=req,
                                      body='Invalid Content-Length')

            try:
                if not check_utf8(req.path_info):
                    self.logger.increment('errors')
                    return HTTPPreconditionFailed(
                        request=req, body='Invalid UTF8 or contains NULL')
            except UnicodeError:
                self.logger.increment('errors')
                return HTTPPreconditionFailed(
                    request=req, body='Invalid UTF8 or contains NULL')

            try:
                error_response = constraints.check_non_allowed_headers(req)
                if error_response:
                    return error_response

                obj_list = ''
                if ('bulk-delete' in req.params or 'X-Bulk-Delete' in \
                req.headers) and req.method in ['POST', 'DELETE']:
                    self.logger.debug("*** Bulk delete request ***")
                    cont, obj_list = get_objs_to_delete(req, \
                    self.max_bulk_delete_entries, self.logger)
                    self.logger.debug("Container: %s, Obj list: %s" \
                    % (cont, obj_list))
                    version, account, container = \
                    split_path(req.path, 1, 3, True)
                    if container:
                        container = container.strip('/')
                        if cont != container:
                            self.logger.error("Container in path is different")
                            return HTTPBadRequest(request=req, \
                            body='Container name mismatch')
                        req.path_info = req.path.rstrip('/')
                    else:
                        req.path_info = os.path.join(req.path, cont)
                    req.method = 'BULK_DELETE'
                    req.headers['Content-Length'] = len(str(obj_list))
                    req.body = str(obj_list)

                controller, path_parts = self.get_controller(
                    req.path, req.method)
                p = req.path_info
                if isinstance(p, unicode):
                    p = p.encode('utf-8')
            except ValueError:
                self.logger.increment('errors')
                return HTTPNotFound(request=req)
            if not controller:
                self.logger.increment('errors')
                return HTTPPreconditionFailed(request=req, body='Bad URL')
            if self.deny_host_headers and \
                    req.host.split(':')[0] in self.deny_host_headers:
                return HTTPForbidden(request=req, body='Invalid host header')

            self.logger.set_statsd_prefix('proxy-server.' +
                                          controller.server_type.lower())
            controller = controller(self, **path_parts)
            controller.trans_id = req.environ['swift.trans_id']
            #self.logger.client_ip = get_remote_client(req)
            try:
                handler = getattr(controller, req.method)
                getattr(handler, 'publicly_accessible')
            except AttributeError:
                allowed_methods = getattr(controller, 'allowed_methods', set())
                return HTTPMethodNotAllowed(
                    request=req, headers={'Allow': ', '.join(allowed_methods)})
            if 'swift.authorize' in req.environ:
                # We call authorize before the handler, always. If authorized,
                # we remove the swift.authorize hook so isn't ever called
                # again. If not authorized, we return the denial unless the
                # controller's method indicates it'd like to gather more
                # information and try again later.
                resp = req.environ['swift.authorize'](req)
                if not resp:
                    # No resp means authorized, no delayed recheck required.
                    del req.environ['swift.authorize']
                else:
                    # Response indicates denial, but we might delay the denial
                    # and recheck later. If not delayed, return the error now.
                    if not getattr(handler, 'delay_denial', None):
                        return resp
            # Save off original request method (GET, POST, etc.) in case it
            # gets mutated during handling.  This way logging can display the
            # method the client actually sent.
            req.environ['swift.orig_req_method'] = req.method
            if not req.method == "STOP_SERVICE":
                self.ongoing_operation_list.append(req)
            resp = handler(req)
            self.logger.info(
                'Proxy returning response %(status)s for '
                '%(method)s %(path)s with %(headers)s '
                '(txn: %(trans_id)s)', {
                    'status': resp.status,
                    'method': req.method,
                    'path': req.path,
                    'headers': resp.headers,
                    'trans_id': req.headers['x-trans-id']
                })
            return resp
        except HTTPException as error_response:
            return error_response
        except (Exception, Timeout):
            self.logger.exception(_('ERROR Unhandled exception in request'))
            return HTTPServerError(request=req)
示例#9
0
    def DELETE(self, req):
        """HTTP DELETE request handler."""
        #        account_partition, accounts, container_count = \
        #            self.account_info(self.account_name, req)
        #        if not accounts:
        account_node, account_filesystem, account_directory, container_count, \
            account_component_number, head_status = self.account_info(\
            self.account_name, req)
        if not account_node and not head_status:
            return HTTPInternalServerError(request=req)
        if head_status and int(str(head_status).split()[0]) == 503:
            self.app.logger.info("account HEAD returning 503 service" \
                "unavailable error due to which this request got failed")
            return HTTPServiceUnavailable(request=req)
        if head_status and int(str(head_status).split()[0]) == 500:
            return HTTPInternalServerError(request=req)
        if not account_node:
            return HTTPNotFound(request=req)
#        container_partition, containers = self.app.container_ring.get_nodes(
#
#            self.account_name, self.container_name)
        container_node, container_filesystem, container_directory, \
            global_map_version, component_number = \
            self.app.container_ring.get_node( \
            self.account_name, self.container_name)
        if not len(container_node):
            self.app.logger.error(
                _('%(msg)s %(method)s %(path)s'), {
                    'msg': _('ERROR Wrong ring file content'),
                    'method': 'DELETE',
                    'path': req.path
                })
            return HTTPInternalServerError(request=req)
#        headers = self._backend_requests(req, len(containers),
#                                         account_partition, accounts)
        try:
            headers = self._backend_requests(req, len(container_node),
                                             account_node, account_filesystem,
                                             account_directory,
                                             global_map_version,
                                             component_number,
                                             account_component_number)
        except ZeroDivisionError:
            self.app.logger.error(
                _('%(msg)s %(method)s %(path)s'), {
                    'msg': _('ERROR Wrong ring file content'),
                    'method': 'DELETE',
                    'path': req.path
                })
            return HTTPInternalServerError(request=req)

        clear_info_cache(self.app, req.environ, self.account_name,
                         self.container_name)
        #        resp = self.make_requests(
        #            req, self.app.container_ring, container_partition, 'DELETE',
        #
        #            req.swift_entity_path, headers)
        if not self.is_req_blocked(_('Container'), component_number):
            #return HTTPTemporaryRedirect(request=req, body = 'Component'
            #    'movement is in progress')
            resp = self.make_requests(req, self.app.container_ring,
                                      container_node, container_filesystem,
                                      container_directory, 'DELETE',
                                      req.swift_entity_path, headers)
            resp = self.container_req_for_comp_distribution(req, \
                component_number, global_map_version, self.account_name, \
                self.container_name, resp)
            if int(resp.status.split()[0]) in (301, 307):
                resp = HTTPInternalServerError(request=req)
            # Indicates no server had the container
            if resp.status_int == HTTP_ACCEPTED:
                return HTTPNotFound(request=req)
            return resp
示例#10
0
    def POST(self, req):
        """HTTP POST request handler."""
        error_response = \
            self.clean_acls(req) or check_metadata(req, 'container')
        if error_response:
            return error_response
        if not req.environ.get('swift_owner'):
            for key in self.app.swift_owner_headers:
                req.headers.pop(key, None)
#        account_partition, accounts, container_count = \
#            self.account_info(self.account_name, req)
#        if not accounts:
        account_node, account_filesystem, account_directory, container_count, \
            account_component_number, head_status = self.account_info(\
            self.account_name, req)
        if not account_node and not head_status:
            return HTTPInternalServerError(request=req)
        if head_status and int(str(head_status).split()[0]) == 503:
            self.app.logger.info("account HEAD returning 503 service" \
                "unavailable error due to which this request got failed")
            return HTTPServiceUnavailable(request=req)
        if head_status and int(str(head_status).split()[0]) == 500:
            return HTTPInternalServerError(request=req)
        if not account_node:
            return HTTPNotFound(request=req)
#        container_partition, containers = self.app.container_ring.get_nodes(
#
#            self.account_name, self.container_name)
        container_node, container_filesystem, container_directory, \
            global_map_version, component_number = \
            self.app.container_ring.get_node( \
            self.account_name, self.container_name)
        if not len(container_node):
            self.app.logger.error(
                _('%(msg)s %(method)s %(path)s'), {
                    'msg': _('ERROR Wrong ring file content'),
                    'method': 'POST',
                    'path': req.path
                })
            return HTTPInternalServerError(request=req)
        headers = self.generate_request_headers(global_map_version,
                                                component_number,
                                                req,
                                                transfer=True)
        clear_info_cache(self.app, req.environ, self.account_name,
                         self.container_name)
        if not self.is_req_blocked(_('Container'), component_number):
            #return HTTPTemporaryRedirect(request=req, body = 'Component'
            #    'movement is in progress')
            #        resp = self.make_requests(
            #            req, self.app.container_ring, container_partition, 'POST',
            #
            #            req.swift_entity_path, [headers] * len(containers))
            resp = self.make_requests(req, self.app.container_ring,
                                      container_node, container_filesystem,
                                      container_directory, 'POST',
                                      req.swift_entity_path,
                                      [headers] * len(container_node))
            resp = self.container_req_for_comp_distribution(req, \
                component_number, global_map_version, self.account_name, \
                self.container_name, resp)
            if int(resp.status.split()[0]) in (301, 307):
                resp = HTTPInternalServerError(request=req)
            return resp
示例#11
0
    def PUT(self, req):
        """HTTP PUT request handler."""
        error_response = \
            self.clean_acls(req) or check_metadata(req, 'container')
        if error_response:
            return error_response
        if not req.environ.get('swift_owner'):
            for key in self.app.swift_owner_headers:
                req.headers.pop(key, None)
        if len(self.container_name) > MAX_CONTAINER_NAME_LENGTH:
            resp = HTTPBadRequest(request=req)
            resp.body = 'Container name length of %d longer than %d' % \
                        (len(self.container_name), MAX_CONTAINER_NAME_LENGTH)
            return resp
#        account_partition, accounts, container_count = \
#            self.account_info(self.account_name, req)
        account_node, account_filesystem, account_directory, container_count, \
             account_component_number, head_status = self.account_info(\
                                               self.account_name, req)
        #        if not accounts and self.app.account_autocreate:
        #            self.autocreate_account(req.environ, self.account_name)
        #            account_partition, accounts, container_count = \
        #                self.account_info(self.account_name, req)
        #        if not accounts:
        if not account_node and not head_status:
            return HTTPInternalServerError(request=req)
        if head_status and int(str(head_status).split()[0]) == 503:
            #TODO need to check why head_status is int or sometimes str
            self.app.logger.info("account HEAD returning 503 service " \
                "unavailable error due to which this request got failed")
            return HTTPServiceUnavailable(request=req)
        if head_status and int(str(head_status).split()[0]) == 500:
            return HTTPInternalServerError(request=req)

        if not account_node and self.app.account_autocreate:
            self.autocreate_account(req.environ, self.account_name)
            account_node, account_filesystem, account_directory, \
            container_count, account_component_number, head_status = \
            self.account_info(self.account_name, req)
            if not account_node and not head_status:
                return HTTPInternalServerError(request=req)
            if head_status and int(str(head_status).split()[0]) == 503:
                self.app.logger.info("account HEAD returning 503 service" \
                    "unavailable error due to which this request got failed")
                return HTTPServiceUnavailable(request=req)
            if head_status and int(str(head_status).split()[0]) == 500:
                return HTTPInternalServerError(request=req)

        if not account_node:
            return HTTPNotFound(request=req)
        if self.app.max_containers_per_account > 0 and \
                container_count >= self.app.max_containers_per_account and \
                self.account_name not in self.app.max_containers_whitelist:
            resp = HTTPForbidden(request=req)
            resp.body = 'Reached container limit of %s' % \
                        self.app.max_containers_per_account
            return resp
#        container_partition, containers = self.app.container_ring.get_nodes(
#            self.account_name, self.container_name)
        container_node, container_filesystem, container_directory, \
            global_map_version, component_number = \
            self.app.container_ring.get_node(self.account_name,
                                             self.container_name)
        if not len(container_node):
            self.app.logger.error(
                _('%(msg)s %(method)s %(path)s'), {
                    'msg': _('ERROR Wrong ring file content'),
                    'method': 'PUT',
                    'path': req.path
                })
            return HTTPInternalServerError(request=req)
#        headers = self._backend_requests(req, len(containers),
#                                         account_partition, accounts)
        try:
            headers = self._backend_requests(req, len(container_node),
                                             account_node, account_filesystem,
                                             account_directory,
                                             global_map_version,
                                             component_number,
                                             account_component_number)
        except ZeroDivisionError:
            self.app.logger.error(
                _('%(msg)s %(method)s %(path)s'), {
                    'msg': _('ERROR Wrong ring file content'),
                    'method': 'PUT',
                    'path': req.path
                })
            return HTTPInternalServerError(request=req)

        clear_info_cache(self.app, req.environ, self.account_name,
                         self.container_name)
        if not self.is_req_blocked(_('Container'), component_number):
            #return HTTPTemporaryRedirect(request=req, body = 'Component'
            #    'movement is in progress')
            #        resp = self.make_requests(
            #
            #
            #            container_partition, 'PUT', req.swift_entity_path, headers)
            resp = self.make_requests(req, self.app.container_ring,
                                      container_node, container_filesystem,
                                      container_directory, 'PUT',
                                      req.swift_entity_path, headers)
            resp = self.container_req_for_comp_distribution(req, \
                component_number, global_map_version, self.account_name, \
                self.container_name, resp)
            if int(resp.status.split()[0]) in (301, 307):
                resp = HTTPInternalServerError(request=req)
            return resp
示例#12
0
    def GETorHEAD(self, req):
        """Handler for HTTP GET/HEAD requests."""
        account_node, account_filesystem, account_directory, container_count, \
             account_component_number, head_status = self.account_info(\
                                               self.account_name, req)
        if not account_node and not head_status:
            return HTTPInternalServerError(request=req)
        if head_status and int(str(head_status).split()[0]) == 503:
            #TODO need to check why head_status is int or sometimes str
            self.app.logger.info("account HEAD returning 503 service " \
                "unavailable error due to which this request got failed")
            return HTTPServiceUnavailable(request=req)
        if head_status and int(str(head_status).split()[0]) == 500:
            return HTTPInternalServerError(request=req)
        if not account_node:
            return HTTPNotFound(request=req)
        path = get_param(req, 'path')
        #delimiter = get_param(req, 'delimiter')
        #prefix = get_param(req, 'prefix')
        limit = get_param(req, 'limit')
        if limit and not limit.isdigit():
            return HTTPPreconditionFailed(request=req,
                                          body='Value of limit must '
                                          'be a positive integer')
        if path:
            return HTTPBadRequest(request=req,
                                  body='Unsupported query parameter '
                                  'path')
#        part = self.app.container_ring.get_part(
#            self.account_name, self.container_name)
        node, filesystem, directory, global_map_version, component_number = \
            self.app.container_ring.get_node(self.account_name, \
            self.container_name)
        if not len(node):
            self.app.logger.error(
                _('%(msg)s %(method)s %(path)s'), {
                    'msg': _('ERROR Wrong ring file content'),
                    'method': 'GET/HEAD',
                    'path': req.path
                })
            return HTTPInternalServerError(request=req)
        #TODO Currently same component request is blocked until previous same
        #component request's map version is not updated, need to check other provision
        if not self.is_req_blocked(_('Container'), component_number):
            #return HTTPTemporaryRedirect(request=req, body = 'Component'
            #    'movement is in progress')
            #        resp = self.GETorHEAD_base(
            #            req, _('Container'), self.app.container_ring, part,
            #            req.swift_entity_path)
            resp = self.GETorHEAD_base(req, _('Container'), \
                self.app.container_ring, node, filesystem, directory, \
                req.swift_entity_path, global_map_version, component_number)
            resp = self.container_req_for_comp_distribution(req, \
                component_number, global_map_version, self.account_name, \
                self.container_name, resp)
            if int(resp.status.split()[0]) in (301, 307):
                resp = HTTPInternalServerError(request=req)
            if 'swift.authorize' in req.environ:
                req.acl = resp.headers.get('x-container-read')
                aresp = req.environ['swift.authorize'](req)
                if aresp:
                    return aresp
            if not req.environ.get('swift_owner', False):
                for key in self.app.swift_owner_headers:
                    if key in resp.headers:
                        del resp.headers[key]
            return resp
示例#13
0
    def authorize(self, req):
        env = req.environ
        env_identity = self._integral_keystone_identity(env)
        tenant_id, tenant_name = env_identity['tenant']
        user_id, user_name = env_identity['user']
        referrers, roles = osd_acl.parse_acl(getattr(req, 'acl', None))

        #allow OPTIONS requests to proceed as normal
        if req.method == 'OPTIONS':
            return

        try:
            part = req.split_path(1, 4, True)
            version, account, container, obj = part
        except ValueError:
            return HTTPNotFound(request=req)

        user_roles = [r.lower() for r in env_identity.get('roles', [])]

        # Give unconditional access to a user with the reseller_admin
        # role.
        if self.reseller_admin_role in user_roles:
            msg = 'User %s has reseller admin authorizing'
            self.logger.debug(msg, tenant_id)
            req.environ['swift_owner'] = True
            return

        # if we are not reseller admin and user is trying to post metadata
        #X-Account-Meta-Max-Read-Bandwidth or X-Account-Meta-Max-Write-Bandwidth
        # on account then deny it.
        if not container and not obj and (req.method == 'POST' or \
            req.method == 'PUT') and self.authorize_for_bandwidth_limit(req):
            msg = 'User %s:%s is not allowed to post this metadata on account'
            self.logger.debug(msg, tenant_name, user_name)
            return self.denied_response(req)

        # If we are not reseller admin and user is trying to delete its own
        # account then deny it.
        if not container and not obj and req.method == 'DELETE':
            # User is not allowed to issue a DELETE on its own account
            msg = 'User %s:%s is not allowed to delete its own account'
            self.logger.debug(msg, tenant_name, user_name)
            return self.denied_response(req)

        # cross-tenant authorization
        matched_acl = self._authorize_cross_tenant(user_id, user_name,
                                                   tenant_id, tenant_name,
                                                   roles)
        if matched_acl is not None:
            log_msg = 'user %s allowed in ACL authorizing.'
            self.logger.debug(log_msg, matched_acl)
            return

        acl_authorized = self._authorize_unconfirmed_identity(
            req, obj, referrers, roles)
        if acl_authorized:
            return

        # Check if a user tries to access an account that does not match their
        # token
        if not self._reseller_check(account, tenant_id):
            log_msg = 'tenant mismatch: %s != %s'
            self.logger.debug(log_msg, account, tenant_id)
            return self.denied_response(req)

        # Check the roles the user is belonging to. If the user is
        # part of the role defined in the config variable
        # operator_roles (like admin) then it will be
        # promoted as an admin of the account/tenant.
        for role in self.operator_roles.split(','):
            role = role.strip()
            if role in user_roles:
                log_msg = 'allow user with role %s as account admin'
                self.logger.debug(log_msg, role)
                req.environ['swift_owner'] = True
                return

        # If user is of the same name of the tenant then make owner of it.
        if self.is_admin and user_name == tenant_name:
            self.logger.warning("the is_admin feature has been deprecated "
                                "and will be removed in the future "
                                "update your config file")
            req.environ['swift_owner'] = True
            return

        if acl_authorized is not None:
            return self.denied_response(req)

        # Check if we have the role in the userroles and allow it
        for user_role in user_roles:
            if user_role in (r.lower() for r in roles):
                log_msg = 'user %s:%s allowed in ACL: %s authorizing'
                self.logger.debug(log_msg, tenant_name, user_name, user_role)
                return

        if "_member_" in user_roles and env.get('swift.source',
                                                '').startswith('S3'):
            log_msg = 'user %s:%s with _member_ role allowed'
            self.logger.debug(log_msg, tenant_name, user_name)
            return

        return self.denied_response(req)
示例#14
0
    def handle_get_token(self, req):
        """
        Handles the various `request for token and service end point(s)` calls.
        There are various formats to support the various auth servers in the
        past. Examples::

            GET <auth-prefix>/v1/<act>/auth
                X-Auth-User: <act>:<usr>  or  X-Storage-User: <usr>
                X-Auth-Key: <key>         or  X-Storage-Pass: <key>
            GET <auth-prefix>/auth
                X-Auth-User: <act>:<usr>  or  X-Storage-User: <act>:<usr>
                X-Auth-Key: <key>         or  X-Storage-Pass: <key>
            GET <auth-prefix>/v1.0
                X-Auth-User: <act>:<usr>  or  X-Storage-User: <act>:<usr>
                X-Auth-Key: <key>         or  X-Storage-Pass: <key>

        On successful authentication, the response will have X-Auth-Token and
        X-Storage-Token set to the token to use with Swift and X-Storage-URL
        set to the URL to the default Swift cluster to use.

        :param req: The swob.Request to process.
        :returns: swob.Response, 2xx on success with data set as explained
                  above.
        """
        # Validate the request info
        try:
            pathsegs = split_path(req.path_info, 1, 3, True)
        except ValueError:
            self.logger.increment('errors')
            return HTTPNotFound(request=req)
        if pathsegs[0] == 'v1' and pathsegs[2] == 'auth':
            account = pathsegs[1]
            user = req.headers.get('x-storage-user')
            if not user:
                user = req.headers.get('x-auth-user')
                if not user or ':' not in user:
                    self.logger.increment('token_denied')
                    return HTTPUnauthorized(request=req,
                                            headers={
                                                'Www-Authenticate':
                                                'Swift realm="%s"' % account
                                            })
                account2, user = user.split(':', 1)
                if account != account2:
                    self.logger.increment('token_denied')
                    return HTTPUnauthorized(request=req,
                                            headers={
                                                'Www-Authenticate':
                                                'Swift realm="%s"' % account
                                            })
            key = req.headers.get('x-storage-pass')
            if not key:
                key = req.headers.get('x-auth-key')
        elif pathsegs[0] in ('auth', 'v1.0'):
            user = req.headers.get('x-auth-user')
            if not user:
                user = req.headers.get('x-storage-user')
            if not user or ':' not in user:
                self.logger.increment('token_denied')
                return HTTPUnauthorized(
                    request=req,
                    headers={'Www-Authenticate': 'Swift realm="unknown"'})
            account, user = user.split(':', 1)
            key = req.headers.get('x-auth-key')
            if not key:
                key = req.headers.get('x-storage-pass')
        else:
            return HTTPBadRequest(request=req)
        if not all((account, user, key)):
            self.logger.increment('token_denied')
            realm = account or 'unknown'
            return HTTPUnauthorized(
                request=req,
                headers={'Www-Authenticate': 'Swift realm="%s"' % realm})
        # Authenticate user
        account_user = account + ':' + user
        if account_user not in self.users:
            self.logger.increment('token_denied')
            return HTTPUnauthorized(
                request=req,
                headers={'Www-Authenticate': 'Swift realm="%s"' % account})
        if self.users[account_user]['key'] != key:
            self.logger.increment('token_denied')
            return HTTPUnauthorized(
                request=req,
                headers={'Www-Authenticate': 'Swift realm="unknown"'})
        account_id = self.users[account_user]['url'].rsplit('/', 1)[-1]
        # Get memcache client
        memcache_client = cache_from_env(req.environ)
        if not memcache_client:
            raise Exception('Memcache required')
        # See if a token already exists and hasn't expired
        token = None
        memcache_user_key = '%s/user/%s' % (self.reseller_prefix, account_user)
        candidate_token = memcache_client.get(memcache_user_key)
        if candidate_token:
            memcache_token_key = \
                '%s/token/%s' % (self.reseller_prefix, candidate_token)
            cached_auth_data = memcache_client.get(memcache_token_key)
            if cached_auth_data:
                expires, old_groups = cached_auth_data
                old_groups = old_groups.split(',')
                new_groups = self._get_user_groups(account, account_user,
                                                   account_id)

                if expires > time() and \
                        set(old_groups) == set(new_groups.split(',')):
                    token = candidate_token
        # Create a new token if one didn't exist
        if not token:
            # Generate new token
            token = '%stk%s' % (self.reseller_prefix, uuid4().hex)
            expires = time() + self.token_life
            groups = self._get_user_groups(account, account_user, account_id)
            # Save token
            memcache_token_key = '%s/token/%s' % (self.reseller_prefix, token)
            memcache_client.set(memcache_token_key, (expires, groups),
                                time=float(expires - time()))
            # Record the token with the user info for future use.
            memcache_user_key = \
                '%s/user/%s' % (self.reseller_prefix, account_user)
            memcache_client.set(memcache_user_key,
                                token,
                                time=float(expires - time()))
        resp = Response(request=req,
                        headers={
                            'x-auth-token': token,
                            'x-storage-token': token
                        })
        url = self.users[account_user]['url'].replace('$HOST', resp.host_url)
        if self.storage_url_scheme != 'default':
            url = self.storage_url_scheme + ':' + url.split(':', 1)[1]
        resp.headers['x-storage-url'] = url
        return resp
示例#15
0
    def authorize(self, req):
        """
        Returns None if the request is authorized to continue or a standard
        WSGI response callable if not.
        """

        try:
            _junk, account, container, obj = req.split_path(1, 4, True)
        except ValueError:
            self.logger.increment('errors')
            return HTTPNotFound(request=req)

        if not account or not account.startswith(self.reseller_prefix):
            self.logger.debug("Account name: %s doesn't start with "
                              "reseller_prefix: %s." %
                              (account, self.reseller_prefix))
            return self.denied_response(req)

        # At this point, TempAuth is convinced that it is authoritative.
        # If you are sending an ACL header, it must be syntactically valid
        # according to TempAuth's rules for ACL syntax.
        acl_data = req.headers.get('x-account-access-control')
        if acl_data is not None:
            error = self.extract_acl_and_report_errors(req)
            if error:
                msg = 'X-Account-Access-Control invalid: %s\n\nInput: %s\n' % (
                    error, acl_data)
                headers = [('Content-Type', 'text/plain; charset=UTF-8')]
                return HTTPBadRequest(request=req, headers=headers, body=msg)

        user_groups = (req.remote_user or '').split(',')
        account_user = user_groups[1] if len(user_groups) > 1 else None

        if '.reseller_admin' in user_groups and \
                account != self.reseller_prefix and \
                account[len(self.reseller_prefix)] != '.':
            req.environ['swift_owner'] = True
            self.logger.debug("User %s has reseller admin authorizing." %
                              account_user)
            return None

        # if we are not reseller admin and user is trying to post metadata
        #X-Account-Meta-Max-Read-Bandwidth or X-Account-Meta-Max-Write-Bandwidth
        # on account then deny it.
        if not container and not obj and (req.method == 'POST' or \
            req.method == 'PUT') and self.authorize_for_bandwidth_limit(req):
            self.logger.debug('User %s is not allowed to post this metadata'
                              'on account' % account_user)
            return self.denied_response(req)

        if account in user_groups and \
                (req.method not in ('DELETE', 'PUT') or container):
            # If the user is admin for the account and is not trying to do an
            # account DELETE or PUT...
            req.environ['swift_owner'] = True
            self.logger.debug("User %s has admin authorizing." % account_user)
            return None

        if (req.environ.get('swift_sync_key')
                and (req.environ['swift_sync_key'] == req.headers.get(
                    'x-container-sync-key', None))
                and 'x-timestamp' in req.headers):
            self.logger.debug("Allow request with container sync-key: %s." %
                              req.environ['swift_sync_key'])
            return None

        if req.method == 'OPTIONS':
            #allow OPTIONS requests to proceed as normal
            self.logger.debug("Allow OPTIONS request.")
            return None

        referrers, groups = parse_acl(getattr(req, 'acl', None))

        if referrer_allowed(req.referer, referrers):
            if obj or '.rlistings' in groups:
                self.logger.debug("Allow authorizing %s via referer ACL." %
                                  req.referer)
                return None

        for user_group in user_groups:
            if user_group in groups:
                self.logger.debug("User %s allowed in ACL: %s authorizing." %
                                  (account_user, user_group))
                return None

        # Check for access via X-Account-Access-Control
        acct_acls = self.account_acls(req)
        if acct_acls:
            # At least one account ACL is set in this account's sysmeta data,
            # so we should see whether this user is authorized by the ACLs.
            user_group_set = set(user_groups)
            if user_group_set.intersection(acct_acls['admin']):
                req.environ['swift_owner'] = True
                self.logger.debug('User %s allowed by X-Account-Access-Control'
                                  ' (admin)' % account_user)
                return None
            if (user_group_set.intersection(acct_acls['read-write'])
                    and (container or req.method in ('GET', 'HEAD'))):
                # The RW ACL allows all operations to containers/objects, but
                # only GET/HEAD to accounts (and OPTIONS, above)
                self.logger.debug('User %s allowed by X-Account-Access-Control'
                                  ' (read-write)' % account_user)
                return None
            if (user_group_set.intersection(acct_acls['read-only'])
                    and req.method in ('GET', 'HEAD')):
                self.logger.debug('User %s allowed by X-Account-Access-Control'
                                  ' (read-only)' % account_user)
                return None

        return self.denied_response(req)