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
Need a much safer default: DjangoAuthorization
should not allow read
access to all model object
#1407
Comments
DjangoAuthorization
allows read
access to model objectDjangoAuthorization
should not allow read
access to all model object
…ntation. Included fix for Issue django-tastypie#1407, better default for DjangoAuthorization.
…ntation. Included fix for Issue django-tastypie#1407, better default for DjangoAuthorization.
…ntation. Included fix for Issue django-tastypie#1407, better default for DjangoAuthorization.
…ntation. Included fix for Issue django-tastypie#1407, better default for DjangoAuthorization.
I appreciate this is merged already but I still take issue with it. Before opening a new request, here's my concern: This change unfortunately breaks existing code and thus is backwards incompatible (previously all GET requests would pass DjangoAuthorization). To fix one's code, the only two options are to either give all users 'change' permission (bad), or to subclass DjangoAuthorization for a custom implementation of the read actions (potentially lots of work). Was this intended? I appreciate the reasoning behind this in the django admin context, but I doubt setting 'read' as the equivalent to 'change' in an API context is a sensible default assumption as it is not the expected behavior. After all, if GET is an allowed method along with DjangoAuthorization, why would it refuse access based on the fact that the user does not have the I propose he following alternative implementation: for read_detail
for read_list
This way tastypie adds a default that's compatible with Django's permission defaults while giving an easy path to improve by adding view and list permissions for those who need it, short of writing a custom implementation of DjangoAuthorization. At least there should be a way to specify the default permission:
|
@SeanHayes will be the one who decide. Just my opinion here, I think it was a security issue to allow read by default. I totally didn't expect it, until I was very close to production. I think breaking backward-compat is necessary in this context. Look like your suggestion require changing params pass to Consider, class ModifiedDjangoAuthorization(DjangoAuthorization):
READ_PERM_CODE = 'view' If you like to change other, just override the methods. The change actually designed to make it very easy to do it. class ModifiedDjangoAuthorization(DjangoAuthorization):
def delete_list(self, object_list, bundle):
return self.perm_list_checks(bundle.request, 'del', object_list)
def delete_detail(self, object_list, bundle):
return self.perm_obj_checks(bundle.request, 'del', bundle.obj) The change did take into modification in mind and make it easy. I don't necessary think it should be done as init params. |
I'm not questioning the intent of the original issue. Just pointing out that the implementation as merged is breaking people's existing code & assumptions without saying so and with no efficient option to revert.
If you look at my proposed solution again, what I'm advocating is to give people options with a safe and backwards compatible default. No changes would be necessary in user's code in this case.
I disagree. The change as currently merged not only breaks backwards-compatibility but also introduces a much larger potential security issue in that the obvious way (implied by the current implementation) is to assign the change permission to all users that should be allowed GET. Frankly, I fail to see how the Unfortunately, your proposed Of course overriding is always an option to achieve one's specific requirements, however I think the general idea in tastypie is to provide sensible & safe defaults that don't require adding custom code... In short I think this change should be reverted for a better implementation. |
The Not sure what you mean by checking the To my preference, what you proposed seem to be on the "too much magic" side for a default authorization. Just having a safe default, easily overridable, seem to be enough. But, that's just my opinion. |
The change was documented here: https://github.com/django-tastypie/django-tastypie/blob/master/docs/release_notes/v0.13.2.rst
We know that if a user can change something, then they can read it, that's how the Django admin does things. This version is more secure, because it forces the developer to think about what they're doing. If instead of inventing their own "read" permission, the developer chooses to deliberately give everyone "change" permissions when they should only have "read" permissions, that's their problem; I can't stop other developers from deliberately doing stupid things, I'm here to stop Tastypie from doing stupid things. The point of this change was to prevent global read permissions on any resource that used DjangoAuthorization, which developers might not expect; the new behavior is in line with what developers experience in the Django admin. If you want the old behavior:
If you think the documentation could be improved, feel free to submit a PR. |
appreciate your feedback. thanks for the link to the doc, fair enough, my bad for missing that (please note the issue is assigned to v0.13.4 whereas the docs are in v0.13.2). Let me make some final remarks from my pov:
Django admin uses the
One of tastypie's advertised features is to provide reasonable defaults. Wouldn't it be quite reasonable to assume that an API's GET (by definition: read) and PUT (change) methods, being different operations in all intent and purpose, also require different permissions? I'll be happy to contribute a PR along the lines of what I wrote if you guys think it's a valuable addition. |
there is a pending PR in Django to add a |
We're not going to allow public/global read operations by default. That's final. And we're not going to try and guess how developers have their permissions set up when there's not currently any standard way to do so. We're not even sure whether to call it "read" or "view", and I don't want a bunch of people coming in here saying "I do it this way" or "the new Django release calls it something else". If and when Django supports a read/view permission out of the box we'll switch to that. For now developers are just going to have to write some custom code to handle the custom way they handle permissions. |
I know this issue is closed but I just want to chime in and say that this change has basically blocked me from migrating an existing project to 0.13.x. @miraculixx I think you're entirely correct with regards to the fact that requiring change permissions simply to view data is a little crazy. I guess we can blame Django for somehow, still, not having the concept of a view permission (Which blows my mine on a whole other level, I think it's insane that a project including a CRUD admin component simply doesn't have a permission for the READ part of CRUD). |
When you make production-breaking changes, can you at least reflect that in versioning?
I just spent a very uncomfortable hour tracing this during a new production deployment. |
Merged commits from the original repo for v0.13.3 tag. Note: I am going to squash it because I want to keep fork history simpler and to be able to track our own changes. And because I hope that eventually we will get rid of this fork (or even tastypie itself) at all. If you need an actual history - well, sorry. Original commit messages: * Throttled requests now include Retry-After header. * Added failing test case with extra data on a related field. Extra data directly on a parent recource is normally ignored. * Extra data on related resources is now ignored. * A more informative error message in Resource.put_list, and catch an error earlier in RelatedField.resource_from_uri. * Made error in resource_from_uri more specific. * Removed data shared on Field objects, changed code to reuse related resources. * Use list comprehensions where reasonable, which is slightly faster. * Added profilingtests. * Get user model and username field in a more lazy way User model will not be ready at import time Signed-off-by: Ilya Baryshev <baryshev@gmail.com> * Django 1.7: migrations We're keeping old migrations in south_migrations folder to be backwards-compatible with Django<1.7 and South>=1.0 * Django 1.7: Explicit fixtures in tests Using of initial_data in tests is problematic due to new app loading mechanism. Because initial_data fixtures is deprecated in favor of migrations, initial_data fixtures are renamed (to avoid confusion). They're explicitly specified in testcases. * Django 1.7: Lazy loading of User model in management command After app loading change, User model can no longer be referenced while app registry is not populated. This commit completes work in c7aa6c8 * Django 1.7: Fix ApiAccess __unicode__ for py3 * Django 1.7: Fix customuser tests Due to app loading changes app_label is no longer effective, so CustomUser model is redefined in tests. Fixture name is changed to avoid collision with contrib.auth fixture * Django 1.7: Update tox config Update Django and django-oauth-plus versions * Django 1.7: Minor testsuite fixes * RuntimeError in related_resource, due to model name conflict * ValueError when assigning to non-nullable field * Add explicit MIDDLEWARE_CLASSES to avoid checks framework warnings * Django 1.7: Fix PutListNestResouceValidationTestCase Attribute 'pk' is ignored, use 'id' instead. Due to non-monotonic autoincrement in sqlite prior 1.7 test was passing: both notes were deleted and created anew (pk were not taken into account, they just "fortunately" matched pks of newley created models). * Fix for issue django-tastypie#955 * Added test case for the fix * Removed incorrect import * Django 1.7: Update tox to latest Django release * Fixed test requirement for PyYAML. Thanks to AndrewGrossman for the report! * find resource name using rfind this fixes a bug where app name and resource name are the same. /projects/api/v1/projects/1/ should return projects/1/ not projects/api/v1/projects/1/ * make sure we throw exception if not found * add test * Added release notes for v0.12.0. * Bumped to 0.12.0! * Starting next dev cycle. * Enable syntax highlighting in README * Update current version in README.rst * Added 0.12.1 release notes. * Bumped to v0.12.1! (posthumously, since I tagged the wrong commit) * Starting the next dev cycle. * pep8 * add failing test for prefetch_related resources * update test * fix bug by invalidating prefetch cache * Update python3.rst simple change from "Tastpie" to "Tastypie". * Update serialization.rst fixed typo * Added my name to the AUTHORS file for the bug fix django-tastypie#955 * Django 1.8 compatibility * Small optimization of `extract_credentials` method Getting the header value for key `HTTP_AUTHORIZATION` only once for this method * Django 1.8: requires SITE_ID in settings * Django 1.8: update tarball location for 1.8 * Django 1.8: fixed core tests * Django 1.8: fixed basic tests * Django 1.8: fix for minor change in unset attributes of model objects * Django 1.8: update travis config for one more env, disallow 1.7 and 1.8 failures * Django 1.8: fixed related_resource tests. I have no idea what I am doing. * Django 1.8: whoops, no such env as 1.8+py2.6 * Django 1.8: removing not-implemented complex tests which break due to importing deleted comments app * Last traces of complex.tests * PUT response code correction See commit for details * Update PUT response codes See commit for details: django-tastypie@abc0bef * Test against latest django-oauth-plus from PyPI * Update to django 1.8b1 * Changed html_theme to "classic" (from "default") to prevent Sphinx build error * Update testenvs to use official Django 1.8 release * Replace with python importlib Replace django.utils.importlib with python importlib "Warning: django.utils.importlib will be removed in Django 1.9" * Replace with python importlib Fix Warning: django.utils.importlib will be removed in Django 1.9. * Pinning version of mock as 1.1.0 and later no longer support python 2.6 * Bump version in README, note max django version. * [doc] add missing imports in non-orm example The example is still missing some but it's nice to show where `Resource` can be imported from. * add import for Authorization * Added my name to the AUTHORS file for django-tastypie#1311 * Upgrade to new Travis CI containers. * Add code coverage collection. * Cache pip dependencies. * Added flake8 checking, moved doc builds to seperate matrix entry (no need to rebuild docs for each version of Django). * Added myself to AUTHORS. * Removed ApiField.value, which is unused and undocumented. It seems to be leftover from when field objects could store values and be serialized. * Run `gis` tests in Spatialite on Travis CI. * Fixed throttle tests to be deterministic. * Fixing importlib error notification TravisCI fix * Switch order of check for callable and string. This is necessary because in apply_sorting attribute can be only a string, so by first checking for callable and then string in dehydrate, one can define a callable string, which can then be used both in dehydrate and apply_sorting. * Reduces a query when looking up a user and another when comparing api keys. * Allow creation of related resources that have an 'items' related_name * Added mthornhill to Authors * remove unimplemented to_html/from_html * add to authors * Performance improvements to Serializer.to_simple(), which gets called all the time. Got ResourceProfilingTestCase running in 14% less time. * Added profilingtests. * Reduced number of calls to callable() and other small fixes. * Faster version of BaseThrottle.convert_identifier_to_key(). * dict_strip_unicode_keys now uses a list comprehension. * Various performance improvements and code cleanup. * Added tests for Serializer.to_simple(). * Removed obsolete Django 1.3/1.4 compatibility code (current minimum supported version is 1.5). * Fixed git command, closes django-tastypie#840 * Styling fixes for tastypie. * Styling fixes for tests. * Line length and code complexity checking are optional, remaining checks are now mandatory. Styling fixes for docs/conf.py. * Changes to make builds faster. Adds coverage to tests to make sure they're all being executed. * If GEOS is not installed then exclude geos related calls. * Added Travis CI, Coveralls, and PyPi badges * Fixed PyPi badges, added new badges. * Fixes Resource.deserialize() to honor format parameter - issue django-tastypie#1354 * Added test for issue django-tastypie#1354. * More tests for Resource().get_via_uri(). * Raise ValueError when trying to register a Resource class instead of a Resource instance. * Adding a test case to show that a related resource is correctly saved. * Removed call to fk_resource.can_update(), since fk_resource.save() will the necessary object permissions checks (authorized_update_detail or authorized_create_detail). can_update() only checks the HTTP methods allowed, which doesn't really make sense for related resources. This fixes a bug (PR django-tastypie#1344) in otherwise updatable related resources which had PUT disabled. The bug caused the related resource to overwrite existing columns in the DB, since missing values were set to default since full hydration hadn't taken place. Also includes changes to prevent saving bundles loaded via URI, and to make sure data is only saved inside Resource classes (replaced calls to obj_update() in RelatedField().resource_from_data()). * Use Tastypie DateField for DateField on the model. * Importing unicode_literals causes a problem in South when importing from django.db.models.fields because then the name of the model class that is passed into __import__ is unicode, rather than a byte string. I'm not quite sure why this causes a problem importing from this module but not others. However, removing the unicode_literals import from these migrations fixes the issue, and it doesn't look like the migrations have any literals that need to be unicode. * Documentation fix for "Per-Request Alterations To the Queryset doesn't work as described django-tastypie#1352". * Added failing test for embedded schemas. * API consumers can now GET /api/v1/?fullschema=true to get all schemas in a single request. * Updated flake8 commands to ignore Python files in hidden directories. * Added failing test case when a non-null field is omitted. * ApiFieldError is now raised when a not-null field is missing. * New cookbook example for using resources in views with a valid 'resource_uri' * Fixed cookbook for issue django-tastypie#962 ("Cookbook - Using Your Resource In Regular Views - incorrect example"). * Added verbose_name to API schema. * Added more tests for Resource().get_via_uri() (django-tastypie#949). * add test for reverse one to one saving. * Add fix for django-tastypie#566 to ToOneField. All tests pass. * Started setup for doctesting our docs. (django-tastypie#1347) * Fixes django-tastypie#1384 * Test for issue django-tastypie#1323. * Fixes bug which occurs when whatever field detail_uri_name is (usually 'pk') has a default value. (Issue django-tastypie#1323) * Reorder CI build matrix so that flake8 checks happen first. * copy `same_origin` from django stable/1.8.x branch * Change Django dev source URL to avoid pip error. * Bumped Django versions, added testing for Django 1.9. * Fixed disabling cache using timeout=0, appropriate unit tests added; fixes django-tastypie#1213, django-tastypie#1212 * Removed Django 1.5-1.6 support. * Replaced reference to SingleRelatedObjectDescriptor for Django 1.9 compatibility. * Replaced `get_caches` with `caches` for Django 1.9 support. * Changes to make tests pass in Django 1.9. * Updated README to correct version info. * stop using django.conf.urls.patterns * Fix for saving related items when resource_uri is provided but other unique data is not. (django-tastypie#1394) * Release for v0.13.0. Added missing release notes for v0.12.2 (django-tastypie#1345). * Updated old project URLs. * Prevent muting non-django's exceptions (e.g. RequestException from 'requests' library). (Fixes django-tastypie#1297, from PR django-tastypie#1404) * Added namespaces documentation. * Add ResourceTestCaseMixin, deprecate ResourceTestCase. * Added RTD link to README. * Added error message when JSON requests cannot be deserialized * Added tastypie to sys path in docs/conf.py. * Updated docs to recommend StackOverflow instead of old Google Group. * Updated/synced README and index.rst. * Fix for flake8 failure. * Move Content-Type from assertHttpAccepted The assertion was in the wrong place. Rather than being in the tests covering TastyPie, the Content-Type header assertion was in `ResourceTestCase.assertHttpAccepted`. * Fixed patch_detail not returning updated data. (Closes PR django-tastypie#1282) * Added failing test for prefetch_related data during PATCH on a detail endpoint. (PR django-tastypie#1282) * Fixed some doc formatting. * Updated CONTRIBUTING docs. * Gracefully handle UnsupportFormat exception Return HTTP 400 from _handle_500 when handling an UnsupportedFormat exception. * Fixes django-tastypie#1413 - `obj_update` will take one more database search when bundle.obj.pk is an int value. * Cleaned up tox and travis configs. * Got rudimentary tests for the cookbook working. (django-tastypie#1347) * Fix for issue django-tastypie#782 (django-tastypie#782) Schema information only gives information about whether a related field is 'to_one' or 'to_many', but not about the actual resource type of the field. This fix adds a new field, called 'related_schema' to the fields schema entry, which defines the exact type of the related field: 'user': { [..] 'type': 'related' 'related_type': 'to_one' 'related_schema': '/api/v1/user/schema/' } Changes by @juditnovak Unit tests by @mikebryant * Changed repr-value for Bundle to str in PY2 * Version bump for v0.13.1. * Updated DjangoAuthorization to disallow read unless a user has `change` permission. (Closes django-tastypie#1407, PR django-tastypie#1409) * Moved `detail_uri_kwargs` to Resource class. (Fixes django-tastypie#1431) * Fixed doc theme change introduced in 8674d6b. Closes django-tastypie#1430. * Disallow python-mimeparse==1.5 due to bug. Closes django-tastypie#1429. * Authorization classes now handle usernames containing spaces. Closes django-tastypie#966. * Fixed UnboundLocalError in lookup_kwargs_with_identifiers. Closes django-tastypie#942. * Cleaned up old, unneeded code. (closes django-tastypie#1433) Reuse Django test `Client.patch()`. (@SeanHayes, closes django-tastypie#1442) Just a typo fix in the testing docs (by @bezidejni, closes django-tastypie#810) Removed references to patterns() (by @SeanHayes, closes django-tastypie#1437) Removed deprecated methods `Resource.apply_authorization_limits` and `Authorization.apply_limits` from code and documentation. (by @SeanHayes, closes django-tastypie#1383, django-tastypie#1045, django-tastypie#1284, django-tastypie#837) Updates docs/cookbook.rst to make sure it's clear which `url` to import. (by @yuvadm, closes django-tastypie#716) Updated docs/tutorial.rst. Without "null=True, blank=True" parameters in Slugfield, expecting "automatic slug generation" in save method is pointless. (by @orges, closes django-tastypie#753) Cleaned up Riak docs. (by @SeanHayes, closes django-tastypie#275) Include import statement for trailing_slash. (by @ljosa, closes django-tastypie#770) Fix docs: `Meta.filtering` is actually a dict. (by @georgedorn, closes django-tastypie#807) Fix load data command. (by @blite, closes django-tastypie#357, django-tastypie#358) * Fix in `Resource.save_related`: `related_obj` can be empty in patch requests (introduced in django-tastypie#1378). (Fixes django-tastypie#1436) * Avoid modifying Field instances during request/response cycle. (closes django-tastypie#1415) * Cleaned up self referential field handling. * Removing the Manager dependency in ToManyField.dehydrate(). (Closes django-tastypie#537) * Related schemas no longer raise error when not URL accessible. (Fixes PR django-tastypie#1439) * Updated release notes. * Fixed bug that prevented fitlering on related resources. `apply_filters` hook now used in obj_get. (Fixes django-tastypie#1435, Fixes django-tastypie#1443) * Use `build_filters` in `obj_get`. (Fixes django-tastypie#1444) * Test for django-tastypie#1446 which was fixed in af2c1d6. (Closes django-tastypie#1446) * Updating for v0.13.2 release. * Permit changing existing value on a ToOneField to None. (Closes django-tastypie#1449) * Updating for v0.13.3 release. Co-authored-by: Sean Hayes <sean@seanhayes.name> Co-authored-by: Stefan Wehrmeyer <mail@stefanwehrmeyer.com> Co-authored-by: Ilya Baryshev <baryshev@gmail.com> Co-authored-by: harishn_knowlarity <harish.srinivas@eng.knowlarity.com> Co-authored-by: harishn_knowlarity <me.harishn@gmail.com> Co-authored-by: Daniel Lindsley <daniel@toastdriven.com> Co-authored-by: Sam Kuehn <samkuehn@gmail.com> Co-authored-by: Corey Farwell <coreyf@rwell.org> Co-authored-by: Chris Adams <chris@improbable.org> Co-authored-by: Jann Kleen <jann.kleen@freshx.de> Co-authored-by: Eric Theise <erictheise@gmail.com> Co-authored-by: Matthew Crowson <matthew.d.crowson@gmail.com> Co-authored-by: George Dorn <georgedorn@gmail.com> Co-authored-by: Willem Bult <willem.bult@gmail.com> Co-authored-by: Chewey <prosto-chewey@users.noreply.github.com> Co-authored-by: Seán Hayes <gasphynx@gmail.com> Co-authored-by: Simon Kelly <skelly@dimagi.com> Co-authored-by: Max Naude <maxnaude@gmail.com> Co-authored-by: Renjith Thankachan <mail3renjith@gmail.com> Co-authored-by: Sam Thompson <sam.thompson@buildingenergy.com> Co-authored-by: Nik Nyby <nnyby@columbia.edu> Co-authored-by: Renjith Thankachan <sideffect0@users.noreply.github.com> Co-authored-by: Jelena Kutalovskaja <jelena.kutalovskaja@gmail.com> Co-authored-by: Mitar <mitar.git@tnode.com> Co-authored-by: mick <michael@maithu.com> Co-authored-by: Fedor Baart <fedor.baart@deltares.nl> Co-authored-by: Phoebe B <phoebebright310@gmail.com> Co-authored-by: Patrick Hagemeister <patrick@krankikom.de> Co-authored-by: annacorobco <anna.corobco@gmail.com> Co-authored-by: strets123 <strets123@gmail.com> Co-authored-by: Alexey Kotlyarov <alexey@infoxchange.net.au> Co-authored-by: Ben Demboski <bend@meevine.com> Co-authored-by: Simon Ye <sye737@gmail.com> Co-authored-by: yuri <yuri.govor@gmail.com> Co-authored-by: Charpentier Johan <jcharpentier@bearstech.com> Co-authored-by: David Hatch <dhatch387@gmail.com> Co-authored-by: Guilhem Saurel <guilhem.saurel@gmail.com> Co-authored-by: Vsevolod Novikov <nnseva@gmail.com> Co-authored-by: Brad Pitcher <bradpitcher@gmail.com> Co-authored-by: Alexey Subbotin <dotsbb@gmail.com> Co-authored-by: Eleni Lixourioti <contact@eleni.co> Co-authored-by: Jack Cushman <jcushman@gmail.com> Co-authored-by: John Lucas <john.lucas845@gmail.com> Co-authored-by: Matt Briancon <matt.briancon@gmail.com> Co-authored-by: Miguel Gonzalez <scenting@gmail.com> Co-authored-by: Mike Bryant <m@ocado.com> Co-authored-by: Maxim Filipenko <proktusfahitasiv@gmail.com> Co-authored-by: Thomas @ BeeDesk <thomas@beedesk.com> Co-authored-by: J. Javier Maestro <jjmaestro@twoapart.com>
Looking at
tastypie/authorization.py
(around like 133 -- master branch on 1/4/2016), the default, bothread_list
andread_details
bypassuser.has_perm()
check, which is quite unsafe and is a bad default.Django's Admin default is unconventional. So, I could see how it was misinterpreted.
https://docs.djangoproject.com/es/1.9/topics/auth/default/#permissions-and-authorization
Essentially, "change_xyz" is the permission code for both "read" and "update". I think the better default would be following Django's Admin:
The text was updated successfully, but these errors were encountered: