Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resource checking introduced in 0.11.1 doesn't honor multi table inheritance. #1184

Closed
oliverandrich opened this issue Jul 1, 2014 · 3 comments

Comments

@oliverandrich
Copy link

I have a problem with the new resource checking introduced in 0.11.1.

Tastypie previously would accept a relation URI & solely parse out the identifiers, ignoring if the URI was for the right resource. Where 'user': '/api/v1/users/1/', would be accepted as a User URI, you could accidentally/intentionally pass something like 'user': '/api/v1/notes/1/', (notes rather than users), which would assign it to the User with a pk=1. Tastypie would resolve the URI, but proceed to only care about the kwargs, not validating it was for the correct resource.

Tastypie now checks to ensure the resolving resource has a matching URI, so these cases of mistaken identity can no longer happen (& with quicker lookups). Thanks to Sergey Orshanskiy for the report!

In my application I use inheritance for models.

class BaseExporter(models.Model):
    ...
class OnlineExporter(BaseExporter):
    ...
class PrintExporter(BaseExporter):
    ...

And use a reference to BaseExporter in a ForeignKey in some other model.

class ExportLogEntry(models.Model):
    ...
    exporter = ForeignKey('BaseExporter', null=True)
    ...

Assigning an instance of OnlineExporter or PrintExporter works just fine for djangos own ORM. But the new checking inside tastypie prohibits the creation of new ExportLogEntry instances based on a JSON document like this.

{
  ...
  "exporter" : "\/api\/v1\/onlineexporter\/1\/",
  ...
}

It only works, if a resource uri for a baseexporter is used. I think this is to much security checking and doesn't honor the inheritance feature of django.

@Sunno
Copy link

Sunno commented Aug 12, 2014

I'm having same error, I had to get back to 0.11.0 version because of that.

@Sunno
Copy link

Sunno commented Sep 18, 2014

Well, I had to update to 0.12 because I had some problems with httplib2, so I had to solve this problem, I just overrided get_via_uri method and created one based in original source in order to validate it works with subclases

def get_via_uri(self, uri, request=None):
    """
    This pulls apart the salient bits of the URI and populates the
    resource via a ``obj_get``.

    Optionally accepts a ``request``.

    If you need custom behavior based on other portions of the URI,
    simply override this method.
    """
    prefix = get_script_prefix()
    chomped_uri = uri

    if prefix and chomped_uri.startswith(prefix):
        chomped_uri = chomped_uri[len(prefix)-1:]

    # We mangle the path a bit further & run URL resolution against *only*
    # the current class. This ought to prevent bad URLs from resolving to
    # incorrect data.
    urls = []
    found_at = chomped_uri.rfind(self._meta.resource_name)
    if found_at == -1:
        for subclass in self.__class__.__subclasses__():
            found_at = chomped_uri.rfind(subclass._meta.resource_name)
            if found_at != -1:
                child_resource = subclass()
                urls += child_resource.urls  # adding child resource URLs
                urls += self.urls
                break
        if found_at == -1:  # checking if still can't be found
            raise NotFound("An incorrect URL was provided '%s' for the '%s' resource." % (uri, self.__class__.__name__))
    else:
        urls = self.urls

    chomped_uri = chomped_uri[found_at:]
    try:
        for url_resolver in urls:
            result = url_resolver.resolve(chomped_uri)

            if result is not None:
                view, args, kwargs = result
                break
        else:
            raise Resolver404("URI not found in 'self.urls'.")
    except Resolver404:
        raise NotFound("The URL provided '%s' was not a link to a valid resource." % uri)

    bundle = self.build_bundle(request=request)
    return self.obj_get(bundle=bundle, **self.remove_api_resource_names(kwargs))

@SeanHayes
Copy link
Member

The resource name checking is staying in, but I'd be fine with allowing subtypes to match if someone could write a PR with a unit test.

@oliverandrich oliverandrich closed this as not planned Won't fix, can't repro, duplicate, stale Oct 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants