Exemple #1
0
    def on_post(self, req, resp, rid):
        """ Deserialize the file upload & save it to S3

        File uploads are associated with a model of some
        kind. Ensure the associating model exists first &
        foremost.
        """

        signals.pre_req.send(self.model)
        signals.pre_req_upload.send(self.model)

        props = req.deserialize(self.mimetypes)
        model = find(self.model, rid)

        signals.pre_upload.send(self.model, model=model)

        try:
            conn = s3_connect(self.key, self.secret)
            path = self._gen_s3_path(model, props)
            s3_url = s3_upload(self.acl, self.bucket, conn, props['content'],
                               props['content-type'], path)
        except IOError:
            abort(ServiceUnavailable(**{
                'detail': 'The upload attempt failed unexpectedly',
            }))
        else:
            signals.post_upload.send(self.model, model=model, url=s3_url)

            resp.location = s3_url
            resp.status = falcon.HTTP_201

            resp.serialize({'data': {'url': s3_url}})

            signals.post_req.send(self.model)
            signals.post_req_upload.send(self.model)
Exemple #2
0
    def on_get(self, req, resp, rid, related):
        """ Find the related model & serialize it back

        If the parent resource of the related model doesn't
        exist then abort on a 404.
        """

        signals.pre_req.send(self.model)
        signals.pre_req_find.send(self.model)

        if not hasattr(self.model, related):
            abort(InvalidURL(**{
                'detail': 'The "%s" resource does not have a related '
                          'resource named "%s". This is an error, check '
                          'your spelling & retry.' % (self.rtype, related)
            }))

        model = find(self.model, rid)
        try:
            model_related = getattr(model, related).load()
        except AttributeError:
            model_related = None

        if isinstance(model_related, list):
            props = to_rest_models(model_related, includes=req.includes)
        elif model:
            props = to_rest_model(model_related, includes=req.includes)
        else:
            props = model_related

        resp.serialize(props)

        signals.post_req.send(self.model)
        signals.post_req_find.send(self.model)
Exemple #3
0
    def process_request(self, req, resp):
        """ Process the request before routing it.

        We always enforce the use of SSL.
        """

        if goldman.config.TLS_REQUIRED and req.protocol != 'https':
            abort(TLSRequired)
Exemple #4
0
    def process_request(self, req, resp):  # pylint: disable=unused-argument
        """ Process the request before routing it. """

        try:
            creds = self._get_creds(req)
            self.auth_creds(*creds)
        except (AuthRejected, AuthRequired, InvalidAuthSyntax) as exc:
            exc.headers = self._error_headers
            if not self.optional:
                abort(exc)
Exemple #5
0
    def process_request(self, req, resp):
        """ Process the request before routing it. """

        key = req.env['REMOTE_PORT'] + req.env['REMOTE_ADDR']
        val = self.cache.get(key, 0)

        if val == self.count:
            abort(exceptions.TooManyRequests(headers=self._error_headers))
        else:
            self.cache[key] = val + 1
Exemple #6
0
def find(model, rid):
    """ Find a model from the store by resource id """

    validate_rid(model, rid)

    rid_field = model.rid_field
    model = goldman.sess.store.find(model.RTYPE, rid_field, rid)

    if not model:
        abort(exceptions.DocumentNotFound)

    return model
Exemple #7
0
    def fail(detail, link):
        """ Convenience method aborting on non-compliant request bodies

        Call this method with a string description of the reason
        for the error & a URL. The URL is typically a section in
        an online specification citing the proper way to construct
        the request body
        """

        abort(exceptions.InvalidRequestBody(**{
            'detail': detail,
            'links': link
        }))
Exemple #8
0
def validate_rid(model, rid):
    """ Ensure the resource id is proper """

    rid_field = getattr(model, model.rid_field)

    if isinstance(rid_field, IntType):
        try:
            int(rid)
        except (TypeError, ValueError):
            abort(exceptions.InvalidURL(**{
                'detail': 'The resource id {} in your request is not '
                          'syntactically correct. Only numeric type '
                          'resource id\'s are allowed'.format(rid)
            }))
