Exemple #1
0
    def _int_del_key(self, trail, request, response):
        try:
            name = '/'.join(trail)
            msg = self._parse_maybe_body(request, name)
        except Exception as e:
            raise HTTPError(406, str(e))
        key = self._db_key(trail)
        try:
            ret = self.root.store.cut(key)
        except CSStoreDenied:
            self.logger.exception(
                "Delete: Permission to perform this operation was denied")
            raise HTTPError(403)
        except CSStoreError:
            self.logger.exception('Delete: Internal Server Error')
            raise HTTPError(500)
        except CSStoreUnsupported:
            self.logger.exception('Delete: Unsupported operation')
            raise HTTPError(501)

        if ret is False:
            raise HTTPError(404)

        output = msg.reply(None)
        if output is None:
            response['code'] = 204
        else:
            response['headers'][
                'Content-Type'] = 'application/json; charset=utf-8'
            response['output'] = output
            response['code'] = 200
Exemple #2
0
 def _list(self, trail, request, response):
     try:
         name = '/'.join(trail)
         msg = self._parse_query(request, name)
     except Exception as e:
         raise HTTPError(406, str(e))
     default = request.get('default_namespace', None)
     basename = self._db_container_key(default, trail)
     try:
         keylist = self.root.store.list(basename)
         self.logger.debug('list %s returned %r', basename, keylist)
         if keylist is None:
             raise HTTPError(404)
         response['headers'][
             'Content-Type'] = 'application/json; charset=utf-8'
         response['output'] = msg.reply(keylist)
     except CSStoreDenied:
         self.logger.exception(
             "List: Permission to perform this operation was denied")
         raise HTTPError(403)
     except CSStoreError:
         self.logger.exception('List: Internal server error')
         raise HTTPError(500)
     except CSStoreUnsupported:
         self.logger.exception('List: Unsupported operation')
         raise HTTPError(501)
Exemple #3
0
 def _parse_bin_body(self, request, name):
     body = request.get('body')
     if body is None:
         raise HTTPError(400)
     value = b64encode(bytes(body)).decode('utf-8')
     payload = {'type': 'simple', 'value': value}
     return self._parse(request, payload, name)
Exemple #4
0
 def _db_container_key(self, default, trail):
     f = None
     if len(trail) > 1:
         f = self._db_key(trail)
     elif len(trail) == 1 and trail[0] != '':
         self.logger.debug(
             "Forbidden action: Wrong container path. Container names must "
             "end with '/'")
         raise HTTPError(403)
     elif default is None:
         self.logger.debug("Forbidden action: No default namespace")
         raise HTTPError(403)
     else:
         # Use the default namespace
         f = self._db_key([default, ''])
     return f
Exemple #5
0
 def _db_key(self, trail):
     if len(trail) < 2:
         self.logger.debug(
             "Forbidden action: Operation only permitted within a "
             "container")
         raise HTTPError(403)
     return os.path.join('keys', *trail)
Exemple #6
0
 def parse_body(self):
     length = int(self.headers.get('content-length', 0))
     if length > MAX_REQUEST_SIZE:
         raise HTTPError(413)
     if length == 0:
         self.body = None
     else:
         self.body = self.rfile.read(length)
Exemple #7
0
 def DELETE(self, request, response):
     trail = request.get('trail', [])
     if len(trail) == 0:
         raise HTTPError(405)
     if trail[-1] == '':
         self._destroy(trail, request, response)
     else:
         self._del_key(trail, request, response)
Exemple #8
0
    def _int_set_key(self, trail, request, response):
        try:
            name = '/'.join(trail)

            content_type = request.get('headers', {}).get('Content-Type', '')
            content_type_value = content_type.split(';')[0].strip()
            if content_type_value == 'application/octet-stream':
                msg = self._parse_bin_body(request, name)
            elif content_type_value == 'application/json':
                msg = self._parse_body(request, name)
            else:
                raise ValueError('Invalid Content-Type')
        except UnknownMessageType as e:
            raise HTTPError(406, str(e))
        except UnallowedMessage as e:
            raise HTTPError(406, str(e))
        except Exception as e:
            raise HTTPError(400, str(e))

        # must _db_key first as access control is done here for now
        # otherwise users would e able to probe containers in namespaces
        # they do not have access to.
        key = self._db_key(trail)

        try:
            default = request.get('default_namespace', None)
            ok = self._parent_exists(default, trail)
            if not ok:
                raise HTTPError(404)

            ok = self.root.store.set(key, msg.payload)
        except CSStoreDenied:
            self.logger.exception(
                "Set: Permission to perform this operation was denied")
            raise HTTPError(403)
        except CSStoreExists:
            self.logger.exception('Set: Key already exist')
            raise HTTPError(409)
        except CSStoreError:
            self.logger.exception('Set: Internal Server Error')
            raise HTTPError(500)
        except CSStoreUnsupported:
            self.logger.exception('Set: Unsupported operation')
            raise HTTPError(501)

        output = msg.reply(None)
        if output is not None:
            response['headers'][
                'Content-Type'] = 'application/json; charset=utf-8'
            response['output'] = output
        response['code'] = 201
