Esempio n. 1
0
    def wrapper(*args, **kwargs):
        headers = {'Content-Type': 'application/vnd.api+json'}
        try:
            return func(*args, **kwargs)
        except JsonApiException as e:
            return make_response(jsonify(jsonapi_errors([e.to_dict()])),
                                 e.status, headers)
        except Exception as e:
            if current_app.config['DEBUG'] is True or current_app.config.get(
                    'PROPAGATE_EXCEPTIONS') is True:
                raise

            if 'sentry' in current_app.extensions:
                current_app.extensions['sentry'].captureException()

            exc = JsonApiException(getattr(
                e, 'detail',
                current_app.config.get('GLOBAL_ERROR_MESSAGE') or str(e)),
                                   source=getattr(e, 'source', ''),
                                   title=getattr(e, 'title', None),
                                   status=getattr(e, 'status', None),
                                   code=getattr(e, 'code', None),
                                   id_=getattr(e, 'id', None),
                                   links=getattr(e, 'links', None),
                                   meta=getattr(e, 'meta', None))
            return make_response(jsonify(jsonapi_errors([exc.to_dict()])),
                                 exc.status, headers)
Esempio n. 2
0
def internal_server_error(error):
    if current_app.config['PROPOGATE_ERROR'] is True:
        exc = JsonApiException({'pointer': ''}, str(error))
    else:
        exc = JsonApiException({'pointer': ''}, 'Unknown error')
    return make_response(json.dumps(jsonapi_errors([exc.to_dict()])), exc.status,
                         {'Content-Type': 'application/vnd.api+json'})
Esempio n. 3
0
    def dispatch_request(self, *args, **kwargs):
        """Logic of how to handle a request"""
        method = getattr(self, request.method.lower(), None)
        if method is None and request.method == 'HEAD':
            method = getattr(self, 'get', None)
        assert method is not None, 'Unimplemented method {}'.format(request.method)

        headers = {'Content-Type': 'application/vnd.api+json'}

        try:
            response = method(*args, **kwargs)
        except JsonApiException as e:
            return make_response(jsonify(jsonapi_errors([e.to_dict()])),
                                 e.status,
                                 headers)
        except Exception as e:
            if current_app.config['DEBUG'] is True:
                raise e

            if 'sentry' in current_app.extensions:
                current_app.extensions['sentry'].captureException()

            exc = JsonApiException(getattr(e,
                                           'detail',
                                           current_app.config.get('GLOBAL_ERROR_MESSAGE') or str(e)),
                                   source=getattr(e, 'source', ''),
                                   title=getattr(e, 'title', None),
                                   status=getattr(e, 'status', None),
                                   code=getattr(e, 'code', None),
                                   id_=getattr(e, 'id', None),
                                   links=getattr(e, 'links', None),
                                   meta=getattr(e, 'meta', None))
            return make_response(jsonify(jsonapi_errors([exc.to_dict()])),
                                 exc.status,
                                 headers)

        if isinstance(response, Response):
            response.headers.add('Content-Type', 'application/vnd.api+json')
            return response

        if not isinstance(response, tuple):
            if isinstance(response, dict):
                response.update({'jsonapi': {'version': '1.0'}})
            return make_response(jsonify(response), 200, headers)

        try:
            data, status_code, headers = response
            headers.update({'Content-Type': 'application/vnd.api+json'})
        except ValueError:
            pass

        try:
            data, status_code = response
        except ValueError:
            pass

        if isinstance(data, dict):
            data.update({'jsonapi': {'version': '1.0'}})

        return make_response(jsonify(data), status_code, headers)
Esempio n. 4
0
def internal_server_error(error):
    if current_app.config['PROPOGATE_ERROR'] is True:
        exc = JsonApiException({'pointer': ''}, str(error))
    else:
        exc = JsonApiException({'pointer': ''}, 'Unknown error')
    return make_response(json.dumps(jsonapi_errors([exc.to_dict()])), exc.status,
                         {'Content-Type': 'application/vnd.api+json'})
Esempio n. 5
0
def get_token_header():
    auth = request.headers.get('authorization', None)
    if not auth:
        raise JsonApiException(title='authorization header missing',
                               detail='authorization header is expected',
                               status=401)

    parts = auth.split()

    if parts[0].lower() != 'bearer':
        raise JsonApiException(
            title='invalid header',
            detail='authorization header must start with Bearer',
            status=401)
    elif len(parts) == 1:
        raise JsonApiException(title='invalid header',
                               detail='token not found',
                               status=401)
    elif len(parts) > 2:
        raise JsonApiException(
            title='invalid header',
            detail='authorization header must be Bearer token',
            status=401)
    token = parts[1]
    return token