Exemple #9
0
    def merge(self, data, clean=False, validate=False):
        """ Merge a dict with the model

        This is needed because schematics doesn't auto cast
        values when assigned. This method allows us to ensure
        incoming data & existing data on a model are always
        coerced properly.

        We create a temporary model instance with just the new
        data so all the features of schematics deserialization
        are still available.

        :param data:
            dict of potentially new different data to merge

        :param clean:
            set the dirty bit back to clean. This is useful
            when the merge is coming from the store where
            the data could have been mutated & the new merged
            in data is now the single source of truth.

        :param validate:
            run the schematics validate method

        :return:
            nothing.. it has mutation side effects
        """

        try:
            model = self.__class__(data)
        except ConversionError as errors:
            abort(self.to_exceptions(errors.messages))

        for key, val in model.to_native().items():
            if key in data:
                setattr(self, key, val)

        if validate:
            try:
                self.validate()
            except ModelValidationError as errors:
                abort(self.to_exceptions(errors.messages))

        if clean:
            self._original = self.to_native()
Exemple #10
0
def handle_exc(exc):
    """ Given a database exception determine how to fail

    Attempt to lookup a known error & abort on a meaningful
    error. Otherwise issue a generic DatabaseUnavailable exception.

    :param exc: psycopg2 exception
    """

    err = ERRORS_TABLE.get(exc.pgcode)

    if err:
        abort(exceptions.InvalidQueryParams(**{
            'detail': err,
            'parameter': 'filter',
        }))

    abort(exceptions.DatabaseUnavailable)
Exemple #11
0
    def process_resource(self, req, resp, resource):
        """ Process the request after routing.

        Deserializer selection needs a resource to determine which
        deserializers are allowed. If a deserializer is required then
        it will be initialized & added to the request object for
        further processing.
        """

        if req.content_required and resource:
            allowed = resource.deserializer_mimetypes

            if req.content_length in (None, 0):
                abort(EmptyRequestBody)
            elif req.content_type not in allowed:
                abort(ContentTypeUnsupported(allowed))
            else:
                deserializer = self._get_deserializer(req.content_type)
                req.deserializer = deserializer(req, resp)
Exemple #12
0
    def _parse_top_level_content_type(self):
        """ Ensure a boundary is present in the Content-Type header

        This is the Content-Type header outside of any form-data
        & should simply be:

            Content-Type: multipart/form-data; boundary=<value>\r\n

        This is generated by the client obviously & should not
        occur within the uploaded payload.
        """

        if not self.req.content_type_params.get('boundary'):
            abort(exceptions.InvalidRequestHeader(**{
                'detail': 'A boundary param is required in the Content-Type '
                          'header & cannot be an empty string. The details '
                          'of its grammar are further outlined in RFC 2046 '
                          '- section 5.1.1.',
                'links': 'tools.ietf.org/html/rfc2388#section-4.1',
            }))
Exemple #13
0
    def deserialize(self, data=None):
        """ Invoke the deserializer

        If the payload is a collection (more than 1 records)
        then a list will be returned of normalized dict's.

        If the payload is a single item then the normalized
        dict will be returned (not a list)

        :return: list or dict
        """

        data = []

        if self.req.content_type_params.get('header') != 'present':
            abort(exceptions.InvalidRequestHeader(**{
                'detail': 'When using text/csv your Content-Type '
                          'header MUST have a header=present parameter '
                          '& the payload MUST include a header of fields',
                'links': 'tools.ietf.org/html/rfc4180#section-3'
            }))

        try:
            reader = csv.DictReader(self.req.stream)

            self._validate_field_headers(reader)

            for row in reader:
                Parser.run(row, reader)

                row = Normalizer.run(row, reader)
                row = super(Deserializer, self).deserialize(data)

                data.append(row)
        except csv.Error:
            abort(exceptions.InvalidRequestBody)

        return data