Пример #1
0
class AccountController(object):
    """WSGI controller for the account server."""

    def __init__(self, conf):
        self.logger = get_logger(conf, log_route='account-server')
        self.root = conf.get('devices', '/srv/node')
        self.mount_check = conf.get('mount_check', 'true').lower() in \
                              ('true', 't', '1', 'on', 'yes', 'y')
        self.replicator_rpc = \
            ReplicatorRpc(self.root, DATADIR, AccountBroker, self.mount_check)

    def _get_account_broker(self, drive, part, account):
        hsh = hash_path(account)
        db_dir = storage_directory(DATADIR, part, hsh)
        db_path = os.path.join(self.root, drive, db_dir, hsh + '.db')
        return AccountBroker(db_path, account=account, logger=self.logger)

    def DELETE(self, req):
        """Handle HTTP DELETE request."""
        try:
            drive, part, account = split_path(unquote(req.path), 3)
        except ValueError, err:
            return HTTPBadRequest(body=str(err), content_type='text/plain',
                                                    request=req)
        if self.mount_check and not check_mount(self.root, drive):
            return Response(status='507 %s is not mounted' % drive)
        if 'x-timestamp' not in req.headers or \
                    not check_float(req.headers['x-timestamp']):
            return HTTPBadRequest(body='Missing timestamp', request=req,
                        content_type='text/plain')
        broker = self._get_account_broker(drive, part, account)
        if broker.is_deleted():
            return HTTPNotFound(request=req)
        broker.delete_db(req.headers['x-timestamp'])
        return HTTPNoContent(request=req)
Пример #2
0
 def complete_rsync(self, drive, db_file, args):
     old_filename = os.path.join(self.root, drive, 'tmp', args[0])
     if os.path.exists(db_file):
         return HTTPNotFound()
     if not os.path.exists(old_filename):
         return HTTPNotFound()
     broker = self.broker_class(old_filename)
     broker.newid(args[0])
     renamer(old_filename, db_file)
     return HTTPNoContent()
Пример #3
0
 def __call__(self, env, start_response):
     if env['REQUEST_METHOD'] == 'GET':
         if self.status == 200:
             start_response(Response().status)
             start_response({'Content-Type': 'text/xml'})
             json_pattern = [
                 '"name":%s', '"last_modified":%s', '"hash":%s',
                 '"bytes":%s'
             ]
             json_pattern = '{' + ','.join(json_pattern) + '}'
             json_out = []
             for b in self.objects:
                 name = simplejson.dumps(b[0])
                 time = simplejson.dumps(b[1])
                 json_out.append(json_pattern % (name, time, b[2], b[3]))
             account_list = '[' + ','.join(json_out) + ']'
             return account_list
         elif self.status == 401:
             start_response(HTTPUnauthorized().status)
             start_response({})
         elif self.status == 404:
             start_response(HTTPNotFound().status)
             start_response({})
         else:
             start_response(HTTPBadRequest().status)
             start_response({})
     elif env['REQUEST_METHOD'] == 'PUT':
         if self.status == 201:
             start_response(HTTPCreated().status)
             start_response({})
         elif self.status == 401:
             start_response(HTTPUnauthorized().status)
             start_response({})
         elif self.status == 202:
             start_response(HTTPAccepted().status)
             start_response({})
         else:
             start_response(HTTPBadRequest().status)
             start_response({})
     elif env['REQUEST_METHOD'] == 'DELETE':
         if self.status == 204:
             start_response(HTTPNoContent().status)
             start_response({})
         elif self.status == 401:
             start_response(HTTPUnauthorized().status)
             start_response({})
         elif self.status == 404:
             start_response(HTTPNotFound().status)
             start_response({})
         elif self.status == 409:
             start_response(HTTPConflict().status)
             start_response({})
         else:
             start_response(HTTPBadRequest().status)
             start_response({})
Пример #4
0
def entry_update(slug):
    bucket = riak.bucket("entries")
    username = request.remote_user

    data = json.loads(request.body)
    # validate in someway
    data['author'] = username

    obj = bucket.new(slug, data=data)
    obj.store()
    return HTTPNoContent()
Пример #5
0
class AccountController(object):
    """WSGI controller for the account server."""
    def __init__(self, conf):
        self.logger = get_logger(conf, log_route='account-server')
        self.root = conf.get('devices', '/srv/node')
        self.mount_check = conf.get('mount_check', 'true').lower() in \
                              ('true', 't', '1', 'on', 'yes', 'y')
        self.replicator_rpc = ReplicatorRpc(self.root,
                                            DATADIR,
                                            AccountBroker,
                                            self.mount_check,
                                            logger=self.logger)
        self.auto_create_account_prefix = \
            conf.get('auto_create_account_prefix') or '.'
        swift.common.db.DB_PREALLOCATION = \
            conf.get('db_preallocation', 'f').lower() in TRUE_VALUES

    def _get_account_broker(self, drive, part, account):
        hsh = hash_path(account)
        db_dir = storage_directory(DATADIR, part, hsh)
        db_path = os.path.join(self.root, drive, db_dir, hsh + '.db')
        return AccountBroker(db_path, account=account, logger=self.logger)

    @public
    def DELETE(self, req):
        """Handle HTTP DELETE request."""
        start_time = time.time()
        try:
            drive, part, account = split_path(unquote(req.path), 3)
            validate_device_partition(drive, part)
        except ValueError, err:
            self.logger.increment('DELETE.errors')
            return HTTPBadRequest(body=str(err),
                                  content_type='text/plain',
                                  request=req)
        if self.mount_check and not check_mount(self.root, drive):
            self.logger.increment('DELETE.errors')
            return HTTPInsufficientStorage(drive=drive, request=req)
        if 'x-timestamp' not in req.headers or \
                    not check_float(req.headers['x-timestamp']):
            self.logger.increment('DELETE.errors')
            return HTTPBadRequest(body='Missing timestamp',
                                  request=req,
                                  content_type='text/plain')
        broker = self._get_account_broker(drive, part, account)
        if broker.is_deleted():
            self.logger.timing_since('DELETE.timing', start_time)
            return HTTPNotFound(request=req)
        broker.delete_db(req.headers['x-timestamp'])
        self.logger.timing_since('DELETE.timing', start_time)
        return HTTPNoContent(request=req)
Пример #6
0
def resource_options_view(obj, request, resource, smart=True):
    methods = set()
    q = Query('json').filter(model=type(obj))
    for action, res in list(q(request.app)):
        if 'request_method' in action.predicates:
            methods.add(action.predicates.get('request_method'))

    @request.after
    def _after(response):
        response.headers.add(
            'Access-Control-Allow-Methods',
            ', '.join(sorted(methods))
        )
    return HTTPNoContent()
Пример #7
0
    def handle_add_user(self, request):
        """
        Handles Rest requests from developers to have a user added. If the
        account specified doesn't exist, it will also be added. Currently,
        updating a user's information (password, admin access) must be done by
        directly updating the sqlite database.

        Valid URL paths:
            * PUT /account/<account-name>/<user-name> - create the account

        Valid headers:
            * X-Auth-User-Key: <password>
            * X-Auth-User-Admin: <true|false>
            * X-Auth-User-Reseller-Admin: <true|false>

        If the HTTP request returns with a 204, then the user was added,
        and the storage url will be available in the X-Storage-Url header.

        :param request: webob.Request object
        """
        try:
            _junk, account_name, user_name = \
                split_path(request.path, minsegs=3)
        except ValueError:
            return HTTPBadRequest()
        create_reseller_admin = \
            request.headers.get('x-auth-user-reseller-admin') == 'true'
        if create_reseller_admin and (
                request.headers.get('X-Auth-Admin-User') != '.super_admin'
                or request.headers.get('X-Auth-Admin-Key') !=
                self.super_admin_key):
            return HTTPUnauthorized(request=request)
        create_account_admin = \
            request.headers.get('x-auth-user-admin') == 'true'
        if create_account_admin and \
                not self.is_account_admin(request, account_name):
            return HTTPForbidden(request=request)
        if 'X-Auth-User-Key' not in request.headers:
            return HTTPBadRequest(body='X-Auth-User-Key is required')
        password = request.headers['x-auth-user-key']
        storage_url = self.create_user(account_name, user_name, password,
                                       create_account_admin,
                                       create_reseller_admin)
        if storage_url == 'already exists':
            return HTTPConflict(body=storage_url)
        if not storage_url:
            return HTTPServiceUnavailable()
        return HTTPNoContent(headers={'x-storage-url': storage_url})