Exemple #9
0
    def _create(self, trail, request, response):
        try:
            name = '/'.join(trail)
            msg = self._parse_maybe_body(request, name)
        except Exception as e:
            raise HTTPError(406, str(e))
        default = request.get('default_namespace', None)
        basename = self._db_container_key(None, trail)
        try:
            if len(trail) > 2:
                ok = self._parent_exists(default, trail[:-1])
                if not ok:
                    raise HTTPError(404)

            self.root.store.span(basename)
        except CSStoreDenied:
            self.logger.exception(
                "Create: Permission to perform this operation was denied")
            raise HTTPError(403)
        except CSStoreExists:
            self.logger.debug('Create: Key already exists')
            response['code'] = 200
            return
        except CSStoreError:
            self.logger.exception('Create: Internal server error')
            raise HTTPError(500)
        except CSStoreUnsupported:
            self.logger.exception('Create: Unsupported operation')
            raise HTTPError(501)

        output = msg.reply(None)
        if output is not None:
            response['headers'][
                'Content-Type'] = 'application/json; charset=utf-8'
            response['output'] = output
        response['code'] = 201
Exemple #10
0
    def _parent_exists(self, default, trail):
        # check that the containers exist
        basename = self._db_container_key(trail[0], trail[:-1] + [''])
        try:
            keylist = self.root.store.list(basename)
        except CSStoreError:
            raise HTTPError(500)

        self.logger.debug('parent_exists: %s (%s, %r) -> %r',
                          basename, default, trail, keylist)

        if keylist is not None:
            return True

        # create default namespace if it is the only missing piece
        if len(trail) == 2 and default == trail[0]:
            container = self._db_container_key(default, '')
            self.root.store.span(container)
            return True

        return False
Exemple #11
0
 def _int_get_key(self, trail, request, response):
     try:
         name = '/'.join(trail)
         handler = self._parse_query(request, name)
     except Exception as e:
         raise HTTPError(406, str(e))
     key = self._db_key(trail)
     try:
         output = self.root.store.get(key)
         if output is None:
             raise HTTPError(404)
         elif len(output) == 0:
             raise HTTPError(406)
         self._format_reply(request, response, handler, output)
     except CSStoreDenied:
         self.logger.exception(
             "Get: Permission to perform this operation was denied")
         raise HTTPError(403)
     except CSStoreError:
         self.logger.exception('Get: Internal server error')
         raise HTTPError(500)
     except CSStoreUnsupported:
         self.logger.exception('Get: Unsupported operation')
         raise HTTPError(501)
