What is the purpose of Permissions?

Hello,

I am working on a web application which has registered users and I am trying to understand whether I need to use the permissions feature, and if so then how to use it properly.

Let’s take a website line Linktree: users can sign up and create a page which lists links. So there is some User model, some Tree model and some Entry model. Entries belong to a tree, a tree belongs to a user. A user can have multiple trees. A user (let’s call her Alice) signs up, logs in, gets to her profile page, adjusts a couple of settings and a page is generated. Another user (let’s call him Bob) can view Alice’s page under let’s say /tree/k24j2lk4j/ (because it is public, no login required), but he cannot edit it. If he tries to open the URL (/user/profile/) he will see his own profile, not Alice’s.

To my understanding all authorization happens via authentication. Bob must not be able to edit Alice’s profile page, but the view only lets him edit he own page anyway. Would there be a way for Bob to somehow manipulate Django into altering the Tree or Entry instances or database tables which belong to Alice by feeding a particular URL into the browser?

My web application uses a custom User class that inherits AbstractBaseUser. The only real difference is that my class uses the email address for identification and has no user name. I was following the Customizing authentication in Django guide and the example implementation simply gives full blanket permissions to every user. I must be missing something here because I don’t see what purpose the permission system serves.

The permissions system is designed to work directly with views.

In the general case, a User may be assigned permissions. Or, a User may be made a member of a Group, and the permissions are assigned to the Group.

Each view then checks to see whether the user who is trying to access that view is allowed to do that.

If this test only needs to check the user making the request, you can use the permissions_required decorator on a function.

Otherwise, if your view needs to check other data (like a parameter being passed in the url), the other options available to you are documented throughout the rest of the docs at Using the Django authentication system | Django documentation | Django.

Assuming Bob is a regular user of the site, Bob doesn’t have access to the database. Bob issues requests, which are directed to views. The view defines what Bob can or cannot do. What Bob can do is solely determined by the permissions tests written for those views.

These tests can be as intricate and complex as you need them to be. We have a system where permissions can be granted based upon the object being accessed along with the time of day/day of week when the access is requested.

Side note: Do not mix up authentication with authorization, while they are related topics, they are two separate and distinct issues. Authentication addresses the issue of identifying who is accessing the system. Authorization deals with determining what that user can do. You do need to identify yourself (authenticate) before the permissions system can determine if you’re allowed to access the view you are trying to access.

1 Like

Each view then checks to see whether the user who is trying to access that view is allowed to do that.

Does that mean that if my views are not restricted by permissions I don’t need to use the permission system? In my above example when Bob is logged in the view function will pick out the “tree” that is associated with him from the database and render it in the template. So both Alice and Bob may access the same view, but the view itself picks the correct “tree” object based on the currently logged in user.

I am trying to come up with a scenario where the permission system would be useful. Maybe in a forum software there would be an “edit” and “delete” button that is only visible to admins. A regular user might be able to figure out the URL and enter it manually into the browser, but the view would refuse to process that request. A user should be able to edit his own post, so there already needs to be some sort of “post author” check through object-level permissions. Is this a reasonable use-case for permissions?

Another question: are permissions automatically enforced by by Django? Like if a user has only read permissions for a model, can the view update objects regardless of the current user without checking permissions?

That is correct. If you have no views that need to authorize an action based on information not present in the request, then there’s no need to use it. And yes, the remainder of that paragraph is correct.

It is useful in any situation where the same url needs to behave differently based upon information not present in the request.

Maybe. I’m not sure which portion of the description that you think the permission system would apply to.

Some examples of real-life usage of the permission system:

  • Instead of Bob and Alice, it’s two teams, Baltimore and Annapolis. Each team has actions that they can perform on their “tree”. You can no longer simply depend upon identifying the user, you need to ensure that the person editing that tree is a member of the same team.

  • An editorial / approval process workflow for a company blog. An employee may be able to create the blog entry, but their supervisor needs to approve it before it can be published. Likewise, the supervisor may also be able to create a blog entry, but their supervisor must approve it, that supervisor may not approve their own entry.

  • A view generating some type of report, where the contents of the report differ based upon the employee’s position within the company. (e.g., Staff, Supervisor, or Manager)

  • Bob and Alice can access a certain view to see their own tree, but Carol isn’t allowed on that page at all.

  • A navigation menu in your system, where the menu entries available are different based on a person’s job, or possibly even that the view referenced by that entry may differ.

These are fairly trivial examples - every case I mentioned above could be addressed in different ways. However, in most cases, the real requirements for something like this can get significantly more complex. The Group/Permission system facilitates creating the logic for managing that type of complexity.

It depends upon what you mean by “automatically”. You need to identify what permissions are required by an individual view, and apply the proper test(s) to that view.

Permissions aren’t granted to models. Permissions are tested by views. There is no such thing as “having read permission on a model.” A user may have the permission named "foo.view_bar", but that itself does not grant permission to view Bar objects anywhere other than in the admin.

The admin uses the permission system for that purpose, but that’s an arbitrary decision made by the admin.

The purpose or semantics of a permission named foo.view_bar is entirely up to you in your application. If you wish to say that the permission named foo.view_bar is going to give a user the ability to delete instances of Baz, that’s perfectly ok. (Confusing, perhaps. Almost certainly a bad decision from a maintenance perspective. But, Django doesn’t care.)

With all this in mind, I suggest you reread the section Permissions and authorization, and work on adjusting your mindset as to what a Django Permission is.

A view will always perform any operation it is written to perform.

One of those operations may be to check permissions before rendering a page or performing an update operation.

1 Like

Let’s say that in a company blog only a manager has permission to delete a post. In the view I check whether the current user has permission to delete a post and then send a boolean can_delete as part of the context to the template. Then in the template I do something like this:

{% if can_delete %}<a href="/blog/2023/10/17/new-product-reveal/delete/">delete</a>{% endif %}

That means I use permission blog.delete_post to hide a UI element, but I do not use permissions when I actually delete the post. Could a malicious employee type in the URL manually to delete a post, or would Django automatically raise an exception once the view tried to perform the deletion in the database? From what I have read in this thread this would indeed be a vulnerability in the application.

Yes, that is exactly the case. Nothing prevents any user from issuing a request on any url, which is why you want to put the appropriate protection on every view. (Or at least every view that you don’t want everyone to be able to use.)

1 Like

Thank you, I think I understand now. To recap:

  • Assigning permissions to a user does nothing in of itself
  • A view can make decisions based on the user’s permissions if it needs information beyond what is included in a request
  • The name of a permission is completely arbitrary
  • The admin does use permissions of the form <app>.<action>_<model> to authorize user actions, but this form is just a convention that the admin app has adopted, it has no meaning on its own

I am surprised that AbstractBaseUser does not have any default implementations for has_perm and has_module_perms when both of these methods are mandatory. Should I just make my model inherit from PermissionsMixin to use the default implementation?

To be completely accurate, a view can make decisions even if it only needs the information in a request. The permissions system is desirable when you need additional information, but it’s not necessary that addition information be needed in order to use the permissions system. You can use it anywhere you want.

Other than that fine distinction, your understanding is correct.

As you pointed out earlier, those methods are not mandatory. It’s perfectly acceptable to create a Django project without the permissions system. (I believe that that would prevent the Admin from working, but then the Admin is an optional model. There’s no requirement that it be available.)

At a minimum, yes. See the docs Custom users and permissions for more details.

1 Like

Thank you very much, this clears up my confusion.