Пример #8
0
 def rsync_then_merge(self, drive, db_file, args):
     old_filename = os.path.join(self.root, drive, 'tmp', args[0])
     if not os.path.exists(db_file) or not os.path.exists(old_filename):
         return HTTPNotFound()
     new_broker = self.broker_class(old_filename)
     existing_broker = self.broker_class(db_file)
     point = -1
     objects = existing_broker.get_items_since(point, 1000)
     while len(objects):
         new_broker.merge_items(objects)
         point = objects[-1]['ROWID']
         objects = existing_broker.get_items_since(point, 1000)
         sleep()
     new_broker.newid(args[0])
     renamer(old_filename, db_file)
     return HTTPNoContent()
Пример #9
0
 def __call__(self, env, start_response):
     if env['REQUEST_METHOD'] == 'GET' or env['REQUEST_METHOD'] == 'HEAD':
         if self.status == 200:
             start_response(Response().status)
             start_response(self.response_headers)
             if env['REQUEST_METHOD'] == 'GET':
                 return self.object_body
         elif self.status == 401:
             start_response(HTTPUnauthorized().status)
             start_response({})
         elif self.status == 404:
             start_response(HTTPNotFound().status)
             start_response({})
         else:
             start_response(HTTPBadRequest().status)
             start_response({})
     elif env['REQUEST_METHOD'] == 'PUT':
         if self.status == 201:
             start_response(HTTPCreated().status)
             start_response({'etag': self.response_headers['etag']})
         elif self.status == 401:
             start_response(HTTPUnauthorized().status)
             start_response({})
         elif self.status == 404:
             start_response(HTTPNotFound().status)
             start_response({})
         else:
             start_response(HTTPBadRequest().status)
             start_response({})
     elif env['REQUEST_METHOD'] == 'DELETE':
         if self.status == 204:
             start_response(HTTPNoContent().status)
             start_response({})
         elif self.status == 401:
             start_response(HTTPUnauthorized().status)
             start_response({})
         elif self.status == 404:
             start_response(HTTPNotFound().status)
             start_response({})
         else:
             start_response(HTTPBadRequest().status)
             start_response({})
Пример #10
0
 def __call__(self, env, start_response):
     if env['REQUEST_METHOD'] == 'GET' or env['REQUEST_METHOD'] == 'HEAD':
         if self.status == 200:
             if 'HTTP_RANGE' in env:
                 resp = Response(body=self.object_body,
                                 conditional_response=True)
                 return resp(env, start_response)
             start_response(Response().status,
                            self.response_headers.items())
             if env['REQUEST_METHOD'] == 'GET':
                 return self.object_body
         elif self.status == 401:
             start_response(HTTPUnauthorized().status, [])
         elif self.status == 404:
             start_response(HTTPNotFound().status, [])
         else:
             start_response(HTTPBadRequest().status, [])
     elif env['REQUEST_METHOD'] == 'PUT':
         if self.status == 201:
             start_response(HTTPCreated().status,
                            [('etag', self.response_headers['etag'])])
         elif self.status == 401:
             start_response(HTTPUnauthorized().status, [])
         elif self.status == 404:
             start_response(HTTPNotFound().status, [])
         else:
             start_response(HTTPBadRequest().status, [])
     elif env['REQUEST_METHOD'] == 'DELETE':
         if self.status == 204:
             start_response(HTTPNoContent().status, [])
         elif self.status == 401:
             start_response(HTTPUnauthorized().status, [])
         elif self.status == 404:
             start_response(HTTPNotFound().status, [])
         else:
             start_response(HTTPBadRequest().status, [])
     return []
