mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-15 16:42:34 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9560c4b05d | ||
|
|
81a5febfbc | ||
|
|
a09a59e7b3 | ||
|
|
a7d0e3ce70 | ||
|
|
6f762d7198 | ||
|
|
ddbcbb0d66 | ||
|
|
a1976e4f0b | ||
|
|
0eeb1e6f6d | ||
|
|
ae5487720c | ||
|
|
0d8386359e | ||
|
|
d338e53862 | ||
|
|
441c6a7f60 | ||
|
|
79a40f7ded | ||
|
|
b9c221c856 | ||
|
|
f6dada03bf | ||
|
|
d3d18b8f48 | ||
|
|
a2f23c2d81 | ||
|
|
f0df23a15a | ||
|
|
a6a4bb1c7d | ||
|
|
9ac3c6b120 |
@@ -8,6 +8,7 @@ from django.conf import settings
|
|||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
from common.mixins import CommonModelMixin
|
from common.mixins import CommonModelMixin
|
||||||
from common.tree import TreeNode
|
from common.tree import TreeNode
|
||||||
|
from common.utils import is_uuid
|
||||||
from assets.models import Asset, SystemUser
|
from assets.models import Asset, SystemUser
|
||||||
|
|
||||||
from ..utils import KubernetesTree
|
from ..utils import KubernetesTree
|
||||||
@@ -100,6 +101,9 @@ class ApplicationTreeNodeMixin:
|
|||||||
type_category_mapper = const.AppType.type_category_mapper()
|
type_category_mapper = const.AppType.type_category_mapper()
|
||||||
types = const.AppType.type_category_mapper().keys()
|
types = const.AppType.type_category_mapper().keys()
|
||||||
for tp in types:
|
for tp in types:
|
||||||
|
# TODO: Temporary exclude mongodb
|
||||||
|
if tp == const.AppType.mongodb:
|
||||||
|
continue
|
||||||
if not settings.XPACK_ENABLED and const.AppType.is_xpack(tp):
|
if not settings.XPACK_ENABLED and const.AppType.is_xpack(tp):
|
||||||
continue
|
continue
|
||||||
category = type_category_mapper.get(tp)
|
category = type_category_mapper.get(tp)
|
||||||
@@ -264,12 +268,12 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
|
|||||||
'parameters': parameters
|
'parameters': parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_remote_app_asset(self):
|
def get_remote_app_asset(self, raise_exception=True):
|
||||||
asset_id = self.attrs.get('asset')
|
asset_id = self.attrs.get('asset')
|
||||||
if not asset_id:
|
if is_uuid(asset_id):
|
||||||
|
return Asset.objects.filter(id=asset_id).first()
|
||||||
|
if raise_exception:
|
||||||
raise ValueError("Remote App not has asset attr")
|
raise ValueError("Remote App not has asset attr")
|
||||||
asset = Asset.objects.filter(id=asset_id).first()
|
|
||||||
return asset
|
|
||||||
|
|
||||||
|
|
||||||
class ApplicationUser(SystemUser):
|
class ApplicationUser(SystemUser):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from perms.filters import AssetPermissionFilter
|
|||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from orgs.mixins import generics
|
from orgs.mixins import generics
|
||||||
from assets.api import FilterAssetByNodeMixin
|
from assets.api import FilterAssetByNodeMixin
|
||||||
from ..models import Asset, Node, Platform
|
from ..models import Asset, Node, Platform, Gateway
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..tasks import (
|
from ..tasks import (
|
||||||
update_assets_hardware_info_manual, test_assets_connectivity_manual,
|
update_assets_hardware_info_manual, test_assets_connectivity_manual,
|
||||||
@@ -181,7 +181,7 @@ class AssetsTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
|
|||||||
def check_permissions(self, request):
|
def check_permissions(self, request):
|
||||||
action = request.data.get('action')
|
action = request.data.get('action')
|
||||||
action_perm_require = {
|
action_perm_require = {
|
||||||
'refresh': 'assets.refresh_assethardwareinfo1',
|
'refresh': 'assets.refresh_assethardwareinfo',
|
||||||
}
|
}
|
||||||
perm_required = action_perm_require.get(action)
|
perm_required = action_perm_require.get(action)
|
||||||
has = self.request.user.has_perm(perm_required)
|
has = self.request.user.has_perm(perm_required)
|
||||||
@@ -199,7 +199,7 @@ class AssetGatewayListApi(generics.ListAPIView):
|
|||||||
asset_id = self.kwargs.get('pk')
|
asset_id = self.kwargs.get('pk')
|
||||||
asset = get_object_or_404(Asset, pk=asset_id)
|
asset = get_object_or_404(Asset, pk=asset_id)
|
||||||
if not asset.domain:
|
if not asset.domain:
|
||||||
return []
|
return Gateway.objects.none()
|
||||||
queryset = asset.domain.gateways.filter(protocol='ssh')
|
queryset = asset.domain.gateways.filter(protocol='ssh')
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,14 @@ class AuthMixin:
|
|||||||
self.password = password
|
self.password = password
|
||||||
|
|
||||||
def load_app_more_auth(self, app_id=None, username=None, user_id=None):
|
def load_app_more_auth(self, app_id=None, username=None, user_id=None):
|
||||||
|
from applications.models import Application
|
||||||
|
app = get_object_or_none(Application, pk=app_id)
|
||||||
|
if app and app.category_remote_app:
|
||||||
|
# Remote app
|
||||||
|
self._load_remoteapp_more_auth(app, username, user_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Other app
|
||||||
self._clean_auth_info_if_manual_login_mode()
|
self._clean_auth_info_if_manual_login_mode()
|
||||||
# 加载临时认证信息
|
# 加载临时认证信息
|
||||||
if self.login_mode == self.LOGIN_MANUAL:
|
if self.login_mode == self.LOGIN_MANUAL:
|
||||||
@@ -148,6 +156,11 @@ class AuthMixin:
|
|||||||
_username = username
|
_username = username
|
||||||
self.username = _username
|
self.username = _username
|
||||||
|
|
||||||
|
def _load_remoteapp_more_auth(self, app, username, user_id):
|
||||||
|
asset = app.get_remote_app_asset(raise_exception=False)
|
||||||
|
if asset:
|
||||||
|
self.load_asset_more_auth(asset_id=asset.id, username=username, user_id=user_id)
|
||||||
|
|
||||||
def load_asset_special_auth(self, asset, username=''):
|
def load_asset_special_auth(self, asset, username=''):
|
||||||
"""
|
"""
|
||||||
AuthBook 的数据状态
|
AuthBook 的数据状态
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from .. import serializers
|
|||||||
|
|
||||||
|
|
||||||
class AccessKeyViewSet(ModelViewSet):
|
class AccessKeyViewSet(ModelViewSet):
|
||||||
permission_classes = (IsValidUser,)
|
|
||||||
serializer_class = serializers.AccessKeySerializer
|
serializer_class = serializers.AccessKeySerializer
|
||||||
search_fields = ['^id', '^secret']
|
search_fields = ['^id', '^secret']
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:449810c3661c09f6448b9c67e7a193f303a3bef7ccc3d0f1efe6e099804e782a
|
oid sha256:b1c6c0f9212f9d154a432d93785677ebc206eed4fd4338d3fe11b4b528d65c11
|
||||||
size 104323
|
size 104323
|
||||||
|
|||||||
@@ -2050,7 +2050,7 @@ msgstr "请修改密码"
|
|||||||
|
|
||||||
#: authentication/models.py:33 terminal/serializers/storage.py:30
|
#: authentication/models.py:33 terminal/serializers/storage.py:30
|
||||||
msgid "Access key"
|
msgid "Access key"
|
||||||
msgstr "Api key"
|
msgstr "API key"
|
||||||
|
|
||||||
#: authentication/models.py:40
|
#: authentication/models.py:40
|
||||||
msgid "Private Token"
|
msgid "Private Token"
|
||||||
|
|||||||
@@ -1,33 +1,54 @@
|
|||||||
|
from functools import wraps
|
||||||
from django.db.models.signals import post_save, pre_delete, pre_save, post_delete
|
from django.db.models.signals import post_save, pre_delete, pre_save, post_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from orgs.models import Organization
|
from orgs.models import Organization
|
||||||
from assets.models import Node
|
from assets.models import Node
|
||||||
from perms.models import (AssetPermission, ApplicationPermission)
|
from perms.models import AssetPermission, ApplicationPermission
|
||||||
from users.models import UserGroup, User
|
from users.models import UserGroup, User
|
||||||
|
from users.signals import pre_user_leave_org
|
||||||
from applications.models import Application
|
from applications.models import Application
|
||||||
from terminal.models import Session
|
from terminal.models import Session
|
||||||
|
from rbac.models import OrgRoleBinding
|
||||||
from assets.models import Asset, SystemUser, Domain, Gateway
|
from assets.models import Asset, SystemUser, Domain, Gateway
|
||||||
from orgs.caches import OrgResourceStatisticsCache
|
from orgs.caches import OrgResourceStatisticsCache
|
||||||
|
from common.utils import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def refresh_user_amount_on_user_create_or_delete(user_id):
|
def refresh_cache(name, org):
|
||||||
orgs = Organization.objects.filter(m2m_org_members__user_id=user_id).distinct()
|
names = None
|
||||||
|
if isinstance(name, (str,)):
|
||||||
|
names = [name, ]
|
||||||
|
if isinstance(names, (list, tuple)):
|
||||||
|
for name in names:
|
||||||
|
OrgResourceStatisticsCache(org).expire(name)
|
||||||
|
OrgResourceStatisticsCache(Organization.root()).expire(name)
|
||||||
|
else:
|
||||||
|
logger.warning('refresh cache fail: {}'.format(name))
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_user_amount_cache(user):
|
||||||
|
orgs = user.orgs.distinct()
|
||||||
for org in orgs:
|
for org in orgs:
|
||||||
org_cache = OrgResourceStatisticsCache(org)
|
refresh_cache('users_amount', org)
|
||||||
org_cache.expire('users_amount')
|
|
||||||
OrgResourceStatisticsCache(Organization.root()).expire('users_amount')
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=OrgRoleBinding)
|
||||||
def on_user_create_refresh_cache(sender, instance, created, **kwargs):
|
def on_user_create_or_invite_refresh_cache(sender, instance, created, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
refresh_user_amount_on_user_create_or_delete(instance.id)
|
refresh_cache('users_amount', instance.org)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(pre_user_leave_org)
|
||||||
|
def on_user_remove_refresh_cache(sender, org=None, **kwargs):
|
||||||
|
refresh_cache('users_amount', org)
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_delete, sender=User)
|
@receiver(pre_delete, sender=User)
|
||||||
def on_user_delete_refresh_cache(sender, instance, **kwargs):
|
def on_user_delete_refresh_cache(sender, instance, **kwargs):
|
||||||
refresh_user_amount_on_user_create_or_delete(instance.id)
|
refresh_user_amount_cache(instance)
|
||||||
|
|
||||||
|
|
||||||
# @receiver(m2m_changed, sender=OrganizationMember)
|
# @receiver(m2m_changed, sender=OrganizationMember)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class RBACBackend(JMSBaseAuthBackend):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def has_perm(self, user_obj, perm, obj=None):
|
def has_perm(self, user_obj, perm, obj=None):
|
||||||
if not user_obj.is_active:
|
if not user_obj.is_active or not perm:
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
if perm == '*':
|
if perm == '*':
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ exclude_permissions = (
|
|||||||
('common', 'setting', '*', '*'),
|
('common', 'setting', '*', '*'),
|
||||||
|
|
||||||
('authentication', 'privatetoken', '*', '*'),
|
('authentication', 'privatetoken', '*', '*'),
|
||||||
('authentication', 'accesskey', 'change,delete', 'accesskey'),
|
|
||||||
('authentication', 'connectiontoken', 'change,delete', 'connectiontoken'),
|
('authentication', 'connectiontoken', 'change,delete', 'connectiontoken'),
|
||||||
('authentication', 'ssotoken', '*', '*'),
|
('authentication', 'ssotoken', '*', '*'),
|
||||||
('authentication', 'superconnectiontoken', 'change,delete', 'superconnectiontoken'),
|
('authentication', 'superconnectiontoken', 'change,delete', 'superconnectiontoken'),
|
||||||
@@ -49,6 +48,8 @@ exclude_permissions = (
|
|||||||
('rbac', 'contenttype', '*', '*'),
|
('rbac', 'contenttype', '*', '*'),
|
||||||
('rbac', 'permission', 'add,delete,change', 'permission'),
|
('rbac', 'permission', 'add,delete,change', 'permission'),
|
||||||
('rbac', 'rolebinding', '*', '*'),
|
('rbac', 'rolebinding', '*', '*'),
|
||||||
|
('rbac', 'systemrolebinding', 'change', 'systemrolebinding'),
|
||||||
|
('rbac', 'orgrolebinding', 'change', 'orgrolebinding'),
|
||||||
('rbac', 'role', '*', '*'),
|
('rbac', 'role', '*', '*'),
|
||||||
('ops', 'adhoc', 'delete,change', '*'),
|
('ops', 'adhoc', 'delete,change', '*'),
|
||||||
('ops', 'adhocexecution', 'add,delete,change', '*'),
|
('ops', 'adhocexecution', 'add,delete,change', '*'),
|
||||||
@@ -99,6 +100,7 @@ only_system_permissions = (
|
|||||||
('orgs', 'organization', '*', '*'),
|
('orgs', 'organization', '*', '*'),
|
||||||
('xpack', 'license', '*', '*'),
|
('xpack', 'license', '*', '*'),
|
||||||
('settings', 'setting', '*', '*'),
|
('settings', 'setting', '*', '*'),
|
||||||
|
('tickets', '*', '*', '*'),
|
||||||
('ops', 'task', 'view', 'taskmonitor'),
|
('ops', 'task', 'view', 'taskmonitor'),
|
||||||
('terminal', 'terminal', '*', '*'),
|
('terminal', 'terminal', '*', '*'),
|
||||||
('terminal', 'commandstorage', '*', '*'),
|
('terminal', 'commandstorage', '*', '*'),
|
||||||
@@ -106,6 +108,7 @@ only_system_permissions = (
|
|||||||
('terminal', 'status', '*', '*'),
|
('terminal', 'status', '*', '*'),
|
||||||
('terminal', 'task', '*', '*'),
|
('terminal', 'task', '*', '*'),
|
||||||
('authentication', '*', '*', '*'),
|
('authentication', '*', '*', '*'),
|
||||||
|
('tickets', '*', '*', '*'),
|
||||||
)
|
)
|
||||||
|
|
||||||
only_org_permissions = (
|
only_org_permissions = (
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class RBACPermission(permissions.DjangoModelPermissions):
|
|||||||
try:
|
try:
|
||||||
queryset = self._queryset(view)
|
queryset = self._queryset(view)
|
||||||
model_cls = queryset.model
|
model_cls = queryset.model
|
||||||
except AssertionError:
|
except:
|
||||||
model_cls = None
|
model_cls = None
|
||||||
return model_cls
|
return model_cls
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ special_pid_mapper = {
|
|||||||
'terminal.status': 'terminal_node',
|
'terminal.status': 'terminal_node',
|
||||||
'terminal.task': 'terminal_node',
|
'terminal.task': 'terminal_node',
|
||||||
'audits.ftplog': 'terminal',
|
'audits.ftplog': 'terminal',
|
||||||
'rbac.menupermission': 'view_other',
|
|
||||||
'perms.view_myassets': 'my_assets',
|
'perms.view_myassets': 'my_assets',
|
||||||
'perms.view_myapps': 'my_apps',
|
'perms.view_myapps': 'my_apps',
|
||||||
'ops.add_commandexecution': 'view_workspace',
|
'ops.add_commandexecution': 'view_workspace',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from common.exceptions import JMSException
|
|||||||
from common.utils import lazyproperty
|
from common.utils import lazyproperty
|
||||||
from rbac.permissions import RBACPermission
|
from rbac.permissions import RBACPermission
|
||||||
from tickets import serializers
|
from tickets import serializers
|
||||||
from tickets.models import Ticket
|
from tickets.models import Ticket, Comment
|
||||||
from tickets.permissions.comment import IsAssignee, IsApplicant, IsSwagger
|
from tickets.permissions.comment import IsAssignee, IsApplicant, IsSwagger
|
||||||
|
|
||||||
|
|
||||||
@@ -36,5 +36,7 @@ class CommentViewSet(mixins.CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
if getattr(self, 'swagger_fake_view', False):
|
||||||
|
return Comment.objects.none()
|
||||||
queryset = self.ticket.comments.all()
|
queryset = self.ticket.comments.all()
|
||||||
return queryset
|
return queryset
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, SuggestionMixin, BulkModelV
|
|||||||
self.check_object_permissions(self.request, user)
|
self.check_object_permissions(self.request, user)
|
||||||
return super().perform_bulk_update(serializer)
|
return super().perform_bulk_update(serializer)
|
||||||
|
|
||||||
|
def allow_bulk_destroy(self, qs, filtered):
|
||||||
|
return filtered.count() < qs.count()
|
||||||
|
|
||||||
def perform_bulk_destroy(self, objects):
|
def perform_bulk_destroy(self, objects):
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
self.check_object_permissions(self.request, obj)
|
self.check_object_permissions(self.request, obj)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from orgs.models import Organization
|
|||||||
from common.utils import date_expired_default, get_logger, lazyproperty, random_string
|
from common.utils import date_expired_default, get_logger, lazyproperty, random_string
|
||||||
from common import fields
|
from common import fields
|
||||||
from django.db.models import TextChoices
|
from django.db.models import TextChoices
|
||||||
|
from rbac.const import Scope
|
||||||
from ..signals import post_user_change_password, post_user_leave_org, pre_user_leave_org
|
from ..signals import post_user_change_password, post_user_leave_org, pre_user_leave_org
|
||||||
|
|
||||||
__all__ = ['User', 'UserPasswordHistory']
|
__all__ = ['User', 'UserPasswordHistory']
|
||||||
@@ -204,6 +205,13 @@ class RoleManager(models.Manager):
|
|||||||
return
|
return
|
||||||
return self.role_bindings.delete()
|
return self.role_bindings.delete()
|
||||||
|
|
||||||
|
def refresh_user_amount_cache(self):
|
||||||
|
from orgs.signal_handlers.cache import refresh_user_amount_cache, refresh_cache
|
||||||
|
if current_org.is_root():
|
||||||
|
refresh_cache('users_amount', current_org)
|
||||||
|
elif self.scope == Scope.org:
|
||||||
|
refresh_user_amount_cache(self.user)
|
||||||
|
|
||||||
def add(self, *roles):
|
def add(self, *roles):
|
||||||
from rbac.models import RoleBinding
|
from rbac.models import RoleBinding
|
||||||
items = []
|
items = []
|
||||||
@@ -224,6 +232,9 @@ class RoleManager(models.Manager):
|
|||||||
RoleBinding.objects.bulk_create(items, ignore_conflicts=True)
|
RoleBinding.objects.bulk_create(items, ignore_conflicts=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error('Create role binding error: {}'.format(e))
|
logger.error('Create role binding error: {}'.format(e))
|
||||||
|
finally:
|
||||||
|
self.user.expire_users_rbac_perms_cache()
|
||||||
|
self.refresh_user_amount_cache()
|
||||||
|
|
||||||
def set(self, roles):
|
def set(self, roles):
|
||||||
self.clear()
|
self.clear()
|
||||||
@@ -237,16 +248,16 @@ class RoleManager(models.Manager):
|
|||||||
|
|
||||||
class OrgRoleManager(RoleManager):
|
class OrgRoleManager(RoleManager):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
from rbac.const import Scope
|
from rbac.const import Scope
|
||||||
self.scope = Scope.org
|
self.scope = Scope.org
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SystemRoleManager(RoleManager):
|
class SystemRoleManager(RoleManager):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
from rbac.const import Scope
|
from rbac.const import Scope
|
||||||
self.scope = Scope.system
|
self.scope = Scope.system
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RoleMixin:
|
class RoleMixin:
|
||||||
@@ -257,6 +268,8 @@ class RoleMixin:
|
|||||||
_org_roles = None
|
_org_roles = None
|
||||||
_system_roles = None
|
_system_roles = None
|
||||||
PERM_CACHE_KEY = 'USER_PERMS_{}_{}'
|
PERM_CACHE_KEY = 'USER_PERMS_{}_{}'
|
||||||
|
_is_superuser = None
|
||||||
|
_update_superuser = False
|
||||||
|
|
||||||
@lazyproperty
|
@lazyproperty
|
||||||
def roles(self):
|
def roles(self):
|
||||||
@@ -288,17 +301,25 @@ class RoleMixin:
|
|||||||
key = cls.PERM_CACHE_KEY.format('*', '*')
|
key = cls.PERM_CACHE_KEY.format('*', '*')
|
||||||
cache.delete_pattern(key)
|
cache.delete_pattern(key)
|
||||||
|
|
||||||
@lazyproperty
|
@property
|
||||||
def is_superuser(self):
|
def is_superuser(self):
|
||||||
"""
|
"""
|
||||||
由于这里用了 cache ,所以不能改成 self.system_roles.filter().exists() 会查询的
|
由于这里用了 cache ,所以不能改成 self.system_roles.filter().exists() 会查询的
|
||||||
"""
|
"""
|
||||||
|
if self._is_superuser is not None:
|
||||||
|
return self._is_superuser
|
||||||
from rbac.builtin import BuiltinRole
|
from rbac.builtin import BuiltinRole
|
||||||
# return self.system_roles.all().filter(id=BuiltinRole.system_admin.id).exists()
|
# return self.system_roles.all().filter(id=BuiltinRole.system_admin.id).exists()
|
||||||
ids = [str(r.id) for r in self.system_roles.all()]
|
ids = [str(r.id) for r in self.system_roles.all()]
|
||||||
yes = BuiltinRole.system_admin.id in ids
|
yes = BuiltinRole.system_admin.id in ids
|
||||||
|
self._is_superuser = yes
|
||||||
return yes
|
return yes
|
||||||
|
|
||||||
|
@is_superuser.setter
|
||||||
|
def is_superuser(self, value):
|
||||||
|
self._is_superuser = value
|
||||||
|
self._update_superuser = True
|
||||||
|
|
||||||
@lazyproperty
|
@lazyproperty
|
||||||
def is_org_admin(self):
|
def is_org_admin(self):
|
||||||
from rbac.builtin import BuiltinRole
|
from rbac.builtin import BuiltinRole
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ class RolesSerializerMixin(serializers.Serializer):
|
|||||||
return fields
|
return fields
|
||||||
|
|
||||||
action = view.action or 'list'
|
action = view.action or 'list'
|
||||||
|
if action in ('partial_bulk_update', 'bulk_update', 'partial_update', 'update'):
|
||||||
|
action = 'create'
|
||||||
model_cls_field_mapper = {
|
model_cls_field_mapper = {
|
||||||
SystemRoleBinding: ['system_roles', 'system_roles_display'],
|
SystemRoleBinding: ['system_roles', 'system_roles_display'],
|
||||||
OrgRoleBinding: ['org_roles', 'system_roles_display']
|
OrgRoleBinding: ['org_roles', 'system_roles_display']
|
||||||
@@ -130,6 +132,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer
|
|||||||
'date_joined', 'last_login', 'created_by', 'is_first_login',
|
'date_joined', 'last_login', 'created_by', 'is_first_login',
|
||||||
'wecom_id', 'dingtalk_id', 'feishu_id'
|
'wecom_id', 'dingtalk_id', 'feishu_id'
|
||||||
]
|
]
|
||||||
|
disallow_self_update_fields = ['is_active']
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'password': {'write_only': True, 'required': False, 'allow_null': True, 'allow_blank': True},
|
'password': {'write_only': True, 'required': False, 'allow_null': True, 'allow_blank': True},
|
||||||
'public_key': {'write_only': True},
|
'public_key': {'write_only': True},
|
||||||
@@ -178,7 +181,23 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer
|
|||||||
attrs.pop(field, None)
|
attrs.pop(field, None)
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def check_disallow_self_update_fields(self, attrs):
|
||||||
|
request = self.context.get('request')
|
||||||
|
if not request or not request.user.is_authenticated:
|
||||||
|
return attrs
|
||||||
|
if not self.instance:
|
||||||
|
return attrs
|
||||||
|
if request.user.id != self.instance.id:
|
||||||
|
return attrs
|
||||||
|
disallow_fields = set(list(attrs.keys())) & set(self.Meta.disallow_self_update_fields)
|
||||||
|
if not disallow_fields:
|
||||||
|
return attrs
|
||||||
|
# 用户自己不能更新自己的一些字段
|
||||||
|
error = 'User Cannot self-update fields: {}'.format(disallow_fields)
|
||||||
|
raise serializers.ValidationError(error)
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
|
attrs = self.check_disallow_self_update_fields(attrs)
|
||||||
attrs = self.change_password_to_raw(attrs)
|
attrs = self.change_password_to_raw(attrs)
|
||||||
attrs = self.clean_auth_fields(attrs)
|
attrs = self.clean_auth_fields(attrs)
|
||||||
attrs.pop('password_strategy', None)
|
attrs.pop('password_strategy', None)
|
||||||
@@ -203,17 +222,6 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer
|
|||||||
field.set(value)
|
field.set(value)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def validate_is_active(self, is_active):
|
|
||||||
request = self.context.get('request')
|
|
||||||
if not request or not request.user.is_authenticated:
|
|
||||||
return is_active
|
|
||||||
|
|
||||||
user = request.user
|
|
||||||
if user.id == self.instance.id and not is_active:
|
|
||||||
# 用户自己不能禁用启用自己
|
|
||||||
raise serializers.ValidationError("Cannot inactive self")
|
|
||||||
return is_active
|
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
save_handler = partial(super().update, instance)
|
save_handler = partial(super().update, instance)
|
||||||
instance = self.save_and_set_custom_m2m_fields(validated_data, save_handler, created=False)
|
instance = self.save_and_set_custom_m2m_fields(validated_data, save_handler, created=False)
|
||||||
|
|||||||
@@ -60,9 +60,30 @@ def save_passwd_change(sender, instance: User, **kwargs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_role_superuser_if_need(user):
|
||||||
|
if not user._update_superuser:
|
||||||
|
return
|
||||||
|
value = user._is_superuser
|
||||||
|
from rbac.models import SystemRoleBinding
|
||||||
|
from rbac.builtin import BuiltinRole
|
||||||
|
role = BuiltinRole.system_admin.get_role()
|
||||||
|
|
||||||
|
kwargs = {'user_id': user.id, 'role_id': role.id, 'scope': 'system'}
|
||||||
|
exists = SystemRoleBinding.objects.filter(**kwargs).exists()
|
||||||
|
# 需要添加并且不存在
|
||||||
|
if value and not exists:
|
||||||
|
SystemRoleBinding.objects.create(**kwargs)
|
||||||
|
# 需要删除并且存在
|
||||||
|
elif not value and exists:
|
||||||
|
SystemRoleBinding.objects.filter(**kwargs).delete()
|
||||||
|
else:
|
||||||
|
print("No need operate")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
@on_transaction_commit
|
@on_transaction_commit
|
||||||
def on_user_create_set_default_system_role(sender, instance, created, **kwargs):
|
def on_user_create_set_default_system_role(sender, instance, created, **kwargs):
|
||||||
|
update_role_superuser_if_need(instance)
|
||||||
if not created:
|
if not created:
|
||||||
return
|
return
|
||||||
has_system_role = instance.system_roles.all().exists()
|
has_system_role = instance.system_roles.all().exists()
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def clean_db_content_types():
|
|||||||
('perms', 'applicationpermission', 'view_permuserapplication'),
|
('perms', 'applicationpermission', 'view_permuserapplication'),
|
||||||
('perms', 'assetpermission', 'view_permuserasset'),
|
('perms', 'assetpermission', 'view_permuserasset'),
|
||||||
('perms', 'assetpermission', 'view_permusergroupasset'),
|
('perms', 'assetpermission', 'view_permusergroupasset'),
|
||||||
|
('rbac', 'menupermission', 'view_dashboard'),
|
||||||
('applications', 'databaseapp', 'add_databaseapp'),
|
('applications', 'databaseapp', 'add_databaseapp'),
|
||||||
('applications', 'databaseapp', 'change_databaseapp'),
|
('applications', 'databaseapp', 'change_databaseapp'),
|
||||||
('applications', 'databaseapp', 'delete_databaseapp'),
|
('applications', 'databaseapp', 'delete_databaseapp'),
|
||||||
|
|||||||
Reference in New Issue
Block a user