diff --git a/apps/users/api/user.py b/apps/users/api/user.py index d1eee8083..6c8b5c8c9 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -12,12 +12,14 @@ from common.api import SuggestionMixin from common.utils import get_logger from orgs.utils import current_org, tmp_to_root_org from rbac.models import Role, RoleBinding +from rbac.permissions import RBACPermission from users.utils import LoginBlockUtil, MFABlockUtils from .mixins import UserQuerysetMixin from .. import serializers from ..filters import UserFilter from ..models import User from ..notifications import ResetMFAMsg +from ..permissions import UserObjectPermission from ..serializers import ( UserSerializer, MiniUserSerializer, InviteSerializer @@ -34,6 +36,7 @@ __all__ = [ class UserViewSet(CommonApiMixin, UserQuerysetMixin, SuggestionMixin, BulkModelViewSet): filterset_class = UserFilter search_fields = ('username', 'email', 'name') + permission_classes = [RBACPermission, UserObjectPermission] serializer_classes = { 'default': UserSerializer, 'suggestion': MiniUserSerializer, diff --git a/apps/users/permissions.py b/apps/users/permissions.py index ee821f8d7..c7099bb3e 100644 --- a/apps/users/permissions.py +++ b/apps/users/permissions.py @@ -1,5 +1,6 @@ from rest_framework import permissions +from rbac.builtin import BuiltinRole from .utils import is_auth_password_time_valid @@ -7,4 +8,20 @@ class IsAuthPasswdTimeValid(permissions.IsAuthenticated): def has_permission(self, request, view): return super().has_permission(request, view) \ - and is_auth_password_time_valid(request.session) + and is_auth_password_time_valid(request.session) + + +class UserObjectPermission(permissions.BasePermission): + + def has_object_permission(self, request, view, obj): + if view.action not in ['update', 'partial_update', 'destroy']: + return True + + user = request.user + if user.is_superuser: + return True + + system_admin_id = BuiltinRole.system_admin.id + return system_admin_id not in [ + str(r.id) for r in obj.system_roles.all() + ]