Пример #11
0
    def handle_token(self, request):
        """
        Handles ReST requests from Swift to validate tokens

        Valid URL paths:
            * GET /token/<token>

        If the HTTP request returns with a 204, then the token is valid, the
        TTL of the token will be available in the X-Auth-Ttl header, and a
        comma separated list of the "groups" the user belongs to will be in the
        X-Auth-Groups header.

        :param request: webob.Request object
        """
        try:
            _junk, token = split_path(request.path, minsegs=2)
        except ValueError:
            return HTTPBadRequest()
        # Retrieves (TTL, account, user, cfaccount) if valid, False otherwise
        headers = {}
        if 'Authorization' in request.headers:
            validation = self.validate_s3_sign(request, token)
            if validation:
                headers['X-Auth-Account-Suffix'] = validation[3]
        else:
            validation = self.validate_token(token)
        if not validation:
            return HTTPNotFound()
        groups = ['%s:%s' % (validation[1], validation[2]), validation[1]]
        if validation[3]:
            # admin access to a cfaccount or ".reseller_admin" to access to all
            # accounts, including creating new ones.
            groups.append(validation[3])
        headers['X-Auth-TTL'] = validation[0]
        headers['X-Auth-Groups'] = ','.join(groups)
        return HTTPNoContent(headers=headers)
Пример #12
0
            raise HTTPForbidden()
        elif req.context.owner is None:
            raise HTTPUnauthorized(_("No authenticated user"))

        try:
            registry.replace_members(self.options, req.context, image_id, body)
        except exception.NotFound, e:
            msg = "%s" % e
            logger.debug(msg)
            raise HTTPNotFound(msg, request=req, content_type='text/plain')
        except exception.NotAuthorized, e:
            msg = "%s" % e
            logger.debug(msg)
            raise HTTPNotFound(msg, request=req, content_type='text/plain')

        return HTTPNoContent()

    def add_member(self, req, image_id, member, body=None):
        """
        Adds a membership to the image, or updates an existing one.
        If a body is present, it is a dict with the following format::

            {"member": {
                "can_share": [True|False]
            }}

        If "can_share" is provided, the member's ability to share is
        set accordingly.  If it is not provided, existing memberships
        remain unchanged and new memberships default to False.
        """
        if req.context.read_only:
Пример #13
0
 if self.mount_check and not check_mount(self.root, drive):
     return Response(status='507 %s is not mounted' % drive)
 broker = self._get_account_broker(drive, part, account)
 if container:  # put account container
     if 'x-cf-trans-id' in req.headers:
         broker.pending_timeout = 3
     if req.headers.get('x-account-override-deleted', 'no').lower() != \
             'yes' and broker.is_deleted():
         return HTTPNotFound(request=req)
     broker.put_container(container, req.headers['x-put-timestamp'],
                          req.headers['x-delete-timestamp'],
                          req.headers['x-object-count'],
                          req.headers['x-bytes-used'])
     if req.headers['x-delete-timestamp'] > \
             req.headers['x-put-timestamp']:
         return HTTPNoContent(request=req)
     else:
         return HTTPCreated(request=req)
 else:  # put account
     timestamp = normalize_timestamp(req.headers['x-timestamp'])
     if not os.path.exists(broker.db_file):
         broker.initialize(timestamp)
         created = True
     elif broker.is_status_deleted():
         return HTTPForbidden(request=req, body='Recently deleted')
     else:
         created = broker.is_deleted()
         broker.update_put_timestamp(timestamp)
         if broker.is_deleted():
             return HTTPConflict(request=req)
     metadata = {}
Пример #14
0
 def test_204(self, *args, **kw):
     from webob.exc import HTTPNoContent
     raise HTTPNoContent()