Esempio n. 6
0
    def delete_relationship(self, json_data, relationship_field,
                            related_id_field, view_kwargs):
        """Delete a relationship

        :param dict json_data: the request params
        :param str relationship_field: the model attribute used for relationship
        :param str related_id_field: the identifier field of the related model
        :param dict view_kwargs: kwargs from the resource view
        """
        self.before_delete_relationship(json_data, relationship_field,
                                        related_id_field, view_kwargs)

        obj = self.get_object(view_kwargs)

        if obj is None:
            url_field = getattr(self, 'url_field', 'id')
            filter_value = view_kwargs[url_field]
            raise ObjectNotFound('{}: {} not found'.format(
                self.model.__name__, filter_value),
                                 source={'parameter': url_field})

        if not hasattr(obj, relationship_field):
            raise RelationNotFound("{} has no attribute {}".format(
                obj.__class__.__name__, relationship_field))

        related_model = getattr(obj.__class__,
                                relationship_field).property.mapper.class_

        updated = False

        if isinstance(json_data['data'], list):
            obj_ids = {
                str(getattr(obj__, related_id_field))
                for obj__ in getattr(obj, relationship_field)
            }

            for obj_ in json_data['data']:
                if obj_['id'] in obj_ids:
                    getattr(obj, relationship_field).remove(
                        self.get_related_object(related_model,
                                                related_id_field, obj_))
                    updated = True
        else:
            setattr(obj, relationship_field, None)
            updated = True

        try:
            self.session.commit()
        except JsonApiException as e:
            self.session.rollback()
            raise e
        except Exception as e:
            self.session.rollback()
            raise JsonApiException("Delete relationship error: " + str(e))

        self.after_delete_relationship(obj, updated, json_data,
                                       relationship_field, related_id_field,
                                       view_kwargs)

        return obj, updated
Esempio n. 7
0
    def update_object(self, obj, data, view_kwargs):
        """Update an object through sqlalchemy

        :param DeclarativeMeta obj: an object from sqlalchemy
        :param dict data: the data validated by marshmallow
        :param dict view_kwargs: kwargs from the resource view
        :return boolean: True if object have changed else False
        """
        if obj is None:
            url_field = getattr(self, 'url_field', 'id')
            filter_value = view_kwargs[url_field]
            raise ObjectNotFound('{}: {} not found'.format(
                self.model.__name__, filter_value),
                                 source={'parameter': url_field})

        self.before_update_object(obj, data, view_kwargs)

        relationship_fields = get_relationships(self.resource.schema,
                                                model_field=True)
        for key, value in data.items():
            if hasattr(obj, key) and key not in relationship_fields:
                setattr(obj, key, value)

        self.apply_relationships(data, obj)

        try:
            self.session.commit()
        except Exception as e:
            self.session.rollback()
            raise JsonApiException("Update object error: " + str(e),
                                   source={'pointer': '/data'})

        self.after_update_object(obj, data, view_kwargs)
Esempio n. 8
0
    def before_get_object(self, view_kwargs):
        """Before retrieving requested details of a user, check all is OK"""
        # pylint: disable=no-self-use

        # Find the user and check access permission if we got here through:
        # - GET /api/v1/users/<id>
        if view_kwargs.get('id') is not None:
            if (g.current_user.is_administrator()
                    or g.current_user.is_usermanager()
                    or g.current_user.id == view_kwargs['id']):
                pass
            else:
                # Unauthorized
                raise JsonApiException(' ',
                                       title=HTTP_STATUS_CODES[403],
                                       status='403')

        # - GET /api/v1/categories/<category_id>/user
        if view_kwargs.get('category_id') is not None:
            user = find_user_by_category_id(view_kwargs['category_id'])
            view_kwargs['id'] = user.id

        # - GET /api/v1/items/<item_id>/user
        if view_kwargs.get('item_id') is not None:
            user = find_user_by_item_id(view_kwargs['item_id'])
            view_kwargs['id'] = user.id