Exemple #12
0
    def pipeline(self, config, request):
        """
        The pipeline() function handles authentication and invocation of
        the correct consumer based on the server configuration, that is
        provided at initialization time.

        When authentication is performed all the authenticators are
        executed. If any returns False, authentication fails and a 403
        error is raised. If none of them positively succeeds and they all
        return None then also authentication fails and a 403 error is
        raised. Authentication plugins can add attributes to the request
        object for use of authorization or other plugins.

        When authorization is performed and positive result will cause the
        operation to be accepted and any negative result will cause it to
        fail. If no authorization plugin returns a positive result a 403
        error is returned.

        Once authentication and authorization are successful the pipeline
        will parse the path component and find the consumer plugin that
        handles the provided path walking up the path component by
        component until a consumer is found.

        Paths are walked up from the leaf to the root, so if two consumers
        hang on the same tree, the one closer to the leaf will be used. If
        there is a trailing path when the conumer is selected then it will
        be stored in the request dicstionary named 'trail'. The 'trail' is
        an ordered list of the path components below the consumer entry
        point.
        """
        path_chain = request['path_chain']
        if not path_chain or path_chain[0] != '':
            # no path or not an absolute path
            raise HTTPError(400)

        # auth framework here
        authers = config.get('authenticators')
        if authers is None:
            raise HTTPError(403)
        valid_once = False
        for auth in authers:
            valid = authers[auth].handle(request)
            if valid is False:
                raise HTTPError(403)
            elif valid is True:
                valid_once = True
        if valid_once is not True:
            self.server.auditlog.svc_access(self.__class__.__name__,
                                            log.AUDIT_SVC_AUTH_FAIL,
                                            request['client_id'], 'No auth')
            raise HTTPError(403)

        # auhz framework here
        authzers = config.get('authorizers')
        if authzers is None:
            raise HTTPError(403)
        authz_ok = None
        for authz in authzers:
            valid = authzers[authz].handle(request)
            if valid is True:
                authz_ok = True
            elif valid is False:
                authz_ok = False
                break
        if authz_ok is not True:
            self.server.auditlog.svc_access(self.__class__.__name__,
                                            log.AUDIT_SVC_AUTHZ_FAIL,
                                            request['client_id'], path_chain)
            raise HTTPError(403)

        # Select consumer
        trail = []
        while path_chain:
            if path_chain in config['consumers']:
                con = config['consumers'][path_chain]
                if len(trail) != 0:
                    request['trail'] = trail
                return con.handle(request)
            trail.insert(0, path_chain[-1])
            path_chain = path_chain[:-1]

        raise HTTPError(404)
Exemple #13
0
    def handle_one_request(self):
        if self.request.family == socket.AF_UNIX:
            # Set a fake client address to make log functions happy
            self.client_address = ['127.0.0.1', 0]
        try:
            if not self.server.config:
                self.close_connection = 1
                return
            self.raw_requestline = self.rfile.readline(65537)
            if not self.raw_requestline:
                self.close_connection = 1
                return
            if len(self.raw_requestline) > 65536:
                self.requestline = ''
                self.request_version = ''
                self.command = ''
                self.send_error(414)
                self.wfile.flush()
                return
            if not self.parse_request():
                self.close_connection = 1
                return
            try:
                self.parse_body()
            except HTTPError as e:
                self.send_error(e.code, e.mesg)
                self.wfile.flush()
                return
            request = {
                'creds': self.peer_creds,
                'client_cert': self.peer_cert,
                'client_id': self.peer_info,
                'command': self.command,
                'path': self.path,
                'path_chain': self.path_chain,
                'query': self.query,
                'url': self.url,
                'version': self.request_version,
                'headers': self.headers,
                'body': self.body
            }
            logger.debug(
                "REQUEST: %s %s, query: %r, cred: %r, client_id: %s, "
                "headers: %r, body: %r", request['command'],
                request['path_chain'],
                request['query'], request['creds'], request['client_id'],
                dict(request['headers']), request['body'])
            try:
                response = self.pipeline(self.server.config, request)
                if response is None:
                    raise HTTPError(500)
            except HTTPError as e:
                self.send_error(e.code, e.mesg)
                self.wfile.flush()
                return
            except socket.timeout as e:
                self.log_error("Request timed out: %r", e)
                self.close_connection = 1
                return
            except Exception as e:  # pylint: disable=broad-except
                self.log_error("Handler failed: %r", e, exc_info=True)
                self.send_error(500)
                self.wfile.flush()
                return

            self.send_response(response.get('code', 200))
            for header, value in six.iteritems(response.get('headers', {})):
                self.send_header(header, value)
            self.end_headers()

            output = response.get('output', None)
            if hasattr(output, 'read'):
                shutil.copyfileobj(output, self.wfile)
                output.close()
            elif output is not None:
                self.wfile.write(output)
            else:
                self.close_connection = 1
            self.wfile.flush()
            return
        except socket.timeout as e:
            self.log_error("Request timed out: %r", e)
            self.close_connection = 1
            return
Exemple #14
0
 def _parse_body(self, request, name):
     body = request.get('body')
     if body is None:
         raise HTTPError(400)
     value = json.loads(bytes(body).decode('utf-8'))
     return self._parse(request, value, name)
Exemple #15
0
 def POST(self, request, response):
     trail = request.get('trail', [])
     if len(trail) > 0 and trail[-1] == '':
         self._create(trail, request, response)
     else:
         raise HTTPError(405)
Exemple #16
0
 def PUT(self, request, response):
     trail = request.get('trail', [])
     if len(trail) == 0 or trail[-1] == '':
         raise HTTPError(405)
     else:
         self._set_key(trail, request, response)