Skip to content

Instantly share code, notes, and snippets.

@jurrian
Last active September 4, 2023 12:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jurrian/9c41c936929d9d0ec869f33960d67149 to your computer and use it in GitHub Desktop.
Save jurrian/9c41c936929d9d0ec869f33960d67149 to your computer and use it in GitHub Desktop.
Mixin to apply over the Django `User` or `AbstractUser` model, this will change the superuser checkbox to a datetime field that will expire.
from datetime import timedelta
from django.db import models
from django.utils.timezone import now
class ExpiringSuperuserMixin:
_is_superuser = models.DateTimeField(
_('superuser until'),
blank=True,
null=True,
default=None,
help_text=_('Until when the user has special top-level permissions. Use with extra caution, '
'this includes most destructive permissions. Only specify a very limited timeframe.'),
)
def save(self, *args, **kwargs):
"""Clear superuser field if expired.
"""
try:
if self._is_superuser < now():
self._is_superuser = None
except TypeError:
pass
return super().save(*args, **kwargs)
@property
def is_superuser(self):
"""Evaluate if superuser datetime is not due or None.
If the datetime is due then reset the field to None.
"""
try:
is_within_window = self._is_superuser > now()
except TypeError:
return False
else:
if not is_within_window:
self._is_superuser = None
self.save(update_fields=['_is_superuser'])
return False
return True
@is_superuser.setter
def is_superuser(self, value):
"""Backwards compatibility: set a default time for superuser.
When using `manage.py createsuperuser`, this method will be called with `value=True`.
In that case we need to set a default time starting now.
"""
if isinstance(value, datetime):
self._is_superuser = value
elif value is True:
self._is_superuser = now() + timedelta(minutes=30)
else:
self._is_superuser = None
class Meta(AbstractUser.Meta):
permissions = [('can_assign_superuser', 'Can assign superuser')] # Creates permission on model creation
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
class UserAdmin(AuthUserAdmin):
fieldsets = (
(None, {"fields": ("username", "password")}),
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(
_("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"_is_superuser", # <--
"groups",
"user_permissions",
),
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
)
list_filter = ("is_staff", "_is_superuser", "is_active", "groups")
def get_readonly_fields(self, request, obj=None):
"""Only allow editing superuser status when has can_assign_superuser perm.
The has_perm() check also triggers is_superuser() to reset it if needed.
"""
readonly_fields = super().get_readonly_fields(request, obj)
if not request.user.has_perm('profiles.can_assign_superuser'):
readonly_fields += ('_is_superuser',)
return readonly_fields
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment