Deprecate is_superuser, is_staff and is_active

932 views
Skip to first unread message

guettli

unread,
Mar 24, 2017, 7:31:32 AM3/24/17
to Django developers (Contributions to Django itself)
I know this is a crazy idea, and it will get the "won't fix" tag very soon.

Nevertheless I want to speak it out.

My use case: Get a queryset of users who have a given permission.

I would like to get this with a simple SQL statement.

At the moment this query is complex and results in performance issues, since
several outer joins get created. See: https://groups.google.com/forum/#!topic/django-users/lUGkZybXllM

is_superuser and is_staff could be changed to permissions easily.

It's different with is_active. But again: a simple SQL to get all users which have a given permission is my goal.

I personally could live without is_active: Just remove all permissions for this user.

If you don't like dropping all perms to make a user inactive since you want to switch back and forward between active and inactive   ...
then this a different (bigger) problem: switching back and forward between states. Why not solve
this for all models (not just User instances)? But this is a different topic :-)

I know the road to deprecate these three friends would be hard since a lot of code uses them.

What do you think?






Andrew Ingram

unread,
Mar 24, 2017, 7:42:03 AM3/24/17
to Django developers (Contributions to Django itself)
I've always felt that `is_staff` should be changed to a permission such as `can_access_admin` provided by the Admin app itself. However, `is_superuser` is slightly different, in that it's not a permission, but rather a flag that says "this user has EVERY permission". It's also potentially nonsensical from a functional perspective, you could have two permissions that are intended to be mutually exclusive, but a superuser would nonetheless have both of them - potentially resulting in a broken/nonsense UI. This is the main reason why almost nobody should be a superuser (yet nearly every Django project i've inherited has nearly every member of staff marked as superusers).

`is_active` is intended to be used a little differently isn't it? It's not saying "user has no permissions", it's saying "user is disabled". It's not access-control, it's data.

I think that most of it could do with a rethink, but `is_staff` is probably the biggest wart.

Andy

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/961fd91c-3954-4c7f-a05e-807f2863dc5e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Tim Graham

unread,
Mar 24, 2017, 8:41:10 AM3/24/17
to Django developers (Contributions to Django itself)
I don't think the current fields are so bad or nonsensical that it warrants a change. Also, consider that every Django user would have to learn how to use a new permissions setup.

If you don't like the default permissions structure, use a custom user model.

guettli

unread,
Mar 24, 2017, 8:55:58 AM3/24/17
to Django developers (Contributions to Django itself)


Am Freitag, 24. März 2017 12:42:03 UTC+1 schrieb Andrew Ingram:
I've always felt that `is_staff` should be changed to a permission such as `can_access_admin` provided by the Admin app itself.

 
However, `is_superuser` is slightly different, in that it's not a permission, but rather a flag that says "this user has EVERY permission". It's also potentially nonsensical from a functional perspective, you could have two permissions that are intended to be mutually exclusive, but a superuser would nonetheless have both of them - potentially resulting in a broken/nonsense UI.