Esempio n. 9
0
    def delete_object(self, obj, view_kwargs):
        """Delete an object through sqlalchemy

        :param DeclarativeMeta item: an item from sqlalchemy
        :param dict view_kwargs: kwargs from the resource view
        """
        if obj is None:
            url_field = getattr(self, 'url_field', 'id')
            filter_value = view_kwargs[url_field]
            raise ObjectNotFound('{}: {} not found'.format(
                self.model.__name__, filter_value),
                                 source={'parameter': url_field})

        self.before_delete_object(obj, view_kwargs)

        self.session.delete(obj)
        try:
            self.session.commit()
        except JsonApiException as e:
            self.session.rollback()
            raise e
        except Exception as e:
            self.session.rollback()
            raise JsonApiException("Delete object error: " + str(e))

        self.after_delete_object(obj, view_kwargs)
Esempio n. 10
0
    def update_object(self, obj, data, **view_kwargs):
        """Update an object through sqlalchemy

        :param DeclarativeMeta obj: an object from sqlalchemy
        :param dict data: the data validated by marshmallow
        :param dict view_kwargs: kwargs from the resource view
        :return boolean: True if object have changed else False
        """
        self.before_update_object(obj, data, **view_kwargs)

        update = False

        relationship_fields = get_relationships(self.resource.schema)
        for field in data:
            if hasattr(obj, field) and field not in relationship_fields:
                if getattr(obj, field) != data[field]:
                    update = True
                setattr(obj, field, data[field])

        update_relationship = self.apply_relationships(data, obj)

        if update_relationship is True:
            update = True

        try:
            self.session.commit()
        except Exception as e:
            self.session.rollback()
            raise JsonApiException({'pointer': '/data'},
                                   "Update object error: " + str(e))

        return update
Esempio n. 11
0
    def create_object(self, data, view_kwargs):
        """Create an object through sqlalchemy

        :param dict data: the data validated by marshmallow
        :param dict view_kwargs: kwargs from the resource view
        :return DeclarativeMeta: an object from sqlalchemy
        """
        self.before_create_object(data, view_kwargs)

        relationship_fields = get_relationships(self.resource.schema,
                                                model_field=True)
        obj = self.model(
            **{
                key: value
                for (key, value) in data.items()
                if key not in relationship_fields
            })
        self.apply_relationships(data, obj)

        self.session.add(obj)
        try:
            self.session.commit()
        except Exception as e:
            self.session.rollback()
            raise JsonApiException("Object creation error: " + str(e),
                                   source={'pointer': '/data'})

        self.after_create_object(obj, data, view_kwargs)

        return obj
Esempio n. 12
0
    def update_object(self, obj, data, view_kwargs):
        """Update an object through sqlalchemy

        :param DeclarativeMeta obj: an object from sqlalchemy
        :param dict data: the data validated by marshmallow
        :param dict view_kwargs: kwargs from the resource view
        :return boolean: True if object have changed else False
        """
        self.before_update_object(obj, data, view_kwargs)

        relationship_fields = get_relationships(self.resource.schema)
        for key, value in data.items():
            if hasattr(obj, key) and key not in relationship_fields:
                setattr(obj, key, value)

        self.apply_relationships(data, obj)

        try:
            self.session.commit()
        except Exception as e:
            self.session.rollback()
            raise JsonApiException({'pointer': '/data'},
                                   "Update object error: " + str(e))

        self.after_update_object(obj, data, view_kwargs)
