from copy import deepcopy from django.contrib import admin from django.contrib.admin import ModelAdmin, site from django.contrib.admin.utils import unquote from django.contrib.auth import get_user_model from django.contrib.sites.models import Site from django.core.exceptions import PermissionDenied from django.utils.translation import gettext from cms.admin.forms import PageUserChangeForm, PageUserGroupForm from cms.exceptions import NoPermissionsException from cms.models import Page, PagePermission, PageUser, PageUserGroup from cms.utils.compat.forms import UserAdmin from cms.utils.conf import get_cms_setting from cms.utils.permissions import ( get_model_permission_codename, get_subordinate_groups, get_subordinate_users, get_user_permission_level, ) user_model = get_user_model() admin_class: type[ModelAdmin] = UserAdmin for model, admin_instance in site._registry.items(): if model == user_model: admin_class = admin_instance.__class__ class GenericCmsPermissionAdmin: def get_subordinates(self, user, site): raise NotImplementedError def _has_change_permissions_permission(self, request): """ User is able to add/change objects only if he haves can change permission on some page. """ site = Site.objects.get_current(request) try: get_user_permission_level(request.user, site) except NoPermissionsException: return False return True def get_form(self, request, obj=None, **kwargs): form_class = super().get_form(request, obj, **kwargs) form_class._current_user = request.user return form_class def get_queryset(self, request): queryset = super().get_queryset(request) site = Site.objects.get_current(request) user_ids = self.get_subordinates(request.user, site).values_list('pk', flat=True) return queryset.filter(pk__in=user_ids) def has_add_permission(self, request): has_model_perm = super().has_add_permission(request) if not has_model_perm: return False return self._has_change_permissions_permission(request) def has_change_permission(self, request, obj=None): has_model_perm = super().has_change_permission(request, obj) if not has_model_perm: return False return self._has_change_permissions_permission(request) def has_delete_permission(self, request, obj=None): has_model_perm = super().has_delete_permission(request, obj) if not has_model_perm: return False return self._has_change_permissions_permission(request) def has_view_permission(self, request, obj=None): # For django 2.1 # Default is to return True if user got `change` perm, but we have to # get in consideration also cms permission system return self.has_change_permission(request, obj) class PageUserAdmin(GenericCmsPermissionAdmin, admin_class): form = PageUserChangeForm model = PageUser def get_subordinates(self, user, site): return get_subordinate_users(user, site).values_list('pk', flat=True) def get_readonly_fields(self, request, obj=None): fields = super().get_readonly_fields(request, obj) if not request.user.is_superuser: # Non superusers can't set superuser status on # their subordinates. fields = list(fields) + ['is_superuser'] return fields def has_change_permission(self, request, obj=None): if obj is not None and obj.is_superuser and not request.user.is_superuser: # Superusers are nobody's subordinate. get_queryset() already hides # them, this guards the views that resolve an object by other means. return False return super().has_change_permission(request, obj) def has_delete_permission(self, request, obj=None): if obj is not None and obj.is_superuser and not request.user.is_superuser: return False return super().has_delete_permission(request, obj) def user_change_password(self, request, id, form_url=''): # Django's password view only checks has_change_permission(), so a # subordinate check has to happen before anything else. user = self.get_object(request, unquote(id)) if user is not None and user.is_superuser and not request.user.is_superuser: raise PermissionDenied return super().user_change_password(request, id, form_url) def save_model(self, request, obj, form, change): if not change: # By default set the staff flag to True # when a PageUser is first created obj.is_staff = True # Set the created_by field to the current user obj.created_by = request.user super().save_model(request, obj, form, change) class PageUserGroupAdmin(GenericCmsPermissionAdmin, admin.ModelAdmin): form = PageUserGroupForm list_display = ('name', 'created_by') fieldsets = [ (None, {'fields': ('name',)}), ] def get_fieldsets(self, request, obj=None): """ Nobody can grant more than he haves, so check for user permissions to Page and User model and render fieldset depending on them. """ fieldsets = deepcopy(self.fieldsets) perm_models = ( (Page, gettext('Page permissions')), (PageUser, gettext('User & Group permissions')), (PagePermission, gettext('Page permissions management')), ) for i, perm_model in enumerate(perm_models): fields = [] model, title = perm_model name = model.__name__.lower() for key in ('add', 'change', 'delete'): perm_code = get_model_permission_codename(model, action=key) if request.user.has_perm(perm_code): fields.append(f'can_{key}_{name}') if fields: fieldsets.insert(2 + i, (title, {'fields': (fields,)})) return fieldsets def get_subordinates(self, user, site): return get_subordinate_groups(user, site).values_list('pk', flat=True) if get_cms_setting('PERMISSION'): admin.site.register(PageUser, PageUserAdmin) admin.site.register(PageUserGroup, PageUserGroupAdmin)