Thank you for this easy to understand use case. I ask myself if "this user has EVERY permission" is needed at all. If the user should have all permissions, then why not give
him all these permissions at database level? It's like a very special 1:N relation which is: 1:∞  :-)



 
This is the main reason why almost nobody should be a superuser (yet nearly every Django project i've inherited has nearly every member of staff marked as superusers).


Unfortunately we detected this "best practice" too late. We need to switch now, but this not related to django-dev, this needs to be solved in our company.

 
`is_active` is intended to be used a little differently isn't it? It's not saying "user has no permissions", it's saying "user is disabled". It's not access-control, it's data.


Let's look at our use case: I want a queryset of all users which have a given permission. Do I want to be
inactive users in this queryset?  I am unsure. But I am sure: I want this queryset to be simple at SQL level.

 
I think that most of it could do with a rethink, but `is_staff` is probably the biggest wart.

Andy




Thanky you Andy

Regards,
  Thomas

guettli

unread,
Mar 24, 2017, 9:00:42 AM3/24/17
to Django developers (Contributions to Django itself)


Am Freitag, 24. März 2017 13:41:10 UTC+1 schrieb Tim Graham:
I don't think the current fields are so bad or nonsensical that it warrants a change. Also, consider that every Django user would have to learn how to use a new permissions setup.

If you don't like the default permissions structure, use a custom user model.



Yes, I guess we will go this way.

Are there established third party solutions? ..... I could not find any with my favorite search engine ...

Thank you Tim

Regards,
  Thomas
 

Collin Anderson

unread,
Mar 24, 2017, 9:12:44 AM3/24/17
to django-d...@googlegroups.com
Hi Thomas,

"If the user should have all permissions, then why not give him all these permissions at database level?" - I have some use cases where there are only 3-5 people that need to log into the admin. I don't really need to set different levels of access for different people. I also don't want to have to go through and add new permissions to every person every time I add a model. So, I just mark them all as is_superuser and don't use permissions at all. It keeps things simple.

Something like this might handle your situation:
class MyUser:
    # etc
    is_superuser = False
    is_staff = property(lambda u: u.has_perm('admin'))

Collin

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-developers+unsubscribe@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.

guettli

unread,
Mar 27, 2017, 2:46:28 AM3/27/17
to Django developers (Contributions to Django itself)


Am Freitag, 24. März 2017 14:12:44 UTC+1 schrieb Collin Anderson:
Hi Thomas,

"If the user should have all permissions, then why not give him all these permissions at database level?" - I have some use cases where there are only 3-5 people that need to log into the admin. I don't really need to set different levels of access for different people. I also don't want to have to go through and add new permissions to every person every time I add a model. So, I just mark them all as is_superuser and don't use permissions at all. It keeps things simple.

Something like this might handle your situation:
class MyUser:
    # etc
    is_superuser = False
    is_staff = property(lambda u: u.has_perm('admin'))


Yes, configuring permission each time you add a model makes no fun. I have to think about this again.

Regards,
  Thomas Güttler

Melvyn Sopacua

unread,
Mar 27, 2017, 10:11:06 AM3/27/17
to django-d...@googlegroups.com

On Friday 24 March 2017 04:31:32 guettli wrote:

> I know this is a crazy idea, and it will get the "won't fix" tag very

> soon.

>

> Nevertheless I want to speak it out.

>

> My use case: Get a queryset of users who have a given permission.

 

I'm still thinking about this use case.

Cause it's not for normal use - you don't normally use permissions as data, you use permissions to deny / allow access to data.

So, you're already in the "specialized" corner.

 

> At the moment this query is complex and results in performance issues,

 

For a small install this works fine. When performance becomes an issue because there's a large number of users and groups, my first instinct is to regroup users so that no permission exceptions exist anymore at the user level.

I'd add all superusers to the "wheel" group ("root", "superuser", whatever floats your boat).

Now the query to get this info no longer requires the user model for conditions and you can simply query the group manager.

It is what groups are for and it'll be always faster get all the matching groups as one set and combine it with all the users that have the permission assigned directly.

The "one query" requirement conflicts with your performance requirement.

 

--

Melvyn Sopacua

guettli

unread,
Mar 28, 2017, 6:53:00 AM3/28/17
to Django developers (Contributions to Django itself)


Am Montag, 27. März 2017 16:11:06 UTC+2 schrieb Melvyn Sopacua:

On Friday 24 March 2017 04:31:32 guettli wrote:

> I know this is a crazy idea, and it will get the "won't fix" tag very

> soon.

>

> Nevertheless I want to speak it out.

>

> My use case: Get a queryset of users who have a given permission.

 

I'm still thinking about this use case.

Cause it's not for normal use - you don't normally use permissions as data, you use permissions to deny / allow access to data.

So, you're already in the "specialized" corner.

 



I can use more words, to make this use case more colorful.

Imagine a approval workflow: Only some users are allowed to do the approval.

An invoice instance needs to be forwarded to someone with the matching permissions.

This way I want to provide a html form where the current user can choose the user with the matching
permissions.




 

> At the moment this query is complex and results in performance issues,

 

For a small install this works fine. When performance becomes an issue because there's a large number of users and groups, my first instinct is to regroup users so that no permission exceptions exist anymore at the user level.

I'd add all superusers to the "wheel" group ("root", "superuser", whatever floats your boat).

Now the query to get this info no longer requires the user model for conditions and you can simply query the group manager.

It is what groups are for and it'll be always faster get all the matching groups as one set and combine it with all the users that have the permission assigned directly.

The "one query" requirement conflicts with your performance requirement.



Unfortunately the current sql query has several outer joins and takes several seconds even with only very few data in the database.

We have a work-around this. The basic part is published here: https://code.djangoproject.com/ticket/27260#comment:10



 

 

--

Melvyn Sopacua

Anssi Kääriäinen

unread,
Mar 28, 2017, 7:39:42 AM3/28/17
to Django developers (Contributions to Django itself)
If you want to query for each user with given permission, then the query seems to be this (unfortunately not tested as I don't have a nice database to test this against):

users = User.objects.filter(
    Q(is_superuser=True) |
    Q(pk__in=Group.objects.filter(permissions=permission).values('user__pk')) |
    Q(pk__in=Permission.objects.filter(id=permission.pk).values('user__pk')))

I can't see how removing the is_superuser attribute would make this query more efficient. And, it's likely the query you were using is slow because it doesn't use subqueries. It would be nice to make Django use subqueries automatically, but for queryset API reasons that's hard to do.

 - Anssi

Melvyn Sopacua

unread,
Mar 28, 2017, 8:31:33 AM3/28/17
to django-d...@googlegroups.com

On Tuesday 28 March 2017 03:52:59 guettli wrote:

> Am Montag, 27. März 2017 16:11:06 UTC+2 schrieb Melvyn Sopacua:

> > On Friday 24 March 2017 04:31:32 guettli wrote:

> > > I know this is a crazy idea, and it will get the "won't fix" tag

> > > very

> > >

> > > soon.

> > >

> > >

> > >

> > > Nevertheless I want to speak it out.

> > >

> > >

> > >

> > > My use case: Get a queryset of users who have a given permission.

> >

> > I'm still thinking about this use case.

> >

> > Cause it's not for normal use - you don't normally use permissions

> > as

> > data, you use permissions to deny / allow access to data.

> >

> > So, you're already in the "specialized" corner.

>

> I can use more words, to make this use case more colorful.

>

> Imagine a approval workflow: Only some users are allowed to do the

> approval.

>

> An invoice instance needs to be forwarded to someone with the matching

> permissions.

 

From an object perspective, you need to send the invoice to the group "Approvers". Again, best solved at the group level.

And it's questionable if superusers should be in there. They are the equivalent of the Posix 'root' user, which has the power to lock/unlock everything in and about the system. Data privilege and system privilege are always in fight and in this case it's questionable if superusers should automatically be Approvers and while they still could do it, the UI shouldn't present them.

 

As said, a good group structure solves a lot of your problems - in fact you wouldn't need a user selection to begin with, as the mail can simply be sent to all members of the Approvers group.

 

--

Melvyn Sopacua

guettli

unread,
Mar 29, 2017, 6:23:31 AM3/29/17
to Django developers (Contributions to Django itself)
Thank you for the inspiring conversation.


Am Dienstag, 28. März 2017 14:31:33 UTC+2 schrieb Melvyn Sopacua:

 

From an object perspective, you need to send the invoice to the group "Approvers". Again, best solved at the group level.

And it's questionable if superusers should be in there. They are the equivalent of the Posix 'root' user, which has the power to lock/unlock everything in and about the system. Data privilege and system privilege are always in fight and in this case it's questionable if superusers should automatically be Approvers and while they still could do it, the UI shouldn't present them.



My favorite search engine did not reveal much to the topic "Data privilege vs system privilege". Do you have any further readings about this topic?



 

 

As said, a good group structure solves a lot of your problems -

 



Yes, groups exist as container/list data type for users.

On the other hand just because groups exist, I think there are other ways to specify a list of users. Example: All people with a particular OCEAN Vector


> in fact you wouldn't need a user selection to begin with, as the mail can simply be sent to all members of the Approvers group.

A mail to everyone is like a mail to no one (not always, but often).

Again .... system vs data privs ... sounds interesting.

Regards,
  Thomas Güttler
Reply all
Reply to author
Forward
0 new messages