Esempio n. 13
0
    def update_relationship(self, json_data, relationship_field, related_id_field, view_kwargs):
        """Update a relationship

        :param dict json_data: the request params
        :param str relationship_field: the model attribute used for relationship
        :param str related_id_field: the identifier field of the related model
        :param dict view_kwargs: kwargs from the resource view
        :return boolean: True if relationship have changed else False
        """
        self.before_update_relationship(json_data, relationship_field, related_id_field, view_kwargs)

        obj = self.get_object(view_kwargs)

        if obj is None:
            url_field = getattr(self, 'url_field', 'id')
            filter_value = view_kwargs[url_field]
            raise ObjectNotFound('{}: {} not found'.format(self.model.__name__, filter_value),
                                 source={'parameter': url_field})

        if not hasattr(obj, relationship_field):
            raise RelationNotFound("{} has no attribute {}".format(obj.__class__.__name__, relationship_field))

        related_model = getattr(obj.__class__, relationship_field).property.mapper.class_

        updated = False

        if isinstance(json_data['data'], list):
            related_objects = []

            for obj_ in json_data['data']:
                related_objects.append(self.get_related_object(related_model, related_id_field, obj_))

            obj_ids = {getattr(obj__, related_id_field) for obj__ in getattr(obj, relationship_field)}
            new_obj_ids = {getattr(related_object, related_id_field) for related_object in related_objects}
            if obj_ids != new_obj_ids:
                setattr(obj, relationship_field, related_objects)
                updated = True

        else:
            related_object = None

            if json_data['data'] is not None:
                related_object = self.get_related_object(related_model, related_id_field, json_data['data'])

            obj_id = getattr(getattr(obj, relationship_field), related_id_field, None)
            new_obj_id = getattr(related_object, related_id_field, None)
            if obj_id != new_obj_id:
                setattr(obj, relationship_field, related_object)
                updated = True

        try:
            self.session.commit()
        except Exception as e:
            self.session.rollback()
            raise JsonApiException("Update relationship error: " + str(e))

        self.after_update_relationship(obj, updated, json_data, relationship_field, related_id_field, view_kwargs)

        return obj, updated
Esempio n. 14
0
def handle_expired_signature(token, err):
    """Handles tokens with expired signatures."""
    msg = 'Token provided by {} has expired'.format(
        jwt.get_unverified_claims(token).get('sub', 'sub not found'))
    current_app.logger.info(msg)
    raise JsonApiException(detail='{0}'.format(err),
                           title='token expired',
                           status=401)
Esempio n. 15
0
    def dispatch_request(self, *args, **kwargs):
        """Logic of how to handle a request"""
        method = getattr(self, request.method.lower(), None)
        if method is None and request.method == 'HEAD':
            method = getattr(self, 'get', None)
        assert method is not None, 'Unimplemented method {}'.format(
            request.method)

        headers = {'Content-Type': 'application/vnd.api+json'}

        try:
            response = method(*args, **kwargs)
        except JsonApiException as e:
            return make_response(jsonify(jsonapi_errors([e.to_dict()])),
                                 e.status, headers)
        except Exception as e:
            if current_app.config['DEBUG'] is True:
                raise e
            exc = JsonApiException('', str(e))
            return make_response(jsonify(jsonapi_errors([exc.to_dict()])),
                                 exc.status, headers)

        if isinstance(response, Response):
            response.headers.add('Content-Type', 'application/vnd.api+json')
            return response

        if not isinstance(response, tuple):
            if isinstance(response, dict):
                response.update({'jsonapi': {'version': '1.0'}})
            return make_response(jsonify(response), 200, headers)

        try:
            data, status_code, headers = response
            headers.update({'Content-Type': 'application/vnd.api+json'})
        except ValueError:
            pass

        try:
            data, status_code = response
        except ValueError:
            pass

        if isinstance(data, dict):
            data.update({'jsonapi': {'version': '1.0'}})

        return make_response(jsonify(data), status_code, headers)
Esempio n. 16
0
    def create_relationship(self, json_data, relationship_field,
                            related_id_field, **view_kwargs):
        """Create a relationship

        :param dict json_data: the request params
        :param str relationship_field: the model attribut used for relationship
        :param str related_id_field: the identifier field of the related model
        :param dict view_kwargs: kwargs from the resource view
        :return boolean: True if relationship have changed else False
        """
        obj = self.get_object(**view_kwargs)

        if not hasattr(obj, relationship_field):
            raise RelationNotFound(
                '', "{} has no attribut {}".format(obj.__class__.__name__,
                                                   relationship_field))

        related_model = getattr(obj.__class__,
                                relationship_field).property.mapper.class_

        updated = False

        if isinstance(json_data['data'], list):
            obj_ids = {
                str(getattr(obj__, related_id_field))
                for obj__ in getattr(obj, relationship_field)
            }

            for obj_ in json_data['data']:
                if obj_['id'] not in obj_ids:
                    getattr(obj, relationship_field).append(
                        self.get_related_object(related_model,
                                                related_id_field, obj_))
                    updated = True
        else:
            related_object = None

            if json_data['data'] is not None:
                related_object = self.get_related_object(
                    related_model, related_id_field, json_data['data'])

            obj_id = getattr(getattr(obj, relationship_field),
                             related_id_field, None)
            new_obj_id = getattr(related_object, related_id_field, None)
            if obj_id != new_obj_id:
                setattr(obj, relationship_field, related_object)
                updated = True

        try:
            self.session.commit()
        except Exception as e:
            self.session.rollback()
            raise JsonApiException('', "Create relationship error: " + str(e))

        return obj, updated