Пример #15
0
    def handle_auth(self, request):
        """
        Handles ReST requests from end users for a Swift cluster url and auth
        token. This can handle all the various headers and formats that
        existing auth systems used, so it's a bit of a chameleon.

        Valid URL paths:
            * GET /v1/<account-name>/auth
            * GET /auth
            * GET /v1.0

        Valid headers:
            * X-Auth-User: <account-name>:<user-name>
            * X-Auth-Key: <password>
            * X-Storage-User: [<account-name>:]<user-name>
                    The [<account-name>:] is only optional here if the
                    /v1/<account-name>/auth path is used.
            * X-Storage-Pass: <password>

        The (currently) preferred method is to use /v1.0 path and the
        X-Auth-User and X-Auth-Key headers.

        :param request: A webob.Request instance.
        """
        try:
            pathsegs = split_path(request.path,
                                  minsegs=1,
                                  maxsegs=3,
                                  rest_with_last=True)
        except ValueError:
            return HTTPBadRequest()
        if pathsegs[0] == 'v1' and pathsegs[2] == 'auth':
            account = pathsegs[1]
            user = request.headers.get('x-storage-user')
            if not user:
                user = request.headers.get('x-auth-user')
                if not user or ':' not in user:
                    return HTTPUnauthorized()
                account2, user = user.split(':', 1)
                if account != account2:
                    return HTTPUnauthorized()
            password = request.headers.get('x-storage-pass')
            if not password:
                password = request.headers.get('x-auth-key')
        elif pathsegs[0] in ('auth', 'v1.0'):
            user = request.headers.get('x-auth-user')
            if not user:
                user = request.headers.get('x-storage-user')
            if not user or ':' not in user:
                return HTTPUnauthorized()
            account, user = user.split(':', 1)
            password = request.headers.get('x-auth-key')
            if not password:
                password = request.headers.get('x-storage-pass')
        else:
            return HTTPBadRequest()
        if not all((account, user, password)):
            return HTTPUnauthorized()
        self.purge_old_tokens()
        with self.get_conn() as conn:
            row = conn.execute(
                '''
                SELECT cfaccount, url, admin, reseller_admin FROM account
                WHERE account = ? AND user = ? AND password = ?''',
                (account, user, password)).fetchone()
            if row is None:
                return HTTPUnauthorized()
            cfaccount = row[0]
            url = row[1]
            admin = row[2] == 't'
            reseller_admin = row[3] == 't'
            row = conn.execute(
                '''
                SELECT token FROM token WHERE account = ? AND user = ?''',
                (account, user)).fetchone()
            if row:
                token = row[0]
            else:
                token = '%stk%s' % (self.reseller_prefix, uuid4().hex)
                token_cfaccount = ''
                if admin:
                    token_cfaccount = cfaccount
                if reseller_admin:
                    token_cfaccount = '.reseller_admin'
                conn.execute(
                    '''
                    INSERT INTO token
                    (token, created, account, user, cfaccount)
                    VALUES (?, ?, ?, ?, ?)''',
                    (token, time(), account, user, token_cfaccount))
                conn.commit()
            return HTTPNoContent(
                headers={
                    'x-auth-token': token,
                    'x-storage-token': token,
                    'x-storage-url': url
                })
Пример #16
0
def entry_delete(slug):
    bucket = riak.bucket("entries")
    obj = bucket.new(slug)
    obj.delete()
    return HTTPNoContent()
Пример #17
0
class ContainerController(object):
    """WSGI Controller for the container server."""

    # Ensure these are all lowercase
    save_headers = [
        'x-container-read', 'x-container-write', 'x-container-sync-key',
        'x-container-sync-to'
    ]

    def __init__(self, conf):
        self.logger = get_logger(conf, log_route='container-server')
        self.root = conf.get('devices', '/srv/node/')
        self.mount_check = conf.get('mount_check', 'true').lower() in \
                              ('true', 't', '1', 'on', 'yes', 'y')
        self.node_timeout = int(conf.get('node_timeout', 3))
        self.conn_timeout = float(conf.get('conn_timeout', 0.5))
        self.allowed_sync_hosts = [
            h.strip()
            for h in conf.get('allowed_sync_hosts', '127.0.0.1').split(',')
            if h.strip()
        ]
        self.replicator_rpc = ReplicatorRpc(self.root,
                                            DATADIR,
                                            ContainerBroker,
                                            self.mount_check,
                                            logger=self.logger)
        self.auto_create_account_prefix = \
            conf.get('auto_create_account_prefix') or '.'
        self.fs_object = None

    def _get_container_broker(self, drive, part, account, container):
        """
        Get a DB broker for the container.

        :param drive: drive that holds the container
        :param part: partition the container is in
        :param account: account name
        :param container: container name
        :returns: ContainerBroker object
        """
        if self.fs_object:
            return DiskDir(self.root,
                           drive,
                           part,
                           account,
                           container,
                           self.logger,
                           fs_object=self.fs_object)

        hsh = hash_path(account, container)
        db_dir = storage_directory(DATADIR, part, hsh)
        db_path = os.path.join(self.root, drive, db_dir, hsh + '.db')
        return ContainerBroker(db_path,
                               account=account,
                               container=container,
                               logger=self.logger)

    def account_update(self, req, account, container, broker):
        """
        Update the account server with latest container info.

        :param req: webob.Request object
        :param account: account name
        :param container: container name
        :param borker: container DB broker object
        :returns: if the account request returns a 404 error code,
                  HTTPNotFound response object, otherwise None.
        """
        account_host = req.headers.get('X-Account-Host')
        account_partition = req.headers.get('X-Account-Partition')
        account_device = req.headers.get('X-Account-Device')
        if all([account_host, account_partition, account_device]):
            account_ip, account_port = account_host.rsplit(':', 1)
            new_path = '/' + '/'.join([account, container])
            info = broker.get_info()
            account_headers = {
                'x-put-timestamp': info['put_timestamp'],
                'x-delete-timestamp': info['delete_timestamp'],
                'x-object-count': info['object_count'],
                'x-bytes-used': info['bytes_used'],
                'x-trans-id': req.headers.get('x-trans-id', '-')
            }
            if req.headers.get('x-account-override-deleted', 'no').lower() == \
                    'yes':
                account_headers['x-account-override-deleted'] = 'yes'
            try:
                with ConnectionTimeout(self.conn_timeout):
                    conn = http_connect(account_ip, account_port,
                                        account_device, account_partition,
                                        'PUT', new_path, account_headers)
                with Timeout(self.node_timeout):
                    account_response = conn.getresponse()
                    account_response.read()
                    if account_response.status == 404:
                        return HTTPNotFound(request=req)
                    elif account_response.status < 200 or \
                            account_response.status > 299:
                        self.logger.error(
                            _('ERROR Account update failed '
                              'with %(ip)s:%(port)s/%(device)s (will retry '
                              'later): Response %(status)s %(reason)s'), {
                                  'ip': account_ip,
                                  'port': account_port,
                                  'device': account_device,
                                  'status': account_response.status,
                                  'reason': account_response.reason
                              })
            except (Exception, Timeout):
                self.logger.exception(
                    _('ERROR account update failed with '
                      '%(ip)s:%(port)s/%(device)s (will retry later)'), {
                          'ip': account_ip,
                          'port': account_port,
                          'device': account_device
                      })
        return None

    def DELETE(self, req):
        """Handle HTTP DELETE request."""
        try:
            drive, part, account, container, obj = split_path(
                unquote(req.path), 4, 5, True)
        except ValueError, err:
            return HTTPBadRequest(body=str(err),
                                  content_type='text/plain',
                                  request=req)
        if 'x-timestamp' not in req.headers or \
                    not check_float(req.headers['x-timestamp']):
            return HTTPBadRequest(body='Missing timestamp',
                                  request=req,
                                  content_type='text/plain')
        if self.mount_check and not check_mount(self.root, drive):
            return Response(status='507 %s is not mounted' % drive)
        broker = self._get_container_broker(drive, part, account, container)
        if account.startswith(self.auto_create_account_prefix) and obj and \
                not os.path.exists(broker.db_file):
            broker.initialize(
                normalize_timestamp(
                    req.headers.get('x-timestamp') or time.time()))
        if not os.path.exists(broker.db_file):
            return HTTPNotFound()
        if obj:  # delete object
            broker.delete_object(obj, req.headers.get('x-timestamp'))
            return HTTPNoContent(request=req)
        else:
            # delete container
            if not broker.empty():
                return HTTPConflict(request=req)
            existed = float(broker.get_info()['put_timestamp']) and \
                      not broker.is_deleted()
            broker.delete_db(req.headers['X-Timestamp'])
            if not broker.is_deleted():
                return HTTPConflict(request=req)
            resp = self.account_update(req, account, container, broker)
            if resp:
                return resp
            if existed:
                return HTTPNoContent(request=req)
            return HTTPNotFound()
Пример #18
0
def resource_head_view(obj, request, resource, smart=True):
    return HTTPNoContent()
Пример #19
0
def resource_delete_view(obj, request, resource, smart=True):
    if not smart:
        return resource.delete()
    resource.delete()
    return HTTPNoContent()