(SOLVED) How to add a more fine grid authentication/permissions to groups in Django

Hallo,

This may not be a Django specific question but I do not know where to ask it instead.

I have 3 models

class MyCompany(models.Model):
    name = models.CharField()
class MyUser(AbstractUser):
    name    = models.CharField()
    company = models.ForeignKey(MyCompany, on_delete=models.CASCADE, blank=False, null=False)

so every user is a member of a company.

Then I have.

class MyNote(modles.Model):
    note       = models.TextField(null=False)
    company    = models.ForeignKey(MyCompany, on_delete=models.CASCADE, blank=False, null=False)
    created_by = models.ForeignKey(MyUser, on_delete=models.SET_NULL, null=True)

    class Meta:
        permissions = ( ("my_own_can_create", "Can create new note"),
                        ("my_own_can_edit",   "Can edit note"),
                        ("my_own_can_delete", "Can delete note"),
                      )

So a note belongs to a company and is created by an user. So far so good.

e.g.
company(a) → user(b) → note(c)
company(d) → user(e) → note(f)

Now what I try to achieve is to enable an user(e) from company(d) to create a note on behave of an other company(a). Where the foreignkey must point to company(a) but the created_by foreignkey should point to user(e). The restriction is that I can not exposes all companies to all users and I need to give access to user(e) to act on behave of company(a). This I think I can do with groups.

To complicate things is that user(e) has the ability to create-edit-delete notes for company(d) but only create note rights for company(a) and I do not know how to implement this differentiation.

Any suggestions?

I strongly suggest you adopt the Python / Django naming conventions where class names are capitalized. These models would then become MyNote and MyCompany.

Side note (no pun intended): What you have effectively created here is a ManyToMany relationship between MyCompany and MyUser using the through model MyNote, with the additional field note. I’m pointing this out, not because it fundamentally changes anything, but making the ManyToMany semantics available may make some things easier.

How you want to implement this may, to some degree, depend upon the cardinality of MyCompany. There’s a huge difference in relative overhead between having 10 companies involved and 1000.

For example, you could create another - different - model to represent the relationships between users and companies. Again, it depends upon some more details than what you’ve provided here, but if this create-edit-delete authority is solely assigned to the company identified by the MyUser.company field, then one of your options is to add another field to MyUser with a ManyToMany relationship with MyCompany, to identify those companies that this user is able to create notes for. (The ManyToMany field is a separate model, and if you need to represent multiple relationships in addition to the create note permission, you can add an additional field to that through model.)

Thanks, changed that.

Haven’t thought of it like that but yes your right.

Yes, I thought about a ManyToMany with through model but permissions are not related to models but to users (I have edit my original question with info how I add permissions).
And if I assign permission my_own_can_create to user(e) it will have that permission for both companies.
I do not want a through m2m model with a bunch of boolean fields to set permissions, as the MyNote is not the only information I try to have.

Techincally, an instance of “Permission” is just a label. It has no functionality, it’s just data to be used elsewhere.

Yes, when a permission is created, it’s created as a reference to a model. But that has nothing to do with how they can be used.

For some further thoughts on this distinction and usage of permissions, see:

Not necessary (nor appropriate in a normalized relational model) to do that.

Your through model could contain either:

  • A reference to a single Permission, where you allow for multiple instances of the entry for (User, MyCompany)
  • A ManyToMany to Permission for the complete set of permissions assigned to that User relative to that Company

The first version is likely to make the permissions tests easier, while the second version will likely make the administration of the permissions for a user easier. (Off-the-cuff conjectures, no hard data to support this.)

I get the m2m concept but I’m strugeling with your suggestion:

So for your first suggestion, would it look like this?

class MyCompany(models.Model):
    name = models.CharField()
class MyUser(AbstractUser):
    name    = models.CharField()
    company = models.ManyToManyField(MyCompany, through=MyPermissions)
class MyPermissions(models.Model):
    company    = models.ForeignKey(MyCompany, on_delete=models.CASCADE, blank=False, null=False)
    user       = models.ForeignKey(MyUser, on_delete=models.CASCADE, null=False)
    permission = models.Permission()  # ????????????

And then I can add the my_own_can_create to the m2m permission field of MyPermissions ?
That would be great, didn’t know you can do that.

Close.

More like:

class MyPermissions(models.Model):
    company    = models.ForeignKey(MyCompany, on_delete=models.CASCADE, blank=False, null=False)
    user       = models.ForeignKey(MyUser, on_delete=models.CASCADE, null=False)
    permission = models.ForeignKey(Permission, ...) 

or

class MyPermissions(models.Model):
    company    = models.ForeignKey(MyCompany, on_delete=models.CASCADE, blank=False, null=False)
    user       = models.ForeignKey(MyUser, on_delete=models.CASCADE, null=False)
    permission = models.CharField(...)  

where the content of the permission field is the name of the permission.

Right, Permission is a model so a ForeignKey make sense!
Thanks a lot, I get the idea now I’ll try to implement it in my application.

May be one more question:

How does this relate to the last m2m option? Is this very unbeneficial for lot’s of users and companies?

Just the opposite. I would consider the m2m option(s) to be preferred when the number of companies increases. It’s when the number of companies is small that I’d be thinking about alternative solutions.

For example, with only 5 companies, I’d consider making individual groups for the necessary roles by company. That’s an easy solution that doesn’t scale all that well.

Thanks for your help Ken, I gained a lot of insight.