Esempio n. 17
0
    def delete_object(self, obj, **view_kwargs):
        """Delete an object through sqlalchemy

        :param DeclarativeMeta item: an item from sqlalchemy
        :param dict view_kwargs: kwargs from the resource view
        """
        self.before_delete_object(obj, **view_kwargs)

        self.session.delete(obj)
        try:
            self.session.commit()
        except Exception as e:
            self.session.rollback()
            raise JsonApiException('', "Delete object error: " + str(e))
Esempio n. 18
0
def get_jwks():
    rv = cache.get('jwks')
    if rv is None:
        try:
            jwksurl = requests.get(current_app.config['JWKS_URL'], timeout=5)
        except requests.exceptions.Timeout:
            raise JsonApiException(
                title='JWKS Request Timed Out',
                detail=
                'the authentication server is unavailable, or another issue has occured',
                status=500)
        rv = jwksurl.json()
        cache.set('jwks', rv, expire=48 * 3600)
    return rv
Esempio n. 19
0
    def before_create_object(self, data, unused_view_kwargs):
        """Prior to creating a new user, check all is OK"""
        # POST /users
        if ('email' not in data or 'password' not in data
                or 'first_name' not in data or 'last_name' not in data):
            raise BadRequest('Must include email, password, first_name and'
                             'last_name fields')

        user = self.session.query(User).filter_by(email=data['email']).first()
        if user:
            if user.blocked:
                raise JsonApiException(detail='Account has been blocked.'
                                       'Contact the site administrator.',
                                       title='Permission denied',
                                       status='403')
            else:
                raise BadRequest('Email {} already registered'.format(
                    data['email']))
Esempio n. 20
0
 def after_create_object(self, geokret, data, view_kwargs):
     # Create first move if requested
     if self.__create_first_move:
         # But only if user has home coordinates
         if geokret.owner.latitude and geokret.owner.longitude:
             move = Move(
                 author=geokret.owner,
                 geokret=geokret,
                 type=MOVE_TYPE_DIPPED,
                 moved_on_datetime=datetime.utcnow(),
                 latitude=geokret.owner.latitude,
                 longitude=geokret.owner.longitude,
                 comment="Born here",
             )
             geokret.last_move = move
             db.session.add(move)
             db.session.add(geokret)
             try:
                 db.session.commit()
             except Exception as e:  # pragma: no cover
                 db.session.rollback()
                 raise JsonApiException("Failed to create first move: " + str(e), source={'pointer': '/data'})
     return geokret
Esempio n. 21
0
def has_permission(resource_id, **kw):
    '''
    Pre-processor for DELETE_RESOURCE
    Checks if:
        the resource being deleted is 'owned' by the requester
        the requester has admin privs/scope
    '''

    token = get_token_header()
    unverified_claims = jwt.get_unverified_claims(token)

    token_scopes = unverified_claims['scope'].split()
    if 'delete:other-comments' in token_scopes:
        return

    # Check if owner of resource
    user_id = unverified_claims['sub'].split('|')
    user = models.User.query.filter_by(user_id=user_id).first()
    is_owner = models.Comment.query.get(resource_id).owner_is(user)

    if not is_owner:
        raise JsonApiException(title='invalid header',
                               detail='not the owner of this resource',
                               status=401)
Esempio n. 22
0
 def wrapped_f(*args, **kwargs):
     raise JsonApiException('', "Acces to this method have been disallowed", title="MethodNotAllowed", status=405)
Esempio n. 23
0
def handle_claims(err):
    """Handles tokens with invalid claims."""
    raise JsonApiException(detail='{0}'.format(err),
                           title='invalid claim',
                           status=401)
Esempio n. 24
0
def handle_jwt(err):
    """Handles tokens with other jwt-related issues."""
    raise JsonApiException(detail='{0}'.format(err),
                           title='invalid signature',
                           status=401)
Esempio n. 25
0
def handle_non_jwt():
    """Handles everything else."""
    raise JsonApiException(title='invalid header',
                           detail='unable to parse authentication token')