mirror of https://github.com/jumpserver/jumpserver
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
parent
8fe84345e4
commit
34e75099a3
|
@ -18,7 +18,7 @@ from .signals import post_auth_success, post_auth_failed
|
||||||
@receiver(user_logged_in)
|
@receiver(user_logged_in)
|
||||||
def on_user_auth_login_success(sender, user, request, **kwargs):
|
def on_user_auth_login_success(sender, user, request, **kwargs):
|
||||||
# 失效 perms 缓存
|
# 失效 perms 缓存
|
||||||
user.expire_perms_cache()
|
user.expire_rbac_perms_cache()
|
||||||
|
|
||||||
# 开启了 MFA,且没有校验过, 可以全局校验, middleware 中可以全局管理 oidc 等第三方认证的 MFA
|
# 开启了 MFA,且没有校验过, 可以全局校验, middleware 中可以全局管理 oidc 等第三方认证的 MFA
|
||||||
if settings.SECURITY_MFA_AUTH_ENABLED_FOR_THIRD_PARTY \
|
if settings.SECURITY_MFA_AUTH_ENABLED_FOR_THIRD_PARTY \
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f529bbca004aeba7532d9faf50f6f8ab5532b19bf0afd650f8360f418c03c15c
|
oid sha256:5775daad77f39025e7ddd39f04fdc0cb67c9537754caa3b0b520421df65e5041
|
||||||
size 104629
|
size 104739
|
||||||
|
|
|
@ -41,13 +41,3 @@ class PermissionViewSet(JMSModelViewSet):
|
||||||
queryset = Permission.get_permissions(self.scope)
|
queryset = Permission.get_permissions(self.scope)
|
||||||
queryset = queryset.prefetch_related('content_type')
|
queryset = queryset.prefetch_related('content_type')
|
||||||
return queryset
|
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
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ from django.utils.translation import ugettext_noop
|
||||||
|
|
||||||
from .const import Scope, system_exclude_permissions, org_exclude_permissions
|
from .const import Scope, system_exclude_permissions, org_exclude_permissions
|
||||||
|
|
||||||
|
# Todo: 获取应该区分 系统用户,和组织用户的权限
|
||||||
|
# 工作台也区分组织后再考虑
|
||||||
user_perms = (
|
user_perms = (
|
||||||
('rbac', 'menupermission', 'view', 'workspace'),
|
('rbac', 'menupermission', 'view', 'workspace'),
|
||||||
('rbac', 'menupermission', 'view', 'webterminal'),
|
('rbac', 'menupermission', 'view', 'webterminal'),
|
||||||
|
@ -12,14 +14,13 @@ user_perms = (
|
||||||
('assets', 'systemuser', 'match', 'systemuser'),
|
('assets', 'systemuser', 'match', 'systemuser'),
|
||||||
('assets', 'node', 'match', 'node'),
|
('assets', 'node', 'match', 'node'),
|
||||||
('applications', 'application', 'match', 'application'),
|
('applications', 'application', 'match', 'application'),
|
||||||
('tickets', 'ticket', 'view', 'ticket'),
|
|
||||||
('ops', 'commandexecution', 'add', 'commandexecution'),
|
('ops', 'commandexecution', 'add', 'commandexecution'),
|
||||||
('authentication', 'connectiontoken', 'add', 'connectiontoken'),
|
('authentication', 'connectiontoken', 'add', 'connectiontoken'),
|
||||||
|
('tickets', 'ticket', 'view', 'ticket'),
|
||||||
)
|
)
|
||||||
|
|
||||||
auditor_perms = user_perms + (
|
auditor_perms = user_perms + (
|
||||||
('rbac', 'menupermission', 'view', 'audit'),
|
('rbac', 'menupermission', 'view', 'audit'),
|
||||||
('rbac', 'menupermission', 'view', 'dashboard'),
|
|
||||||
('audits', '*', '*', '*'),
|
('audits', '*', '*', '*'),
|
||||||
('terminal', 'commandstorage', 'view', 'commandstorage'),
|
('terminal', 'commandstorage', 'view', 'commandstorage'),
|
||||||
('terminal', 'sessionreplay', 'view,download', 'sessionreplay'),
|
('terminal', 'sessionreplay', 'view,download', 'sessionreplay'),
|
||||||
|
@ -88,7 +89,7 @@ class PredefineRole:
|
||||||
|
|
||||||
class BuiltinRole:
|
class BuiltinRole:
|
||||||
system_admin = PredefineRole(
|
system_admin = PredefineRole(
|
||||||
'1', ugettext_noop('SystemAdmin'), Scope.system, []
|
'1', ugettext_noop('SystemAdmin'), Scope.system, user_perms
|
||||||
)
|
)
|
||||||
system_auditor = PredefineRole(
|
system_auditor = PredefineRole(
|
||||||
'2', ugettext_noop('SystemAuditor'), Scope.system, auditor_perms
|
'2', ugettext_noop('SystemAuditor'), Scope.system, auditor_perms
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from django.dispatch import receiver
|
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 django.apps import apps
|
||||||
|
|
||||||
|
from .models import SystemRole, OrgRole
|
||||||
from .builtin import BuiltinRole
|
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:
|
if app_config.name == last_app.name:
|
||||||
print("After migration, update builtin role permissions")
|
print("After migration, update builtin role permissions")
|
||||||
BuiltinRole.sync_to_db()
|
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()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
import os
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _, gettext, get_language
|
from django.utils.translation import gettext_lazy as _, gettext, get_language
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -10,6 +11,8 @@ from django.db.models import F, Count
|
||||||
from common.tree import TreeNode
|
from common.tree import TreeNode
|
||||||
from .models import Permission, ContentType
|
from .models import Permission, ContentType
|
||||||
|
|
||||||
|
DEBUG_DB = os.environ.get('DEBUG_DB', '0') == '1'
|
||||||
|
|
||||||
# 根节点
|
# 根节点
|
||||||
root_node_data = {
|
root_node_data = {
|
||||||
'id': '$ROOT$',
|
'id': '$ROOT$',
|
||||||
|
@ -315,7 +318,7 @@ class PermissionTreeUtil:
|
||||||
continue
|
continue
|
||||||
# name 要特殊处理,解决 i18n 问题
|
# name 要特殊处理,解决 i18n 问题
|
||||||
name, icon = self._get_permission_name_icon(p, content_types_name_mapper)
|
name, icon = self._get_permission_name_icon(p, content_types_name_mapper)
|
||||||
if settings.DEBUG:
|
if DEBUG_DB:
|
||||||
name += '[{}]'.format(p.app_label_codename)
|
name += '[{}]'.format(p.app_label_codename)
|
||||||
|
|
||||||
title = p.app_label_codename
|
title = p.app_label_codename
|
||||||
|
@ -366,9 +369,9 @@ class PermissionTreeUtil:
|
||||||
}
|
}
|
||||||
node_data['title'] = node_data['id']
|
node_data['title'] = node_data['id']
|
||||||
node = TreeNode(**node_data)
|
node = TreeNode(**node_data)
|
||||||
if settings.DEBUG:
|
if DEBUG_DB:
|
||||||
node.name += ('[' + node.id + ']')
|
node.name += ('[' + node.id + ']')
|
||||||
if settings.DEBUG:
|
if DEBUG_DB:
|
||||||
node.name += ('-' + node.id)
|
node.name += ('-' + node.id)
|
||||||
node.name += f'({checked_count}/{total_count})'
|
node.name += f'({checked_count}/{total_count})'
|
||||||
return node
|
return node
|
||||||
|
|
|
@ -279,10 +279,15 @@ class RoleMixin:
|
||||||
cache.set(key, perms, 3600)
|
cache.set(key, perms, 3600)
|
||||||
return perms
|
return perms
|
||||||
|
|
||||||
def expire_perms_cache(self):
|
def expire_rbac_perms_cache(self):
|
||||||
key = self.PERM_CACHE_KEY.format(self.id, '*')
|
key = self.PERM_CACHE_KEY.format(self.id, '*')
|
||||||
cache.delete_pattern(key)
|
cache.delete_pattern(key)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def expire_users_rbac_perms_cache(cls):
|
||||||
|
key = cls.PERM_CACHE_KEY.format('*', '*')
|
||||||
|
cache.delete_pattern(key)
|
||||||
|
|
||||||
@lazyproperty
|
@lazyproperty
|
||||||
def is_superuser(self):
|
def is_superuser(self):
|
||||||
"""
|
"""
|
||||||
|
@ -377,6 +382,11 @@ class RoleMixin:
|
||||||
perms = RoleBinding.get_user_perms(self)
|
perms = RoleBinding.get_user_perms(self)
|
||||||
return perms
|
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:
|
class TokenMixin:
|
||||||
CACHE_KEY_USER_RESET_PASSWORD_PREFIX = "_KEY_USER_RESET_PASSWORD_{}"
|
CACHE_KEY_USER_RESET_PASSWORD_PREFIX = "_KEY_USER_RESET_PASSWORD_{}"
|
||||||
|
|
|
@ -6,13 +6,11 @@ from rest_framework import serializers
|
||||||
|
|
||||||
from common.mixins import CommonBulkSerializerMixin
|
from common.mixins import CommonBulkSerializerMixin
|
||||||
from common.validators import PhoneValidator
|
from common.validators import PhoneValidator
|
||||||
from rbac.models import Role
|
|
||||||
from rbac.builtin import BuiltinRole
|
from rbac.builtin import BuiltinRole
|
||||||
from rbac.permissions import RBACPermission
|
from rbac.permissions import RBACPermission
|
||||||
from rbac.models import OrgRoleBinding, SystemRoleBinding
|
from rbac.models import OrgRoleBinding, SystemRoleBinding, Role
|
||||||
from ..models import User
|
from ..models import User
|
||||||
from ..const import PasswordStrategy
|
from ..const import PasswordStrategy
|
||||||
from rbac.models import Role
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'UserSerializer', 'MiniUserSerializer',
|
'UserSerializer', 'MiniUserSerializer',
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django_auth_ldap.backend import populate_user
|
from django_auth_ldap.backend import populate_user
|
||||||
from django.conf import settings
|
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 django.db.models.signals import post_save
|
||||||
|
|
||||||
from authentication.backends.oidc.signals import openid_create_or_update_user
|
from authentication.backends.oidc.signals import openid_create_or_update_user
|
||||||
|
|
||||||
from authentication.backends.saml2.signals import saml2_create_or_update_user
|
from authentication.backends.saml2.signals import saml2_create_or_update_user
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
from common.decorator import on_transaction_commit
|
||||||
from .signals import post_user_create
|
from .signals import post_user_create
|
||||||
from .models import User, UserPasswordHistory
|
from .models import User, UserPasswordHistory
|
||||||
|
|
||||||
|
@ -26,7 +25,8 @@ def user_authenticated_handle(user, created, source, attrs=None, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
user.source = source
|
user.source = source
|
||||||
user.save()
|
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')
|
attr_whitelist = ('user', 'username', 'email', 'phone', 'comment')
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Receive saml2 user updated signal: {}, "
|
"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. [{}])"
|
"(Update only properties in the whitelist. [{}])"
|
||||||
"".format(user, str(attrs), ','.join(attr_whitelist))
|
"".format(user, str(attrs), ','.join(attr_whitelist))
|
||||||
)
|
)
|
||||||
if attrs is not None:
|
if not attrs:
|
||||||
for key, value in attrs.items():
|
return
|
||||||
if key in attr_whitelist and value:
|
for key, value in attrs.items():
|
||||||
setattr(user, key, value)
|
if key in attr_whitelist and value:
|
||||||
user.save()
|
setattr(user, key, value)
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
def save_passwd_change(sender, instance: User, **kwargs):
|
def save_passwd_change(sender, instance: User, **kwargs):
|
||||||
passwords = UserPasswordHistory.objects.filter(user=instance) \
|
passwords = UserPasswordHistory.objects\
|
||||||
|
.filter(user=instance) \
|
||||||
.order_by('-date_created')\
|
.order_by('-date_created')\
|
||||||
.values_list('password', flat=True)
|
.values_list('password', flat=True)
|
||||||
passwords = passwords[:int(settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT)]
|
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)
|
@receiver(post_user_create)
|
||||||
def on_user_create(sender, user=None, **kwargs):
|
def on_user_create(sender, user=None, **kwargs):
|
||||||
logger.debug("Receive user `{}` create signal".format(user.name))
|
logger.debug("Receive user `{}` create signal".format(user.name))
|
||||||
|
|
Loading…
Reference in New Issue