perf: 设置默认的角色,系统用户角色添加权限 (#7898)

* perf: 修改 role handler

* perf: 设置默认的角色,系统用户角色添加权限

* perf: authentication 还是放到系统中吧

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
pull/7901/head
fit2bot 2022-03-17 14:08:16 +08:00 committed by GitHub
parent 8fe84345e4
commit 34e75099a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 61 additions and 33 deletions

View File

@ -18,7 +18,7 @@ from .signals import post_auth_success, post_auth_failed
@receiver(user_logged_in)
def on_user_auth_login_success(sender, user, request, **kwargs):
# 失效 perms 缓存
user.expire_perms_cache()
user.expire_rbac_perms_cache()
# 开启了 MFA且没有校验过, 可以全局校验, middleware 中可以全局管理 oidc 等第三方认证的 MFA
if settings.SECURITY_MFA_AUTH_ENABLED_FOR_THIRD_PARTY \

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f529bbca004aeba7532d9faf50f6f8ab5532b19bf0afd650f8360f418c03c15c
size 104629
oid sha256:5775daad77f39025e7ddd39f04fdc0cb67c9537754caa3b0b520421df65e5041
size 104739

View File

@ -41,13 +41,3 @@ class PermissionViewSet(JMSModelViewSet):
queryset = Permission.get_permissions(self.scope)
queryset = queryset.prefetch_related('content_type')
return queryset
# class UserPermsApi(ListAPIView):
# serializer_class = UserPermsSerializer
# permission_classes = (IsValidUser,)
#
# def list(self, request, *args, **kwargs):
# perms = RoleBinding.get_user_perms(request.user)
# serializer = super().get_serializer(data={'perms': perms})
# return Res

View File

@ -2,6 +2,8 @@ from django.utils.translation import ugettext_noop
from .const import Scope, system_exclude_permissions, org_exclude_permissions
# Todo: 获取应该区分 系统用户,和组织用户的权限
# 工作台也区分组织后再考虑
user_perms = (
('rbac', 'menupermission', 'view', 'workspace'),
('rbac', 'menupermission', 'view', 'webterminal'),
@ -12,14 +14,13 @@ user_perms = (
('assets', 'systemuser', 'match', 'systemuser'),
('assets', 'node', 'match', 'node'),
('applications', 'application', 'match', 'application'),
('tickets', 'ticket', 'view', 'ticket'),
('ops', 'commandexecution', 'add', 'commandexecution'),
('authentication', 'connectiontoken', 'add', 'connectiontoken'),
('tickets', 'ticket', 'view', 'ticket'),
)
auditor_perms = user_perms + (
('rbac', 'menupermission', 'view', 'audit'),
('rbac', 'menupermission', 'view', 'dashboard'),
('audits', '*', '*', '*'),
('terminal', 'commandstorage', 'view', 'commandstorage'),
('terminal', 'sessionreplay', 'view,download', 'sessionreplay'),
@ -88,7 +89,7 @@ class PredefineRole:
class BuiltinRole:
system_admin = PredefineRole(
'1', ugettext_noop('SystemAdmin'), Scope.system, []
'1', ugettext_noop('SystemAdmin'), Scope.system, user_perms
)
system_auditor = PredefineRole(
'2', ugettext_noop('SystemAuditor'), Scope.system, auditor_perms

View File

@ -1,7 +1,8 @@
from django.dispatch import receiver
from django.db.models.signals import post_migrate
from django.db.models.signals import post_migrate, post_save
from django.apps import apps
from .models import SystemRole, OrgRole
from .builtin import BuiltinRole
@ -12,3 +13,15 @@ def after_migrate_update_builtin_role_permissions(sender, app_config, **kwargs):
if app_config.name == last_app.name:
print("After migration, update builtin role permissions")
BuiltinRole.sync_to_db()
@receiver(post_save, sender=SystemRole)
def on_system_role_update(sender, instance, created, **kwargs):
from users.models import User
User.expire_users_rbac_perms_cache()
@receiver(post_save, sender=OrgRole)
def on_org_role_update(sender, instance, created, **kwargs):
from users.models import User
User.expire_users_rbac_perms_cache()

View File

@ -1,6 +1,7 @@
#!/usr/bin/python
from collections import defaultdict
from typing import Callable
import os
from django.utils.translation import gettext_lazy as _, gettext, get_language
from django.conf import settings
@ -10,6 +11,8 @@ from django.db.models import F, Count
from common.tree import TreeNode
from .models import Permission, ContentType
DEBUG_DB = os.environ.get('DEBUG_DB', '0') == '1'
# 根节点
root_node_data = {
'id': '$ROOT$',
@ -315,7 +318,7 @@ class PermissionTreeUtil:
continue
# name 要特殊处理,解决 i18n 问题
name, icon = self._get_permission_name_icon(p, content_types_name_mapper)
if settings.DEBUG:
if DEBUG_DB:
name += '[{}]'.format(p.app_label_codename)
title = p.app_label_codename
@ -366,9 +369,9 @@ class PermissionTreeUtil:
}
node_data['title'] = node_data['id']
node = TreeNode(**node_data)
if settings.DEBUG:
if DEBUG_DB:
node.name += ('[' + node.id + ']')
if settings.DEBUG:
if DEBUG_DB:
node.name += ('-' + node.id)
node.name += f'({checked_count}/{total_count})'
return node

View File

@ -279,10 +279,15 @@ class RoleMixin:
cache.set(key, perms, 3600)
return perms
def expire_perms_cache(self):
def expire_rbac_perms_cache(self):
key = self.PERM_CACHE_KEY.format(self.id, '*')
cache.delete_pattern(key)
@classmethod
def expire_users_rbac_perms_cache(cls):
key = cls.PERM_CACHE_KEY.format('*', '*')
cache.delete_pattern(key)
@lazyproperty
def is_superuser(self):
"""
@ -377,6 +382,11 @@ class RoleMixin:
perms = RoleBinding.get_user_perms(self)
return perms
def set_default_system_role(self):
from rbac.builtin import BuiltinRole
role_user = BuiltinRole.org_user.get_role()
self.system_roles.add(role_user)
class TokenMixin:
CACHE_KEY_USER_RESET_PASSWORD_PREFIX = "_KEY_USER_RESET_PASSWORD_{}"

View File

@ -6,13 +6,11 @@ from rest_framework import serializers
from common.mixins import CommonBulkSerializerMixin
from common.validators import PhoneValidator
from rbac.models import Role
from rbac.builtin import BuiltinRole
from rbac.permissions import RBACPermission
from rbac.models import OrgRoleBinding, SystemRoleBinding
from rbac.models import OrgRoleBinding, SystemRoleBinding, Role
from ..models import User
from ..const import PasswordStrategy
from rbac.models import Role
__all__ = [
'UserSerializer', 'MiniUserSerializer',

View File

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
#
from django.dispatch import receiver
from django_auth_ldap.backend import populate_user
from django.conf import settings
@ -9,9 +8,9 @@ from django_cas_ng.signals import cas_user_authenticated
from django.db.models.signals import post_save
from authentication.backends.oidc.signals import openid_create_or_update_user
from authentication.backends.saml2.signals import saml2_create_or_update_user
from common.utils import get_logger
from common.decorator import on_transaction_commit
from .signals import post_user_create
from .models import User, UserPasswordHistory
@ -26,7 +25,8 @@ def user_authenticated_handle(user, created, source, attrs=None, **kwargs):
if created:
user.source = source
user.save()
elif not created and settings.AUTH_SAML2_ALWAYS_UPDATE_USER:
if not created and settings.AUTH_SAML2_ALWAYS_UPDATE_USER:
attr_whitelist = ('user', 'username', 'email', 'phone', 'comment')
logger.debug(
"Receive saml2 user updated signal: {}, "
@ -34,16 +34,18 @@ def user_authenticated_handle(user, created, source, attrs=None, **kwargs):
"(Update only properties in the whitelist. [{}])"
"".format(user, str(attrs), ','.join(attr_whitelist))
)
if attrs is not None:
for key, value in attrs.items():
if key in attr_whitelist and value:
setattr(user, key, value)
user.save()
if not attrs:
return
for key, value in attrs.items():
if key in attr_whitelist and value:
setattr(user, key, value)
user.save()
@receiver(post_save, sender=User)
def save_passwd_change(sender, instance: User, **kwargs):
passwords = UserPasswordHistory.objects.filter(user=instance) \
passwords = UserPasswordHistory.objects\
.filter(user=instance) \
.order_by('-date_created')\
.values_list('password', flat=True)
passwords = passwords[:int(settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT)]
@ -58,6 +60,17 @@ def save_passwd_change(sender, instance: User, **kwargs):
)
@receiver(post_save, sender=User)
@on_transaction_commit
def on_user_create_set_default_system_role(sender, instance, created, **kwargs):
if not created:
return
has_system_role = instance.system_roles.all().exists()
if not has_system_role:
logger.debug("Receive user create signal, set default role")
instance.set_default_system_role()
@receiver(post_user_create)
def on_user_create(sender, user=None, **kwargs):
logger.debug("Receive user `{}` create signal".format(user.name))