Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#30028 closed Bug (duplicate)

Uneditable object still editable through change_list if list_editable not empty

Reported by: ksl Owned by: nobody
Component: contrib.admin Version: 2.1
Severity: Normal Keywords: changelist
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Abstract

This bug allows an object that should be uneditable (its has_change_permission method always returns False) to be edited through an editable changelist.

Steps to reproduce

  • Use the following admin:
class ArticleAdmin(models.ModelAdmin):
	list_display = ("title", "author", "abstract")
	list_editable = ("title", "author")

	def has_change_permission(self, request, obj=None):
		return False
  • Navigate to the article changelist.
  • Change any title/author field and save.

Result

The modified article objects are indeed modified and saved to database.

Expected result

The changelist view should (as does change form) display read-only fields (ie: spans, not inputs), and disallow any modification to be saved to database.

Technical information

Tested on Django 2.1.4.

Attachments (2)

django_test.tar.gz (65.2 KB ) - added by ksl 5 years ago.
Test project
django_test.sql (37.9 KB ) - added by ksl 5 years ago.
Test project postreSQL database dump

Download all attachments as: .zip

Change History (12)

comment:1 by Carlton Gibson, 5 years ago

Resolution: worksforme
Status: newclosed

I can't reproduce this.

  • For a superuser list_editable is working as expected.
  • For a user with view-only permissions on the admin, list_editable fields are not presented as form widgets. (As expected.)
    • Any POST data submitted is not processed.
  • Same adding has_change_permission() to always return False
    • For superuser and view-only user, fields are not presented as editable.

I'm going to close as-is. If you can provide an example project reproducing this (perhaps with a frozen requirements files so we can see the exact Django version) I'm happy to look again.

comment:2 by ksl, 5 years ago

Sorry, my bad.

The situation is actually more complex but boils down to the fact that has_change_permission is called with obj=None.
This does not allow individual objects (rows) in the changelist to be editable while others are not: either the whole changelist is editable or it's not. Or am I missing something here?


Edit:

The admin to reproduce this could be:

class ArticleAdmin(models.ModelAdmin):
	list_display = ("title", "author", "abstract")
	list_editable = ("title", "author")

	def has_change_permission(self, request, obj=None):
		if obj is not None:
			return obj.pk == 1

In such a case (which is closer to our actual use case), the Article object with ID 1 will not be editable through its changeform but would be through its changelist.

Last edited 5 years ago by ksl (previous) (diff)

comment:3 by Carlton Gibson, 5 years ago

Can you put this into a project or a test case, so we can see it in action?

comment:4 by Carlton Gibson, 5 years ago

Happy to look at a project if you can provide one but just glancing at the code, it looks like a programming error: you’re going to need to look at the request.user to see what you should return. Otherwise you’ve overridden the default implementation, which protects against this sort of thing, and created the issue.

You should probably be calling super() before your own logic, and only continuing if that returns True.

Last edited 5 years ago by Carlton Gibson (previous) (diff)

comment:5 by Simon Charette, 5 years ago

That looks like a duplicate of #15759 to me.

by ksl, 5 years ago

Attachment: django_test.tar.gz added

Test project

by ksl, 5 years ago

Attachment: django_test.sql added

Test project postreSQL database dump

in reply to:  4 comment:6 by ksl, 5 years ago

Replying to Carlton Gibson:

Happy to look at a project if you can provide one but just glancing at the code, it looks like a programming error: you’re going to need to look at the request.user to see what you should return. Otherwise you’ve overridden the default implementation, which protects against this sort of thing, and created the issue.

You should probably be calling super() before your own logic, and only continuing if that returns True.

Please find enclosed a test project reflecting our situation. In this project, the Question object with ID 1 should be the only one editable.
As you understand, our logic here is not based on per-user permission (hence we do not use request.user nor do we call super()) but on per-object permission.

Test project credentials:

  • User admin with password adminadmin
  • User notadmin with password adminadmin

in reply to:  5 comment:7 by ksl, 5 years ago

Replying to Simon Charette:

That looks like a duplicate of #15759 to me.

Might be a duplicate indeed, except I'm not sure I understand the "if an auth backend supports per-object permissions." correctly.
In our case, it's a matter of "if an object's has_permission returns False".

comment:8 by Carlton Gibson, 5 years ago

Resolution: worksformeduplicate

Hi ksl — Thanks for the follow-up.

Looks like Simon's right about it being a Duplicate of #15759. With the superuser all rows are shown as editable.

The view-only user behaviour looks correct though: No rows are shown as editable if the user can only view the admin.

comment:9 by ksl, 5 years ago

View-only user is actually the only one working as expected. Once you empower a user to change objects of the model, the has_change_permission logic is somewhat bypassed (or at least does not allow a per-object logic).

Thank you, for taking precious time to answer.

comment:10 by Carlton Gibson, 5 years ago

No problem. Thank you for your report, and for the effort of making sure I followed properly. 🙂

Note: See TracTickets for help on using tickets.
Back to Top