mirror of https://github.com/jumpserver/jumpserver
fix: fix rbac to dev (#7636)
* feat: 添加 RBAC 应用模块 * feat: 添加 RBAC Model、API * feat: 添加 RBAC Model、API 2 * feat: 添加 RBAC Model、API 3 * feat: 添加 RBAC Model、API 4 * feat: RBAC * feat: RBAC * feat: RBAC * feat: RBAC * feat: RBAC * feat: RBAC 整理权限位 * feat: RBAC 整理权限位2 * feat: RBAC 整理权限位2 * feat: RBAC 整理权限位 * feat: RBAC 添加默认角色 * feat: RBAC 添加迁移文件;迁移用户角色->用户角色绑定 * feat: RBAC 添加迁移文件;迁移用户角色->用户角色绑定 * feat: RBAC 修改用户模块API * feat: RBAC 添加组织模块迁移文件 & 修改组织模块API * feat: RBAC 添加组织模块迁移文件 & 修改组织模块API * feat: RBAC 修改用户角色属性的使用 * feat: RBAC No.1 * xxx * perf: 暂存 * perf: ... * perf(rbac): 添加 perms 到 profile serializer 中 * stash * perf: 使用init * perf: 修改migrations * perf: rbac * stash * stash * pref: 修改rbac * stash it * stash: 先去修复其他bug * perf: 修改 role 添加 users * pref: 修改 RBAC Model * feat: 添加权限的 tree api * stash: 暂存一下 * stash: 暂存一下 * perf: 修改 model verbose name * feat: 添加model各种 verbose name * perf: 生成 migrations * perf: 优化权限位 * perf: 添加迁移脚本 * feat: 添加组织角色迁移 * perf: 添加迁移脚本 * stash * perf: 添加migrateion * perf: 暂存一下 * perf: 修改rbac * perf: stash it * fix: 迁移冲突 * fix: 迁移冲突 * perf: 暂存一下 * perf: 修改 rbac 逻辑 * stash: 暂存一下 * perf: 修改内置角色 * perf: 解决 root 组织的问题 * perf: stash it * perf: 优化 rbac * perf: 优化 rolebinding 处理 * perf: 完成用户离开组织的问题 * perf: 暂存一下 * perf: 修改翻译 * perf: 去掉了 IsSuperUser * perf: IsAppUser 去掉完成 * perf: 修改 connection token 的权限 * perf: 去掉导入的问题 * perf: perms define 格式,修改 app 用户 的全新啊 * perf: 修改 permission * perf: 去掉一些 org admin * perf: 去掉部分 org admin * perf: 再去掉点 org admin role * perf: 再去掉部分 org admin * perf: user 角色搜索 * perf: 去掉很多 js * perf: 添加权限位 * perf: 修改权限 * perf: 去掉一个 todo * merge: with dev * fix: 修复冲突 Co-authored-by: Bai <bugatti_it@163.com> Co-authored-by: Michael Bai <baijiangjie@gmail.com> Co-authored-by: ibuler <ibuler@qq.com>pull/7638/head
parent
b088362ae3
commit
e259d2a9e9
|
@ -1,20 +1,14 @@
|
|||
from common.permissions import IsOrgAdmin, HasQueryParamsUserAndIsCurrentOrgMember
|
||||
from common.drf.api import JMSBulkModelViewSet
|
||||
from ..models import LoginACL
|
||||
from .. import serializers
|
||||
from ..filters import LoginAclFilter
|
||||
|
||||
__all__ = ['LoginACLViewSet', ]
|
||||
__all__ = ['LoginACLViewSet']
|
||||
|
||||
|
||||
class LoginACLViewSet(JMSBulkModelViewSet):
|
||||
queryset = LoginACL.objects.all()
|
||||
filterset_class = LoginAclFilter
|
||||
search_fields = ('name',)
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.LoginACLSerializer
|
||||
|
||||
def get_permissions(self):
|
||||
if self.action in ["retrieve", "list"]:
|
||||
self.permission_classes = (IsOrgAdmin, HasQueryParamsUserAndIsCurrentOrgMember)
|
||||
return super().get_permissions()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from common.permissions import IsOrgAdmin
|
||||
from .. import models, serializers
|
||||
|
||||
|
||||
|
@ -10,5 +9,4 @@ class LoginAssetACLViewSet(OrgBulkModelViewSet):
|
|||
model = models.LoginAssetACL
|
||||
filterset_fields = ('name', )
|
||||
search_fields = filterset_fields
|
||||
permission_classes = (IsOrgAdmin, )
|
||||
serializer_class = serializers.LoginAssetACLSerializer
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.generics import CreateAPIView
|
||||
|
||||
from common.permissions import IsAppUser
|
||||
from common.utils import reverse, lazyproperty
|
||||
from orgs.utils import tmp_to_org
|
||||
from tickets.api import GenericTicketStatusRetrieveCloseAPI
|
||||
|
@ -12,8 +11,8 @@ __all__ = ['LoginAssetCheckAPI', 'LoginAssetConfirmStatusAPI']
|
|||
|
||||
|
||||
class LoginAssetCheckAPI(CreateAPIView):
|
||||
permission_classes = (IsAppUser,)
|
||||
serializer_class = serializers.LoginAssetCheckSerializer
|
||||
model = LoginAssetACL
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
is_need_confirm, response_data = self.check_if_need_confirm()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class AclsConfig(AppConfig):
|
||||
name = 'acls'
|
||||
verbose_name = _('Acls')
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 3.1.13 on 2021-11-30 02:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('acls', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='loginacl',
|
||||
options={'ordering': ('priority', '-date_updated', 'name'), 'verbose_name': 'Login acl'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='loginassetacl',
|
||||
options={'ordering': ('priority', '-date_updated', 'name'), 'verbose_name': 'Login asset acl'},
|
||||
),
|
||||
]
|
|
@ -6,8 +6,9 @@ from django.db.models import F, Q
|
|||
|
||||
from common.drf.filters import BaseFilterSet
|
||||
from common.drf.api import JMSBulkModelViewSet
|
||||
from rbac.permissions import RBACPermission
|
||||
from ..models import Account
|
||||
from ..hands import IsOrgAdminOrAppUser, IsOrgAdmin, NeedMFAVerify
|
||||
from ..hands import NeedMFAVerify
|
||||
from .. import serializers
|
||||
|
||||
|
||||
|
@ -31,7 +32,8 @@ class AccountFilterSet(BaseFilterSet):
|
|||
username = self.get_query_param('username')
|
||||
if not username:
|
||||
return qs
|
||||
qs = qs.filter(Q(username=username) | Q(systemuser__username=username)).distinct()
|
||||
q = Q(username=username) | Q(systemuser__username=username)
|
||||
qs = qs.filter(q).distinct()
|
||||
return qs
|
||||
|
||||
|
||||
|
@ -41,7 +43,6 @@ class ApplicationAccountViewSet(JMSBulkModelViewSet):
|
|||
filterset_class = AccountFilterSet
|
||||
filterset_fields = ['username', 'app_display', 'type', 'category', 'app']
|
||||
serializer_class = serializers.AppAccountSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Account.get_queryset()
|
||||
|
@ -50,5 +51,9 @@ class ApplicationAccountViewSet(JMSBulkModelViewSet):
|
|||
|
||||
class ApplicationAccountSecretViewSet(ApplicationAccountViewSet):
|
||||
serializer_class = serializers.AppAccountSecretSerializer
|
||||
permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
||||
permission_classes = [RBACPermission, NeedMFAVerify]
|
||||
http_method_names = ['get', 'options']
|
||||
rbac_perms = {
|
||||
'retrieve': 'view_applicationaccountsecret',
|
||||
'list': 'view_applicationaccountsecret',
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@ from rest_framework.decorators import action
|
|||
from rest_framework.response import Response
|
||||
|
||||
from common.tree import TreeNodeSerializer
|
||||
from common.mixins.api import SuggestionMixin
|
||||
from ..hands import IsOrgAdminOrAppUser
|
||||
from common.mixins.views import SuggestionMixin
|
||||
from .. import serializers
|
||||
from ..models import Application
|
||||
|
||||
|
@ -22,12 +21,14 @@ class ApplicationViewSet(SuggestionMixin, OrgBulkModelViewSet):
|
|||
'type': ['exact', 'in'],
|
||||
}
|
||||
search_fields = ('name', 'type', 'category')
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_classes = {
|
||||
'default': serializers.AppSerializer,
|
||||
'get_tree': TreeNodeSerializer,
|
||||
'suggestion': serializers.MiniAppSerializer
|
||||
}
|
||||
rbac_perms = {
|
||||
'get_tree': 'applications.view_application'
|
||||
}
|
||||
|
||||
@action(methods=['GET'], detail=False, url_path='tree')
|
||||
def get_tree(self, request, *args, **kwargs):
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
#
|
||||
|
||||
from orgs.mixins import generics
|
||||
from ..hands import IsAppUser
|
||||
from .. import models
|
||||
from ..serializers import RemoteAppConnectionInfoSerializer
|
||||
from ..permissions import IsRemoteApp
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -15,5 +13,4 @@ __all__ = [
|
|||
|
||||
class RemoteAppConnectionInfoApi(generics.RetrieveAPIView):
|
||||
model = models.Application
|
||||
permission_classes = (IsAppUser, IsRemoteApp)
|
||||
serializer_class = RemoteAppConnectionInfoSerializer
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ApplicationsConfig(AppConfig):
|
||||
name = 'applications'
|
||||
verbose_name = _('Applications')
|
||||
|
|
|
@ -11,5 +11,5 @@
|
|||
"""
|
||||
|
||||
|
||||
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser, NeedMFAVerify
|
||||
from common.permissions import NeedMFAVerify
|
||||
from users.models import User, UserGroup
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 3.1.13 on 2021-11-30 02:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0011_auto_20210826_1759'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='account',
|
||||
options={'verbose_name': 'Application account'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='application',
|
||||
options={'ordering': ('name',), 'verbose_name': 'Application'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='applicationuser',
|
||||
options={'verbose_name': 'Application user'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='historicalaccount',
|
||||
options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Application account'},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.1.12 on 2022-02-11 06:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('applications', '0012_auto_20211130_1037'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='account',
|
||||
options={'permissions': [('view_applicationaccountsecret', 'Can view application account secret'), ('change_appplicationaccountsecret', 'Can view application account secret')], 'verbose_name': 'Application account'},
|
||||
),
|
||||
]
|
|
@ -20,8 +20,12 @@ class Account(BaseUser):
|
|||
auth_attrs = ['username', 'password', 'private_key', 'public_key']
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Account')
|
||||
verbose_name = _('Application account')
|
||||
unique_together = [('username', 'app', 'systemuser')]
|
||||
permissions = [
|
||||
('view_applicationaccountsecret', _('Can view application account secret')),
|
||||
('change_appplicationaccountsecret', _('Can view application account secret')),
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
|
|
@ -219,6 +219,7 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
|
|||
verbose_name = _('Application')
|
||||
unique_together = [('org_id', 'name')]
|
||||
ordering = ('name',)
|
||||
verbose_name = _("Application")
|
||||
|
||||
def __str__(self):
|
||||
category_display = self.get_category_display()
|
||||
|
@ -265,3 +266,4 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
|
|||
class ApplicationUser(SystemUser):
|
||||
class Meta:
|
||||
proxy = True
|
||||
verbose_name = _('Application user')
|
||||
|
|
|
@ -6,7 +6,6 @@ from django.shortcuts import get_object_or_404
|
|||
from rest_framework.generics import CreateAPIView
|
||||
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, NeedMFAVerify
|
||||
from common.drf.filters import BaseFilterSet
|
||||
from ..tasks.account_connectivity import test_accounts_connectivity_manual
|
||||
from ..models import AuthBook, Node
|
||||
|
@ -62,7 +61,6 @@ class AccountViewSet(OrgBulkModelViewSet):
|
|||
'default': serializers.AccountSerializer,
|
||||
'verify_account': serializers.AssetTaskSerializer
|
||||
}
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = AuthBook.get_queryset()
|
||||
|
@ -82,17 +80,22 @@ class AccountSecretsViewSet(AccountViewSet):
|
|||
serializer_classes = {
|
||||
'default': serializers.AccountSecretSerializer
|
||||
}
|
||||
permission_classes = (IsOrgAdmin, NeedMFAVerify)
|
||||
http_method_names = ['get']
|
||||
rbac_perms = {
|
||||
'list': 'assets.view_assetsecret',
|
||||
'retrieve': 'assets.view_assetsecret',
|
||||
}
|
||||
|
||||
|
||||
class AccountTaskCreateAPI(CreateAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.AccountTaskSerializer
|
||||
filterset_fields = AccountViewSet.filterset_fields
|
||||
search_fields = AccountViewSet.search_fields
|
||||
filterset_class = AccountViewSet.filterset_class
|
||||
|
||||
def check_permissions(self, request):
|
||||
return request.user.has_perm('assets.test_assetconnectivity')
|
||||
|
||||
def get_accounts(self):
|
||||
queryset = AuthBook.objects.all()
|
||||
queryset = self.filter_queryset(queryset)
|
||||
|
@ -109,5 +112,4 @@ class AccountTaskCreateAPI(CreateAPIView):
|
|||
def get_exception_handler(self):
|
||||
def handler(e, context):
|
||||
return Response({"error": str(e)}, status=400)
|
||||
|
||||
return handler
|
||||
|
|
|
@ -2,9 +2,9 @@ from django.db.models import Count
|
|||
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from common.utils import get_logger
|
||||
from ..hands import IsOrgAdmin
|
||||
from ..models import SystemUser
|
||||
from .. import serializers
|
||||
from rbac.permissions import RBACPermission
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
@ -20,7 +20,7 @@ class AdminUserViewSet(OrgBulkModelViewSet):
|
|||
filterset_fields = ("name", "username")
|
||||
search_fields = filterset_fields
|
||||
serializer_class = serializers.AdminUserSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
permission_classes = (RBACPermission,)
|
||||
ordering_fields = ('name',)
|
||||
ordering = ('name', )
|
||||
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from assets.api import FilterAssetByNodeMixin
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from rest_framework.generics import RetrieveAPIView, ListAPIView
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.db.models import Q
|
||||
|
||||
from common.utils import get_logger, get_object_or_none
|
||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsSuperUser
|
||||
from common.mixins.api import SuggestionMixin
|
||||
from users.models import User, UserGroup
|
||||
from users.serializers import UserSerializer, UserGroupSerializer
|
||||
|
@ -17,6 +15,7 @@ from perms.serializers import AssetPermissionSerializer
|
|||
from perms.filters import AssetPermissionFilter
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from assets.api import FilterAssetByNodeMixin
|
||||
from ..models import Asset, Node, Platform
|
||||
from .. import serializers
|
||||
from ..tasks import (
|
||||
|
@ -55,7 +54,6 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
|
|||
'default': serializers.AssetSerializer,
|
||||
'suggestion': serializers.MiniAssetSerializer
|
||||
}
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
extra_filter_backends = [FilterAssetByNodeFilterBackend, LabelFilterBackend, IpInFilterBackend]
|
||||
|
||||
def set_assets_node(self, assets):
|
||||
|
@ -76,8 +74,10 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
|
|||
|
||||
class AssetPlatformRetrieveApi(RetrieveAPIView):
|
||||
queryset = Platform.objects.all()
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.PlatformSerializer
|
||||
rbac_perms = {
|
||||
'retrieve': 'assets.view_gateway'
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
asset_pk = self.kwargs.get('pk')
|
||||
|
@ -87,16 +87,10 @@ class AssetPlatformRetrieveApi(RetrieveAPIView):
|
|||
|
||||
class AssetPlatformViewSet(ModelViewSet):
|
||||
queryset = Platform.objects.all()
|
||||
permission_classes = (IsSuperUser,)
|
||||
serializer_class = serializers.PlatformSerializer
|
||||
filterset_fields = ['name', 'base']
|
||||
search_fields = ['name']
|
||||
|
||||
def get_permissions(self):
|
||||
if self.request.method.lower() in ['get', 'options']:
|
||||
self.permission_classes = (IsOrgAdmin,)
|
||||
return super().get_permissions()
|
||||
|
||||
def check_object_permissions(self, request, obj):
|
||||
if request.method.lower() in ['delete', 'put', 'patch'] and obj.internal:
|
||||
self.permission_denied(
|
||||
|
@ -131,7 +125,6 @@ class AssetsTaskMixin:
|
|||
class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
|
||||
model = Asset
|
||||
serializer_class = serializers.AssetTaskSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
pk = self.kwargs.get('pk')
|
||||
|
@ -139,11 +132,24 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
|
|||
request.data['assets'] = [pk]
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
def check_permissions(self, request):
|
||||
action = request.data.get('action')
|
||||
action_perm_require = {
|
||||
'push_system_user': 'assets.push_assetsystemuser',
|
||||
'test_system_user': 'assets.test_assetconnectivity'
|
||||
}
|
||||
perm_required = action_perm_require.get(action)
|
||||
has = self.request.user.has_perm(perm_required)
|
||||
|
||||
if not has:
|
||||
self.permission_denied(request)
|
||||
|
||||
def perform_asset_task(self, serializer):
|
||||
data = serializer.validated_data
|
||||
action = data['action']
|
||||
if action not in ['push_system_user', 'test_system_user']:
|
||||
return
|
||||
|
||||
asset = data['asset']
|
||||
system_users = data.get('system_users')
|
||||
if not system_users:
|
||||
|
@ -166,12 +172,13 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
|
|||
class AssetsTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
|
||||
model = Asset
|
||||
serializer_class = serializers.AssetsTaskSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
||||
class AssetGatewayListApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.GatewayWithAuthSerializer
|
||||
rbac_perms = {
|
||||
'list': 'assets.view_gateway'
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
asset_id = self.kwargs.get('pk')
|
||||
|
@ -183,7 +190,6 @@ class AssetGatewayListApi(generics.ListAPIView):
|
|||
|
||||
|
||||
class BaseAssetPermUserOrUserGroupListApi(ListAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_object(self):
|
||||
asset_id = self.kwargs.get('pk')
|
||||
|
@ -220,11 +226,13 @@ class AssetPermUserGroupListApi(BaseAssetPermUserOrUserGroupListApi):
|
|||
|
||||
|
||||
class BaseAssetPermUserOrUserGroupPermissionsListApiMixin(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
model = AssetPermission
|
||||
serializer_class = AssetPermissionSerializer
|
||||
filterset_class = AssetPermissionFilter
|
||||
search_fields = ('name',)
|
||||
rbac_perms = {
|
||||
'list': 'perms.view_assetpermission'
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
asset_id = self.kwargs.get('pk')
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from rest_framework import status, mixins, viewsets
|
||||
from rest_framework import status, viewsets
|
||||
from rest_framework.response import Response
|
||||
|
||||
from common.permissions import IsOrgAdmin
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
|
||||
from .. import serializers
|
||||
from ..tasks import execute_account_backup_plan
|
||||
from ..models import (
|
||||
|
@ -24,17 +22,13 @@ class AccountBackupPlanViewSet(OrgBulkModelViewSet):
|
|||
ordering_fields = ('name',)
|
||||
ordering = ('name',)
|
||||
serializer_class = serializers.AccountBackupPlanSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
||||
class AccountBackupPlanExecutionViewSet(
|
||||
mixins.CreateModelMixin, mixins.ListModelMixin,
|
||||
mixins.RetrieveModelMixin, viewsets.GenericViewSet
|
||||
):
|
||||
class AccountBackupPlanExecutionViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = serializers.AccountBackupPlanExecutionSerializer
|
||||
search_fields = ('trigger',)
|
||||
filterset_fields = ('trigger', 'plan_id')
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
http_method_names = ['get', 'post']
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = AccountBackupPlanExecution.objects.all()
|
||||
|
|
|
@ -9,7 +9,6 @@ from common.utils import reverse
|
|||
from common.utils import lazyproperty
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from tickets.api import GenericTicketStatusRetrieveCloseAPI
|
||||
from ..hands import IsOrgAdmin, IsAppUser
|
||||
from ..models import CommandFilter, CommandFilterRule
|
||||
from .. import serializers
|
||||
|
||||
|
@ -23,7 +22,6 @@ class CommandFilterViewSet(OrgBulkModelViewSet):
|
|||
model = CommandFilter
|
||||
filterset_fields = ("name",)
|
||||
search_fields = filterset_fields
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.CommandFilterSerializer
|
||||
|
||||
|
||||
|
@ -31,7 +29,6 @@ class CommandFilterRuleViewSet(OrgBulkModelViewSet):
|
|||
model = CommandFilterRule
|
||||
filterset_fields = ('content',)
|
||||
search_fields = filterset_fields
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.CommandFilterRuleSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -43,8 +40,10 @@ class CommandFilterRuleViewSet(OrgBulkModelViewSet):
|
|||
|
||||
|
||||
class CommandConfirmAPI(CreateAPIView):
|
||||
permission_classes = (IsAppUser,)
|
||||
serializer_class = serializers.CommandConfirmSerializer
|
||||
rbac_perms = {
|
||||
'create': 'tickets.add_ticket'
|
||||
}
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
ticket = self.create_command_confirm_ticket()
|
||||
|
|
|
@ -6,7 +6,6 @@ from rest_framework.views import APIView, Response
|
|||
from rest_framework.serializers import ValidationError
|
||||
|
||||
from common.utils import get_logger
|
||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from ..models import Domain, Gateway
|
||||
from .. import serializers
|
||||
|
@ -20,7 +19,6 @@ class DomainViewSet(OrgBulkModelViewSet):
|
|||
model = Domain
|
||||
filterset_fields = ("name", )
|
||||
search_fields = filterset_fields
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.DomainSerializer
|
||||
ordering_fields = ('name',)
|
||||
ordering = ('name', )
|
||||
|
@ -35,13 +33,14 @@ class GatewayViewSet(OrgBulkModelViewSet):
|
|||
model = Gateway
|
||||
filterset_fields = ("domain__name", "name", "username", "ip", "domain")
|
||||
search_fields = ("domain__name", "name", "username", "ip")
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.GatewaySerializer
|
||||
|
||||
|
||||
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
object = None
|
||||
rbac_perms = {
|
||||
'POST': 'assets.change_gateway'
|
||||
}
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(Gateway.objects.all())
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
from orgs.mixins.api import OrgModelViewSet
|
||||
from assets.models import GatheredUser
|
||||
from common.permissions import IsOrgAdmin
|
||||
|
||||
from ..serializers import GatheredUserSerializer
|
||||
from ..filters import AssetRelatedByNodeFilterBackend
|
||||
|
@ -15,7 +14,6 @@ __all__ = ['GatheredUserViewSet']
|
|||
class GatheredUserViewSet(OrgModelViewSet):
|
||||
model = GatheredUser
|
||||
serializer_class = GatheredUserSerializer
|
||||
permission_classes = [IsOrgAdmin]
|
||||
extra_filter_backends = [AssetRelatedByNodeFilterBackend]
|
||||
|
||||
filterset_fields = ['asset', 'username', 'present', 'asset__ip', 'asset__hostname', 'asset_id']
|
||||
|
|
|
@ -17,7 +17,6 @@ from django.db.models import Count
|
|||
|
||||
from common.utils import get_logger
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from ..hands import IsOrgAdmin
|
||||
from ..models import Label
|
||||
from .. import serializers
|
||||
|
||||
|
@ -30,7 +29,6 @@ class LabelViewSet(OrgBulkModelViewSet):
|
|||
model = Label
|
||||
filterset_fields = ("name", "value")
|
||||
search_fields = filterset_fields
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.LabelSerializer
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
|
|
|
@ -20,7 +20,6 @@ from common.tree import TreeNodeSerializer
|
|||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from orgs.utils import current_org
|
||||
from ..hands import IsOrgAdmin
|
||||
from ..models import Node
|
||||
from ..tasks import (
|
||||
update_node_assets_hardware_info_manual,
|
||||
|
@ -46,7 +45,6 @@ class NodeViewSet(SuggestionMixin, OrgBulkModelViewSet):
|
|||
model = Node
|
||||
filterset_fields = ('value', 'key', 'id')
|
||||
search_fields = ('value', )
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.NodeSerializer
|
||||
|
||||
@action(methods=[POST], detail=False, url_path='check_assets_amount_task')
|
||||
|
@ -85,7 +83,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
|
|||
]
|
||||
"""
|
||||
model = Node
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = TreeNodeSerializer
|
||||
|
||||
@staticmethod
|
||||
|
@ -100,7 +97,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
|
|||
|
||||
|
||||
class NodeChildrenApi(generics.ListCreateAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.NodeSerializer
|
||||
instance = None
|
||||
is_initial = False
|
||||
|
@ -199,7 +195,6 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
|
|||
|
||||
|
||||
class NodeAssetsApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.AssetSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -214,7 +209,6 @@ class NodeAssetsApi(generics.ListAPIView):
|
|||
|
||||
class NodeAddChildrenApi(generics.UpdateAPIView):
|
||||
model = Node
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.NodeAddChildrenSerializer
|
||||
instance = None
|
||||
|
||||
|
@ -231,7 +225,6 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
|
|||
class NodeAddAssetsApi(generics.UpdateAPIView):
|
||||
model = Node
|
||||
serializer_class = serializers.NodeAssetsSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
instance = None
|
||||
|
||||
def perform_update(self, serializer):
|
||||
|
@ -243,7 +236,6 @@ class NodeAddAssetsApi(generics.UpdateAPIView):
|
|||
class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
||||
model = Node
|
||||
serializer_class = serializers.NodeAssetsSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
instance = None
|
||||
|
||||
def perform_update(self, serializer):
|
||||
|
@ -262,7 +254,6 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
|||
class MoveAssetsToNodeApi(generics.UpdateAPIView):
|
||||
model = Node
|
||||
serializer_class = serializers.NodeAssetsSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
instance = None
|
||||
|
||||
def perform_update(self, serializer):
|
||||
|
@ -305,8 +296,6 @@ class MoveAssetsToNodeApi(generics.UpdateAPIView):
|
|||
class NodeTaskCreateApi(generics.CreateAPIView):
|
||||
model = Node
|
||||
serializer_class = serializers.NodeTaskSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_object(self):
|
||||
node_id = self.kwargs.get('pk')
|
||||
node = get_object_or_none(self.model, id=node_id)
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.middleware import csrf
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.decorators import action
|
||||
|
||||
from common.utils import get_logger, get_object_or_none
|
||||
from common.utils.crypto import get_aes_crypto
|
||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsValidUser
|
||||
from common.permissions import IsValidUser
|
||||
from common.mixins.api import SuggestionMixin
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from common.mixins.api import SuggestionMixin
|
||||
from orgs.utils import tmp_to_root_org
|
||||
from rest_framework.decorators import action
|
||||
from ..models import SystemUser, CommandFilterRule
|
||||
from .. import serializers
|
||||
from ..serializers import SystemUserWithAuthInfoSerializer, SystemUserTempAuthSerializer
|
||||
|
@ -46,7 +45,6 @@ class SystemUserViewSet(SuggestionMixin, OrgBulkModelViewSet):
|
|||
}
|
||||
ordering_fields = ('name', 'protocol', 'login_mode')
|
||||
ordering = ('name', )
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
|
||||
@action(methods=['get'], detail=False, url_path='su-from')
|
||||
def su_from(self, request, *args, **kwargs):
|
||||
|
@ -80,8 +78,13 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
|||
Get system user auth info
|
||||
"""
|
||||
model = SystemUser
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = SystemUserWithAuthInfoSerializer
|
||||
rbac_perms = {
|
||||
'retrieve': 'assets.view_systemusersecret',
|
||||
'list': 'assets.view_systemusersecret',
|
||||
'change': 'assets.change_systemuser',
|
||||
'destroy': 'assets.change_systemuser',
|
||||
}
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
|
@ -123,7 +126,6 @@ class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView):
|
|||
Get system user with asset auth info
|
||||
"""
|
||||
model = SystemUser
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = SystemUserWithAuthInfoSerializer
|
||||
|
||||
def get_object(self):
|
||||
|
@ -140,8 +142,10 @@ class SystemUserAppAuthInfoApi(generics.RetrieveAPIView):
|
|||
Get system user with asset auth info
|
||||
"""
|
||||
model = SystemUser
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = SystemUserWithAuthInfoSerializer
|
||||
rbac_perms = {
|
||||
'retrieve': 'assets.view_systemusersecret',
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
instance = super().get_object()
|
||||
|
@ -153,7 +157,6 @@ class SystemUserAppAuthInfoApi(generics.RetrieveAPIView):
|
|||
|
||||
|
||||
class SystemUserTaskApi(generics.CreateAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.SystemUserTaskSerializer
|
||||
|
||||
def do_push(self, system_user, asset_ids=None):
|
||||
|
@ -175,6 +178,18 @@ class SystemUserTaskApi(generics.CreateAPIView):
|
|||
pk = self.kwargs.get('pk')
|
||||
return get_object_or_404(SystemUser, pk=pk)
|
||||
|
||||
def check_permissions(self, request):
|
||||
action = request.data.get('action')
|
||||
action_perm_require = {
|
||||
'push': 'assets.push_systemuser',
|
||||
'test': 'assets.test_connectivity'
|
||||
}
|
||||
perm_required = action_perm_require.get(action)
|
||||
has = self.request.user.has_perm(perm_required)
|
||||
|
||||
if not has:
|
||||
self.permission_denied(request)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
action = serializer.validated_data["action"]
|
||||
asset = serializer.validated_data.get('asset')
|
||||
|
@ -198,7 +213,9 @@ class SystemUserTaskApi(generics.CreateAPIView):
|
|||
|
||||
|
||||
class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
rbac_perms = {
|
||||
'list': 'assets.view_commandfilterule'
|
||||
}
|
||||
|
||||
def get_serializer_class(self):
|
||||
from ..serializers import CommandFilterRuleSerializer
|
||||
|
@ -224,10 +241,12 @@ class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
|
|||
|
||||
|
||||
class SystemUserAssetsListView(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.AssetSimpleSerializer
|
||||
filterset_fields = ("hostname", "ip")
|
||||
search_fields = filterset_fields
|
||||
rbac_perms = {
|
||||
'list': 'assets.view_asset'
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
pk = self.kwargs.get('pk')
|
||||
|
|
|
@ -5,7 +5,6 @@ from django.db.models import F, Value, Model
|
|||
from django.db.models.signals import m2m_changed
|
||||
from django.db.models.functions import Concat
|
||||
|
||||
from common.permissions import IsOrgAdmin
|
||||
from common.utils import get_logger
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.utils import current_org
|
||||
|
@ -71,7 +70,6 @@ class BaseRelationViewSet(RelationMixin, OrgBulkModelViewSet):
|
|||
class SystemUserAssetRelationViewSet(BaseRelationViewSet):
|
||||
serializer_class = serializers.SystemUserAssetRelationSerializer
|
||||
model = models.SystemUser.assets.through
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', 'asset', 'systemuser',
|
||||
]
|
||||
|
@ -97,7 +95,6 @@ class SystemUserAssetRelationViewSet(BaseRelationViewSet):
|
|||
class SystemUserNodeRelationViewSet(BaseRelationViewSet):
|
||||
serializer_class = serializers.SystemUserNodeRelationSerializer
|
||||
model = models.SystemUser.nodes.through
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', 'node', 'systemuser',
|
||||
]
|
||||
|
@ -118,7 +115,6 @@ class SystemUserNodeRelationViewSet(BaseRelationViewSet):
|
|||
class SystemUserUserRelationViewSet(BaseRelationViewSet):
|
||||
serializer_class = serializers.SystemUserUserRelationSerializer
|
||||
model = models.SystemUser.users.through
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', 'user', 'systemuser',
|
||||
]
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
from __future__ import unicode_literals
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AssetsConfig(AppConfig):
|
||||
name = 'assets'
|
||||
verbose_name = _('Assets')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def ready(self):
|
||||
super().ready()
|
||||
|
|
|
@ -11,5 +11,4 @@
|
|||
"""
|
||||
|
||||
|
||||
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
|
||||
from users.models import User, UserGroup
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.1.13 on 2021-11-30 02:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0076_delete_assetuser'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='label',
|
||||
options={'verbose_name': 'Label'},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 3.1.12 on 2022-02-11 06:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0077_auto_20211130_1037'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='asset',
|
||||
options={'ordering': ['hostname'], 'permissions': [('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetsystemuser', 'Can push system user to asset')], 'verbose_name': 'Asset'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='authbook',
|
||||
options={'permissions': [('view_assetaccountsecret', 'Can view asset account secret'), ('change_assetaccountsecret', 'Can change asset account secret')], 'verbose_name': 'AuthBook'},
|
||||
),
|
||||
]
|
|
@ -355,3 +355,7 @@ class Asset(AbsConnectivity, AbsHardwareInfo, ProtocolsMixin, NodesRelationMixin
|
|||
unique_together = [('org_id', 'hostname')]
|
||||
verbose_name = _("Asset")
|
||||
ordering = ["hostname", ]
|
||||
permissions = [
|
||||
('test_assetconnectivity', 'Can test asset connectivity'),
|
||||
('push_assetsystemuser', 'Can push system user to asset'),
|
||||
]
|
||||
|
|
|
@ -26,6 +26,10 @@ class AuthBook(BaseUser, AbsConnectivity):
|
|||
class Meta:
|
||||
verbose_name = _('AuthBook')
|
||||
unique_together = [('username', 'asset', 'systemuser')]
|
||||
permissions = [
|
||||
('view_assetaccountsecret', _('Can view asset account secret')),
|
||||
('change_assetaccountsecret', _('Can change asset account secret'))
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
|
|
@ -37,3 +37,4 @@ class Label(OrgModelMixin):
|
|||
class Meta:
|
||||
db_table = "assets_label"
|
||||
unique_together = [('name', 'value', 'org_id')]
|
||||
verbose_name = _('Label')
|
||||
|
|
|
@ -325,7 +325,7 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
|
|||
verbose_name = _("System user")
|
||||
|
||||
|
||||
# Todo: 准备废弃
|
||||
# Deprecated: 准备废弃
|
||||
class AdminUser(BaseUser):
|
||||
"""
|
||||
A privileged user that ansible can use it to push system user and so on
|
||||
|
|
|
@ -4,7 +4,6 @@ from rest_framework.mixins import ListModelMixin, CreateModelMixin
|
|||
from django.db.models import F, Value
|
||||
from django.db.models.functions import Concat
|
||||
|
||||
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsOrgAdmin
|
||||
from common.drf.filters import DatetimeRangeFilter
|
||||
from common.api import CommonGenericViewSet
|
||||
from orgs.mixins.api import OrgGenericViewSet, OrgBulkModelViewSet, OrgRelationMixin
|
||||
|
@ -20,7 +19,6 @@ class FTPLogViewSet(CreateModelMixin,
|
|||
OrgGenericViewSet):
|
||||
model = FTPLog
|
||||
serializer_class = FTPLogSerializer
|
||||
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
|
||||
extra_filter_backends = [DatetimeRangeFilter]
|
||||
date_range_filter_fields = [
|
||||
('date_start', ('date_from', 'date_to'))
|
||||
|
@ -32,7 +30,6 @@ class FTPLogViewSet(CreateModelMixin,
|
|||
|
||||
class UserLoginLogViewSet(ListModelMixin, CommonGenericViewSet):
|
||||
queryset = UserLoginLog.objects.all()
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
serializer_class = UserLoginLogSerializer
|
||||
extra_filter_backends = [DatetimeRangeFilter]
|
||||
date_range_filter_fields = [
|
||||
|
@ -58,7 +55,6 @@ class UserLoginLogViewSet(ListModelMixin, CommonGenericViewSet):
|
|||
class OperateLogViewSet(ListModelMixin, OrgGenericViewSet):
|
||||
model = OperateLog
|
||||
serializer_class = OperateLogSerializer
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
extra_filter_backends = [DatetimeRangeFilter]
|
||||
date_range_filter_fields = [
|
||||
('datetime', ('date_from', 'date_to'))
|
||||
|
@ -70,7 +66,6 @@ class OperateLogViewSet(ListModelMixin, OrgGenericViewSet):
|
|||
|
||||
class PasswordChangeLogViewSet(ListModelMixin, CommonGenericViewSet):
|
||||
queryset = PasswordChangeLog.objects.all()
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
serializer_class = PasswordChangeLogSerializer
|
||||
extra_filter_backends = [DatetimeRangeFilter]
|
||||
date_range_filter_fields = [
|
||||
|
@ -91,7 +86,6 @@ class PasswordChangeLogViewSet(ListModelMixin, CommonGenericViewSet):
|
|||
class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet):
|
||||
model = CommandExecution
|
||||
serializer_class = CommandExecutionSerializer
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
extra_filter_backends = [DatetimeRangeFilter]
|
||||
date_range_filter_fields = [
|
||||
('date_start', ('date_from', 'date_to'))
|
||||
|
@ -117,7 +111,6 @@ class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet):
|
|||
class CommandExecutionHostRelationViewSet(OrgRelationMixin, OrgBulkModelViewSet):
|
||||
serializer_class = CommandExecutionHostsRelationSerializer
|
||||
m2m_field = CommandExecution.hosts.field
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
filterset_fields = [
|
||||
'id', 'asset', 'commandexecution'
|
||||
]
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
from django.apps import AppConfig
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models.signals import post_save
|
||||
|
||||
|
||||
class AuditsConfig(AppConfig):
|
||||
name = 'audits'
|
||||
verbose_name = _('Audits')
|
||||
|
||||
def ready(self):
|
||||
from . import signals_handler
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 3.1.13 on 2021-11-30 02:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('audits', '0012_auto_20210414_1443'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='ftplog',
|
||||
options={'verbose_name': 'File transfer log'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='operatelog',
|
||||
options={'verbose_name': 'Operate log'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='passwordchangelog',
|
||||
options={'verbose_name': 'Password change log'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='userloginlog',
|
||||
options={'ordering': ['-datetime', 'username'], 'verbose_name': 'User login log'},
|
||||
),
|
||||
]
|
|
@ -43,6 +43,9 @@ class FTPLog(OrgModelMixin):
|
|||
is_success = models.BooleanField(default=True, verbose_name=_("Success"))
|
||||
date_start = models.DateTimeField(auto_now_add=True, verbose_name=_('Date start'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("File transfer log")
|
||||
|
||||
|
||||
class OperateLog(OrgModelMixin):
|
||||
ACTION_CREATE = 'create'
|
||||
|
@ -73,6 +76,9 @@ class OperateLog(OrgModelMixin):
|
|||
self.org_id = Organization.ROOT_ID
|
||||
return super(OperateLog, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Operate log")
|
||||
|
||||
|
||||
class PasswordChangeLog(models.Model):
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
|
@ -84,6 +90,9 @@ class PasswordChangeLog(models.Model):
|
|||
def __str__(self):
|
||||
return "{} change {}'s password".format(self.change_by, self.user)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Password change log')
|
||||
|
||||
|
||||
class UserLoginLog(models.Model):
|
||||
LOGIN_TYPE_CHOICE = (
|
||||
|
@ -155,3 +164,4 @@ class UserLoginLog(models.Model):
|
|||
|
||||
class Meta:
|
||||
ordering = ['-datetime', 'username']
|
||||
verbose_name = _('User login log')
|
||||
|
|
|
@ -102,11 +102,6 @@ def create_operate_log(action, sender, resource):
|
|||
|
||||
|
||||
M2M_NEED_RECORD = {
|
||||
'OrganizationMember': (
|
||||
_('User and Organization'),
|
||||
_('{User} JOINED {Organization}'),
|
||||
_('{User} LEFT {Organization}')
|
||||
),
|
||||
User.groups.through._meta.object_name: (
|
||||
_('User and Group'),
|
||||
_('{User} JOINED {UserGroup}'),
|
||||
|
|
|
@ -24,12 +24,10 @@ from applications.models import Application
|
|||
from authentication.signals import post_auth_failed
|
||||
from common.utils import get_logger, random_string
|
||||
from common.mixins.api import SerializerMixin
|
||||
from common.permissions import IsSuperUserOrAppUser, IsValidUser, IsSuperUser
|
||||
from common.utils.common import get_file_by_arch
|
||||
from orgs.mixins.api import RootOrgViewMixin
|
||||
from common.http import is_true
|
||||
from perms.models.base import Action
|
||||
from perms.utils.application.permission import validate_permission as app_validate_permission
|
||||
from perms.utils.application.permission import get_application_actions
|
||||
from perms.utils.asset.permission import get_asset_actions
|
||||
|
||||
|
@ -42,6 +40,14 @@ __all__ = ['UserConnectionTokenViewSet']
|
|||
|
||||
|
||||
class ClientProtocolMixin:
|
||||
"""
|
||||
下载客户端支持的连接文件,里面包含了 token,和 其他连接信息
|
||||
|
||||
- [x] RDP
|
||||
- [ ] KoKo
|
||||
|
||||
本质上,这里还是暴露出 token 来,进行使用
|
||||
"""
|
||||
request: Request
|
||||
get_serializer: Callable
|
||||
create_token: Callable
|
||||
|
@ -167,7 +173,7 @@ class ClientProtocolMixin:
|
|||
rst = rst.decode('ascii')
|
||||
return rst
|
||||
|
||||
@action(methods=['POST', 'GET'], detail=False, url_path='rdp/file', permission_classes=[IsValidUser])
|
||||
@action(methods=['POST', 'GET'], detail=False, url_path='rdp/file')
|
||||
def get_rdp_file(self, request, *args, **kwargs):
|
||||
if self.request.method == 'GET':
|
||||
data = self.request.query_params
|
||||
|
@ -214,7 +220,7 @@ class ClientProtocolMixin:
|
|||
}
|
||||
return data
|
||||
|
||||
@action(methods=['POST', 'GET'], detail=False, url_path='client-url', permission_classes=[IsValidUser])
|
||||
@action(methods=['POST', 'GET'], detail=False, url_path='client-url')
|
||||
def get_client_protocol_url(self, request, *args, **kwargs):
|
||||
serializer = self.get_valid_serializer()
|
||||
try:
|
||||
|
@ -271,8 +277,14 @@ class SecretDetailMixin:
|
|||
'remote_app': None,
|
||||
}
|
||||
|
||||
@action(methods=['POST'], detail=False, permission_classes=[IsSuperUserOrAppUser], url_path='secret-info/detail')
|
||||
@action(methods=['POST'], detail=False, url_path='secret-info/detail')
|
||||
def get_secret_detail(self, request, *args, **kwargs):
|
||||
perm_required = 'authentication.view_connectiontokensecret'
|
||||
|
||||
# 非常重要的 api,再逻辑层再判断一下,双重保险
|
||||
if not request.user.has_perm(perm_required):
|
||||
raise PermissionDenied('Not allow to view secret')
|
||||
|
||||
token = request.data.get('token', '')
|
||||
try:
|
||||
value, user, system_user, asset, app, expired_at, actions = self.valid_token(token)
|
||||
|
@ -307,12 +319,18 @@ class UserConnectionTokenViewSet(
|
|||
RootOrgViewMixin, SerializerMixin, ClientProtocolMixin,
|
||||
SecretDetailMixin, GenericViewSet
|
||||
):
|
||||
permission_classes = (IsSuperUserOrAppUser,)
|
||||
serializer_classes = {
|
||||
'default': ConnectionTokenSerializer,
|
||||
'get_secret_detail': ConnectionTokenSecretSerializer,
|
||||
}
|
||||
CACHE_KEY_PREFIX = 'CONNECTION_TOKEN_{}'
|
||||
rbac_perms = {
|
||||
'GET': 'view_connectiontoken',
|
||||
'create': 'add_connectiontoken',
|
||||
'get_secret_detail': 'view_connectiontokensecret',
|
||||
'get_rdp_file': 'add_connectiontoken',
|
||||
'get_client_protocol_url': 'add_connectiontoken',
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def check_resource_permission(user, asset, application, system_user):
|
||||
|
@ -403,14 +421,6 @@ class UserConnectionTokenViewSet(
|
|||
raise serializers.ValidationError('Permission expired or invalid')
|
||||
return value, user, system_user, asset, app, expired_at, actions
|
||||
|
||||
def get_permissions(self):
|
||||
if self.action in ["create", "get_rdp_file"]:
|
||||
if self.request.data.get('user', None):
|
||||
self.permission_classes = (IsSuperUser,)
|
||||
else:
|
||||
self.permission_classes = (IsValidUser,)
|
||||
return super().get_permissions()
|
||||
|
||||
def get(self, request):
|
||||
token = request.query_params.get('token')
|
||||
key = self.CACHE_KEY_PREFIX.format(token)
|
||||
|
|
|
@ -5,7 +5,6 @@ from rest_framework.response import Response
|
|||
from users.permissions import IsAuthPasswdTimeValid
|
||||
from users.models import User
|
||||
from common.utils import get_logger
|
||||
from common.permissions import IsOrgAdmin
|
||||
from common.mixins.api import RoleUserMixin, RoleAdminMixin
|
||||
from authentication import errors
|
||||
|
||||
|
@ -32,4 +31,4 @@ class DingTalkQRUnBindForUserApi(RoleUserMixin, DingTalkQRUnBindBase):
|
|||
|
||||
class DingTalkQRUnBindForAdminApi(RoleAdminMixin, DingTalkQRUnBindBase):
|
||||
user_id_url_kwarg = 'user_id'
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
|
@ -5,7 +5,6 @@ from rest_framework.response import Response
|
|||
from users.permissions import IsAuthPasswdTimeValid
|
||||
from users.models import User
|
||||
from common.utils import get_logger
|
||||
from common.permissions import IsOrgAdmin
|
||||
from common.mixins.api import RoleUserMixin, RoleAdminMixin
|
||||
from authentication import errors
|
||||
|
||||
|
@ -32,7 +31,6 @@ class FeiShuQRUnBindForUserApi(RoleUserMixin, FeiShuQRUnBindBase):
|
|||
|
||||
class FeiShuQRUnBindForAdminApi(RoleAdminMixin, FeiShuQRUnBindBase):
|
||||
user_id_url_kwarg = 'user_id'
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
||||
class FeiShuEventSubscriptionCallback(APIView):
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from rest_framework.generics import UpdateAPIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.permissions import AllowAny
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from common.utils import get_logger
|
||||
from common.permissions import IsOrgAdmin
|
||||
from .. import errors, mixins
|
||||
|
||||
__all__ = ['TicketStatusApi']
|
||||
|
|
|
@ -39,14 +39,6 @@ class MFASendCodeApi(AuthMixin, CreateAPIView):
|
|||
username = ''
|
||||
ip = ''
|
||||
|
||||
def get_user_from_db(self, username):
|
||||
try:
|
||||
user = get_object_or_404(User, username=username)
|
||||
return user
|
||||
except Exception as e:
|
||||
self.incr_mfa_failed_time(username, self.ip)
|
||||
raise e
|
||||
|
||||
def get_user_from_db(self, username):
|
||||
"""避免暴力测试用户名"""
|
||||
ip = self.get_request_ip()
|
||||
|
|
|
@ -13,7 +13,7 @@ from common.utils.timezone import utc_now
|
|||
from common.const.http import POST, GET
|
||||
from common.drf.api import JMSGenericViewSet
|
||||
from common.drf.serializers import EmptySerializer
|
||||
from common.permissions import IsSuperUser
|
||||
from common.permissions import OnlySuperUser
|
||||
from common.utils import reverse
|
||||
from users.models import User
|
||||
from ..serializers import SSOTokenSerializer
|
||||
|
@ -32,9 +32,8 @@ class SSOViewSet(AuthMixin, JMSGenericViewSet):
|
|||
'login_url': SSOTokenSerializer,
|
||||
'login': EmptySerializer
|
||||
}
|
||||
permission_classes = (IsSuperUser,)
|
||||
|
||||
@action(methods=[POST], detail=False, permission_classes=[IsSuperUser], url_path='login-url')
|
||||
@action(methods=[POST], detail=False, permission_classes=[OnlySuperUser], url_path='login-url')
|
||||
def login_url(self, request, *args, **kwargs):
|
||||
if not settings.AUTH_SSO:
|
||||
raise SSOAuthClosed()
|
||||
|
|
|
@ -5,7 +5,6 @@ from rest_framework.response import Response
|
|||
from users.permissions import IsAuthPasswdTimeValid
|
||||
from users.models import User
|
||||
from common.utils import get_logger
|
||||
from common.permissions import IsOrgAdmin
|
||||
from common.mixins.api import RoleUserMixin, RoleAdminMixin
|
||||
from authentication import errors
|
||||
|
||||
|
@ -32,4 +31,4 @@ class WeComQRUnBindForUserApi(RoleUserMixin, WeComQRUnBindBase):
|
|||
|
||||
class WeComQRUnBindForAdminApi(RoleAdminMixin, WeComQRUnBindBase):
|
||||
user_id_url_kwarg = 'user_id'
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class AuthenticationConfig(AppConfig):
|
||||
name = 'authentication'
|
||||
verbose_name = _('Authentication')
|
||||
|
||||
def ready(self):
|
||||
from . import signals_handlers
|
||||
|
|
|
@ -29,6 +29,9 @@ def get_request_date_header(request):
|
|||
|
||||
|
||||
class JMSModelBackend(ModelBackend):
|
||||
def has_perm(self, user_obj, perm, obj=None):
|
||||
return False
|
||||
|
||||
def user_can_authenticate(self, user):
|
||||
return True
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.1.12 on 2021-09-29 03:06
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0004_ssotoken'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='loginconfirmsetting',
|
||||
options={'verbose_name': 'Login Confirm'},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 3.1.13 on 2021-11-30 02:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0005_auto_20210929_1106'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='accesskey',
|
||||
options={'verbose_name': 'Access key'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='ssotoken',
|
||||
options={'verbose_name': 'SSO token'},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 3.1.12 on 2022-02-11 06:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0006_auto_20211130_1037'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ConnectionToken',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
||||
('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')),
|
||||
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
],
|
||||
options={
|
||||
'permissions': [('add_superconnectiontoken', 'Can add super connection token'), ('view_connectiontokensecret', 'Can view connect token secret')],
|
||||
},
|
||||
),
|
||||
]
|
|
@ -29,6 +29,9 @@ class AccessKey(models.Model):
|
|||
def __str__(self):
|
||||
return str(self.id)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Access key")
|
||||
|
||||
|
||||
class PrivateToken(Token):
|
||||
"""Inherit from auth token, otherwise migration is boring"""
|
||||
|
@ -45,3 +48,17 @@ class SSOToken(models.JMSBaseModel):
|
|||
authkey = models.UUIDField(primary_key=True, default=uuid.uuid4, verbose_name=_('Token'))
|
||||
expired = models.BooleanField(default=False, verbose_name=_('Expired'))
|
||||
user = models.ForeignKey('users.User', on_delete=models.CASCADE, verbose_name=_('User'), db_constraint=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('SSO token')
|
||||
|
||||
|
||||
class ConnectionToken(models.JMSBaseModel):
|
||||
# Todo: 未来可能放到这里,不记录到 redis 了,虽然方便,但是不易于审计
|
||||
# Todo: add connection token 可能要授权给 普通用户, 或者放开就行
|
||||
|
||||
class Meta:
|
||||
permissions = [
|
||||
('add_superconnectiontoken', _('Can add super connection token')),
|
||||
('view_connectiontokensecret', _('Can view connect token secret'))
|
||||
]
|
||||
|
|
|
@ -2,10 +2,13 @@
|
|||
#
|
||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||
from rest_framework import permissions
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
|
||||
from common.permissions import IsValidUser
|
||||
|
||||
__all__ = ["PermissionsMixin"]
|
||||
__all__ = ["PermissionsMixin", "SuggestionMixin"]
|
||||
|
||||
|
||||
class PermissionsMixin(UserPassesTestMixin):
|
||||
|
@ -23,3 +26,17 @@ class PermissionsMixin(UserPassesTestMixin):
|
|||
return True
|
||||
|
||||
|
||||
class SuggestionMixin:
|
||||
suggestion_mini_count = 10
|
||||
|
||||
@action(methods=['get'], detail=False, permission_classes=(IsValidUser,))
|
||||
def suggestions(self, request, *args, **kwargs):
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
queryset = queryset[:self.suggestion_mini_count]
|
||||
page = self.paginate_queryset(queryset)
|
||||
if page is not None:
|
||||
serializer = self.get_serializer(page, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
return Response(serializer.data)
|
|
@ -5,9 +5,6 @@ from rest_framework import permissions
|
|||
from django.conf import settings
|
||||
from common.exceptions import MFAVerifyRequired
|
||||
|
||||
from orgs.utils import current_org
|
||||
from common.utils import is_uuid
|
||||
|
||||
|
||||
class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
|
||||
"""Allows access to valid user, is active and not expired"""
|
||||
|
@ -17,80 +14,12 @@ class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
|
|||
and request.user.is_valid
|
||||
|
||||
|
||||
class IsAppUser(IsValidUser):
|
||||
"""Allows access only to app user """
|
||||
|
||||
class OnlySuperUser(IsValidUser):
|
||||
def has_permission(self, request, view):
|
||||
return super(IsAppUser, self).has_permission(request, view) \
|
||||
and request.user.is_app
|
||||
|
||||
|
||||
class IsSuperUser(IsValidUser):
|
||||
def has_permission(self, request, view):
|
||||
return super(IsSuperUser, self).has_permission(request, view) \
|
||||
return super().has_permission(request, view) \
|
||||
and request.user.is_superuser
|
||||
|
||||
|
||||
class IsSuperUserOrAppUser(IsSuperUser):
|
||||
def has_permission(self, request, view):
|
||||
if request.user.is_anonymous:
|
||||
return False
|
||||
return super(IsSuperUserOrAppUser, self).has_permission(request, view) \
|
||||
or request.user.is_app
|
||||
|
||||
|
||||
class IsSuperAuditor(IsValidUser):
|
||||
def has_permission(self, request, view):
|
||||
return super(IsSuperAuditor, self).has_permission(request, view) \
|
||||
and request.user.is_super_auditor
|
||||
|
||||
|
||||
class IsOrgAuditor(IsValidUser):
|
||||
def has_permission(self, request, view):
|
||||
if not current_org:
|
||||
return False
|
||||
return super(IsOrgAuditor, self).has_permission(request, view) \
|
||||
and current_org.can_audit_by(request.user)
|
||||
|
||||
|
||||
class IsOrgAdmin(IsValidUser):
|
||||
"""Allows access only to superuser"""
|
||||
|
||||
def has_permission(self, request, view):
|
||||
if not current_org:
|
||||
return False
|
||||
return super(IsOrgAdmin, self).has_permission(request, view) \
|
||||
and current_org.can_admin_by(request.user)
|
||||
|
||||
|
||||
class IsOrgAdminOrAppUser(IsValidUser):
|
||||
"""Allows access between superuser and app user"""
|
||||
|
||||
def has_permission(self, request, view):
|
||||
if not current_org:
|
||||
return False
|
||||
if request.user.is_anonymous:
|
||||
return False
|
||||
return super(IsOrgAdminOrAppUser, self).has_permission(request, view) \
|
||||
and (current_org.can_admin_by(request.user) or request.user.is_app)
|
||||
|
||||
|
||||
class IsOrgAdminOrAppUserOrUserReadonly(IsOrgAdminOrAppUser):
|
||||
def has_permission(self, request, view):
|
||||
if IsValidUser.has_permission(self, request, view) \
|
||||
and request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
else:
|
||||
return IsOrgAdminOrAppUser.has_permission(self, request, view)
|
||||
|
||||
|
||||
class IsCurrentUserOrReadOnly(permissions.BasePermission):
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
return obj == request.user
|
||||
|
||||
|
||||
class WithBootstrapToken(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
authorization = request.META.get('HTTP_AUTHORIZATION', '')
|
||||
|
@ -100,21 +29,6 @@ class WithBootstrapToken(permissions.BasePermission):
|
|||
return settings.BOOTSTRAP_TOKEN == request_bootstrap_token
|
||||
|
||||
|
||||
class UserCanAnyPermCurrentOrg(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
return current_org.can_any_by(request.user)
|
||||
|
||||
|
||||
class UserCanUpdatePassword(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
return request.user.can_update_password()
|
||||
|
||||
|
||||
class UserCanUpdateSSHKey(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
return request.user.can_update_ssh_key()
|
||||
|
||||
|
||||
class NeedMFAVerify(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
if not settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||
|
@ -126,80 +40,7 @@ class NeedMFAVerify(permissions.BasePermission):
|
|||
raise MFAVerifyRequired()
|
||||
|
||||
|
||||
class CanUpdateDeleteUser(permissions.BasePermission):
|
||||
|
||||
@staticmethod
|
||||
def has_delete_object_permission(request, view, obj):
|
||||
if request.user.is_anonymous:
|
||||
return False
|
||||
if not request.user.can_admin_current_org:
|
||||
return False
|
||||
# 超级管理员 / 组织管理员
|
||||
if str(request.user.id) == str(obj.id):
|
||||
return False
|
||||
# 超级管理员
|
||||
if request.user.is_superuser:
|
||||
if obj.is_superuser and obj.username in ['admin']:
|
||||
return False
|
||||
return True
|
||||
# 组织管理员
|
||||
if obj.is_superuser:
|
||||
return False
|
||||
if obj.is_super_auditor:
|
||||
return False
|
||||
if obj.can_admin_current_org:
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def has_update_object_permission(request, view, obj):
|
||||
if request.user.is_anonymous:
|
||||
return False
|
||||
if not request.user.can_admin_current_org:
|
||||
return False
|
||||
# 超级管理员 / 组织管理员
|
||||
if str(request.user.id) == str(obj.id):
|
||||
return True
|
||||
# 超级管理员
|
||||
if request.user.is_superuser:
|
||||
return True
|
||||
# 组织管理员
|
||||
if obj.is_superuser:
|
||||
return False
|
||||
if obj.is_super_auditor:
|
||||
return False
|
||||
return True
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
if request.user.is_anonymous:
|
||||
return False
|
||||
if not request.user.can_admin_current_org:
|
||||
return False
|
||||
if request.method in ['DELETE']:
|
||||
return self.has_delete_object_permission(request, view, obj)
|
||||
if request.method in ['PUT', 'PATCH']:
|
||||
return self.has_update_object_permission(request, view, obj)
|
||||
return True
|
||||
|
||||
|
||||
class IsObjectOwner(IsValidUser):
|
||||
def has_object_permission(self, request, view, obj):
|
||||
return (super().has_object_permission(request, view, obj) and
|
||||
request.user == getattr(obj, 'user', None))
|
||||
|
||||
|
||||
class HasQueryParamsUserAndIsCurrentOrgMember(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
query_user_id = request.query_params.get('user')
|
||||
if not query_user_id or not is_uuid(query_user_id):
|
||||
return False
|
||||
query_user = current_org.get_members().filter(id=query_user_id).first()
|
||||
return bool(query_user)
|
||||
|
||||
|
||||
class OnlySuperUserCanList(IsValidUser):
|
||||
def has_permission(self, request, view):
|
||||
user = request.user
|
||||
if view.action == 'list' and not user.is_superuser:
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -15,6 +15,7 @@ class TreeNode:
|
|||
iconSkin = ""
|
||||
parentInfo = ''
|
||||
meta = {}
|
||||
checked = False
|
||||
|
||||
_tree = None
|
||||
|
||||
|
@ -101,4 +102,7 @@ class TreeNodeSerializer(serializers.Serializer):
|
|||
open = serializers.BooleanField(default=False)
|
||||
iconSkin = serializers.CharField(max_length=128, allow_blank=True)
|
||||
nocheck = serializers.BooleanField(default=False)
|
||||
checked = serializers.BooleanField(default=False)
|
||||
halfCheck = serializers.BooleanField(default=False)
|
||||
chkDisabled = serializers.BooleanField(default=False)
|
||||
meta = serializers.JSONField()
|
||||
|
|
|
@ -15,8 +15,7 @@ from assets.models import Asset
|
|||
from terminal.models import Session
|
||||
from terminal.utils import ComponentsPrometheusMetricsUtil
|
||||
from orgs.utils import current_org
|
||||
from common.permissions import IsOrgAdmin, IsOrgAuditor
|
||||
from common.utils import lazyproperty, get_request_ip
|
||||
from common.utils import lazyproperty
|
||||
from orgs.caches import OrgResourceStatisticsCache
|
||||
|
||||
|
||||
|
@ -213,8 +212,10 @@ class DatesLoginMetricMixin:
|
|||
|
||||
|
||||
class IndexApi(DatesLoginMetricMixin, APIView):
|
||||
permission_classes = (IsOrgAdmin | IsOrgAuditor,)
|
||||
http_method_names = ['get']
|
||||
rbac_perms = {
|
||||
'GET': 'view_auditview'
|
||||
}
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
data = {}
|
||||
|
|
|
@ -139,6 +139,7 @@ OTP_IN_RADIUS = CONFIG.OTP_IN_RADIUS
|
|||
|
||||
|
||||
AUTH_BACKEND_MODEL = 'authentication.backends.api.JMSModelBackend'
|
||||
RBAC_BACKEND = 'rbac.backends.RBACBackend'
|
||||
AUTH_BACKEND_PUBKEY = 'authentication.backends.pubkey.PublicKeyAuthBackend'
|
||||
AUTH_BACKEND_LDAP = 'authentication.backends.ldap.LDAPAuthorizationBackend'
|
||||
AUTH_BACKEND_OIDC_PASSWORD = 'jms_oidc_rp.backends.OIDCAuthPasswordBackend'
|
||||
|
@ -154,7 +155,7 @@ AUTH_BACKEND_SAML2 = 'authentication.backends.saml2.SAML2Backend'
|
|||
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
AUTH_BACKEND_MODEL, AUTH_BACKEND_PUBKEY, AUTH_BACKEND_WECOM,
|
||||
AUTH_BACKEND_MODEL, RBAC_BACKEND, AUTH_BACKEND_PUBKEY, AUTH_BACKEND_WECOM,
|
||||
AUTH_BACKEND_DINGTALK, AUTH_BACKEND_FEISHU, AUTH_BACKEND_AUTH_TOKEN,
|
||||
AUTH_BACKEND_SSO,
|
||||
]
|
||||
|
|
|
@ -49,6 +49,7 @@ INSTALLED_APPS = [
|
|||
'tickets.apps.TicketsConfig',
|
||||
'acls.apps.AclsConfig',
|
||||
'notifications.apps.NotificationsConfig',
|
||||
'rbac.apps.RBACConfig',
|
||||
'common.apps.CommonConfig',
|
||||
'jms_oidc_rp',
|
||||
'rest_framework',
|
||||
|
|
|
@ -7,7 +7,7 @@ REST_FRAMEWORK = {
|
|||
# Use Django's standard `django.contrib.auth` permissions,
|
||||
# or allow read-only access for unauthenticated users.
|
||||
'DEFAULT_PERMISSION_CLASSES': (
|
||||
'common.permissions.IsSuperUser',
|
||||
'rbac.permissions.RBACPermission',
|
||||
),
|
||||
'DEFAULT_RENDERER_CLASSES': (
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
|
|
|
@ -24,6 +24,7 @@ api_v1 = [
|
|||
path('tickets/', include('tickets.urls.api_urls', namespace='api-tickets')),
|
||||
path('acls/', include('acls.urls.api_urls', namespace='api-acls')),
|
||||
path('notifications/', include('notifications.urls.api_urls', namespace='api-notifications')),
|
||||
path('rbac/', include('rbac.urls.api_urls', namespace='api-rbac')),
|
||||
path('prometheus/metrics/', api.PrometheusMetricsApi.as_view()),
|
||||
]
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from rest_framework.views import APIView
|
|||
from rest_framework.response import Response
|
||||
|
||||
from common.drf.api import JMSGenericViewSet
|
||||
from common.permissions import IsObjectOwner, IsSuperUser, OnlySuperUserCanList
|
||||
from common.permissions import IsValidUser
|
||||
from notifications.notifications import system_msgs
|
||||
from notifications.models import SystemMsgSubscription, UserMsgSubscription
|
||||
from notifications.backends import BACKEND
|
||||
|
@ -25,7 +25,7 @@ class BackendListView(APIView):
|
|||
'name': backend,
|
||||
'name_display': backend.label
|
||||
}
|
||||
for backend in BACKEND
|
||||
for backend in BACKEND.choices
|
||||
if backend.is_enable
|
||||
]
|
||||
return Response(data=data)
|
||||
|
@ -41,6 +41,9 @@ class SystemMsgSubscriptionViewSet(ListModelMixin,
|
|||
'update': SystemMsgSubscriptionSerializer,
|
||||
'partial_update': SystemMsgSubscriptionSerializer
|
||||
}
|
||||
rbac_perms = {
|
||||
|
||||
}
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
data = []
|
||||
|
@ -80,9 +83,13 @@ class UserMsgSubscriptionViewSet(ListModelMixin,
|
|||
UpdateModelMixin,
|
||||
JMSGenericViewSet):
|
||||
lookup_field = 'user_id'
|
||||
queryset = UserMsgSubscription.objects.all()
|
||||
serializer_class = UserMsgSubscriptionSerializer
|
||||
permission_classes = (IsObjectOwner | IsSuperUser, OnlySuperUserCanList)
|
||||
permission_classes = (IsValidUser,)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = UserMsgSubscription.objects.filter(user=self.request.user)
|
||||
return queryset
|
||||
|
||||
|
||||
|
||||
def get_all_test_messages(request):
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class NotificationsConfig(AppConfig):
|
||||
name = 'notifications'
|
||||
verbose_name = _('Notifications')
|
||||
|
||||
def ready(self):
|
||||
from . import signals_handler
|
||||
|
|
|
@ -5,8 +5,6 @@ from django.shortcuts import get_object_or_404
|
|||
from rest_framework import viewsets, generics
|
||||
from rest_framework.views import Response
|
||||
|
||||
from common.drf.api import JMSBulkModelViewSet
|
||||
from common.permissions import IsOrgAdmin
|
||||
from common.drf.serializers import CeleryTaskSerializer
|
||||
from ..models import Task, AdHoc, AdHocExecution
|
||||
from ..serializers import (
|
||||
|
@ -18,7 +16,6 @@ from ..serializers import (
|
|||
)
|
||||
from ..tasks import run_ansible_task
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.utils import current_org
|
||||
|
||||
__all__ = [
|
||||
'TaskViewSet', 'TaskRun', 'AdHocViewSet', 'AdHocRunHistoryViewSet'
|
||||
|
@ -30,7 +27,6 @@ class TaskViewSet(OrgBulkModelViewSet):
|
|||
filterset_fields = ("name",)
|
||||
search_fields = filterset_fields
|
||||
serializer_class = TaskSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
|
@ -46,7 +42,6 @@ class TaskViewSet(OrgBulkModelViewSet):
|
|||
class TaskRun(generics.RetrieveAPIView):
|
||||
queryset = Task.objects.all()
|
||||
serializer_class = CeleryTaskSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
task = self.get_object()
|
||||
|
@ -57,7 +52,6 @@ class TaskRun(generics.RetrieveAPIView):
|
|||
class AdHocViewSet(viewsets.ModelViewSet):
|
||||
queryset = AdHoc.objects.all()
|
||||
serializer_class = AdHocSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == 'retrieve':
|
||||
|
@ -75,7 +69,6 @@ class AdHocViewSet(viewsets.ModelViewSet):
|
|||
class AdHocRunHistoryViewSet(viewsets.ModelViewSet):
|
||||
queryset = AdHocExecution.objects.all()
|
||||
serializer_class = AdHocExecutionSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_queryset(self):
|
||||
task_id = self.request.query_params.get('task')
|
||||
|
|
|
@ -10,7 +10,7 @@ from celery.result import AsyncResult
|
|||
from rest_framework import generics
|
||||
from django_celery_beat.models import PeriodicTask
|
||||
|
||||
from common.permissions import IsValidUser, IsSuperUser
|
||||
from common.permissions import IsValidUser
|
||||
from common.api import LogTailApi
|
||||
from ..models import CeleryTask
|
||||
from ..serializers import CeleryResultSerializer, CeleryPeriodTaskSerializer
|
||||
|
@ -88,7 +88,6 @@ class CeleryResultApi(generics.RetrieveAPIView):
|
|||
class CeleryPeriodTaskViewSet(CommonApiMixin, viewsets.ModelViewSet):
|
||||
queryset = PeriodicTask.objects.all()
|
||||
serializer_class = CeleryPeriodTaskSerializer
|
||||
permission_classes = (IsSuperUser,)
|
||||
http_method_names = ('get', 'head', 'options', 'patch')
|
||||
|
||||
def get_queryset(self):
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 3.1.13 on 2021-11-30 02:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ops', '0020_adhoc_run_system_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='adhoc',
|
||||
options={'get_latest_by': 'date_created', 'verbose_name': 'AdHoc'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='adhocexecution',
|
||||
options={'get_latest_by': 'date_start', 'verbose_name': 'AdHoc execution'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='commandexecution',
|
||||
options={'verbose_name': 'Command execution'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='task',
|
||||
options={'get_latest_by': 'date_created', 'ordering': ('-date_updated',), 'verbose_name': 'Task'},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.1.12 on 2022-02-11 06:23
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ops', '0021_auto_20211130_1037'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='task',
|
||||
options={'get_latest_by': 'date_created', 'ordering': ('-date_updated',), 'permissions': [('view_taskmonitor', 'Can view task monitor')], 'verbose_name': 'Task'},
|
||||
),
|
||||
]
|
|
@ -38,7 +38,8 @@ class Task(PeriodTaskModelMixin, OrgModelMixin):
|
|||
comment = models.TextField(blank=True, verbose_name=_("Comment"))
|
||||
date_created = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_("Date created"))
|
||||
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
|
||||
latest_adhoc = models.ForeignKey('ops.AdHoc', on_delete=models.SET_NULL, null=True, related_name='task_latest')
|
||||
latest_adhoc = models.ForeignKey('ops.AdHoc', on_delete=models.SET_NULL,
|
||||
null=True, related_name='task_latest')
|
||||
latest_execution = models.ForeignKey('ops.AdHocExecution', on_delete=models.SET_NULL, null=True, related_name='task_latest')
|
||||
total_run_amount = models.IntegerField(default=0)
|
||||
success_run_amount = models.IntegerField(default=0)
|
||||
|
@ -131,7 +132,11 @@ class Task(PeriodTaskModelMixin, OrgModelMixin):
|
|||
db_table = 'ops_task'
|
||||
unique_together = ('name', 'org_id')
|
||||
ordering = ('-date_updated',)
|
||||
verbose_name = _("Task")
|
||||
get_latest_by = 'date_created'
|
||||
permissions = [
|
||||
('view_taskmonitor', _('Can view task monitor'))
|
||||
]
|
||||
|
||||
|
||||
class AdHoc(OrgModelMixin):
|
||||
|
@ -235,6 +240,7 @@ class AdHoc(OrgModelMixin):
|
|||
class Meta:
|
||||
db_table = "ops_adhoc"
|
||||
get_latest_by = 'date_created'
|
||||
verbose_name = _('AdHoc')
|
||||
|
||||
|
||||
class AdHocExecution(OrgModelMixin):
|
||||
|
@ -330,3 +336,4 @@ class AdHocExecution(OrgModelMixin):
|
|||
class Meta:
|
||||
db_table = "ops_adhoc_execution"
|
||||
get_latest_by = 'date_start'
|
||||
verbose_name = _("AdHoc execution")
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#
|
||||
import uuid
|
||||
import os
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
|
|
|
@ -155,3 +155,6 @@ class CommandExecution(OrgModelMixin):
|
|||
self.save()
|
||||
print('-' * 10 + ' ' + ugettext('Task end') + ' ' + '-' * 10)
|
||||
return self.result
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Command execution")
|
||||
|
|
|
@ -32,7 +32,11 @@ class ServerPerformanceMessage(SystemMessage):
|
|||
|
||||
@classmethod
|
||||
def post_insert_to_db(cls, subscription: SystemMsgSubscription):
|
||||
admins = User.objects.filter(role=User.ROLE.ADMIN)
|
||||
from rbac.models import Role, RoleBinding
|
||||
# Todo: 需要更改这里
|
||||
admin_role = Role.BuiltinRole.system_admin.get_role()
|
||||
admins_ids = RoleBinding.objects.filter(role=admin_role).values_list('user_id', flat=True)
|
||||
admins = User.objects.filter(id__in=admins_ids)
|
||||
subscription.users.add(*admins)
|
||||
subscription.receive_backends = [BACKEND.EMAIL]
|
||||
subscription.save()
|
||||
|
|
|
@ -3,15 +3,18 @@
|
|||
from django.views.generic import TemplateView
|
||||
from django.conf import settings
|
||||
|
||||
from common.permissions import IsOrgAdmin, IsOrgAuditor
|
||||
from common.mixins.views import PermissionsMixin
|
||||
from rbac.permissions import RBACPermission
|
||||
|
||||
__all__ = ['CeleryTaskLogView']
|
||||
|
||||
|
||||
class CeleryTaskLogView(PermissionsMixin, TemplateView):
|
||||
template_name = 'ops/celery_task_log.html'
|
||||
permission_classes = [IsOrgAdmin | IsOrgAuditor]
|
||||
permission_classes = [RBACPermission]
|
||||
rbac_perms = {
|
||||
'GET': 'view_tasklog'
|
||||
}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
|
|
@ -2,20 +2,14 @@
|
|||
#
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from rest_framework import status
|
||||
from rest_framework.views import Response
|
||||
from rest_framework_bulk import BulkModelViewSet
|
||||
from rest_framework.generics import RetrieveAPIView
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from common.permissions import IsSuperUserOrAppUser, IsValidUser, UserCanAnyPermCurrentOrg
|
||||
from common.drf.api import JMSBulkRelationModelViewSet
|
||||
from .models import Organization, ROLE
|
||||
from common.permissions import IsValidUser
|
||||
from .models import Organization
|
||||
from .serializers import (
|
||||
OrgSerializer, OrgReadSerializer,
|
||||
OrgRetrieveSerializer, OrgMemberSerializer,
|
||||
OrgMemberAdminSerializer, OrgMemberUserSerializer,
|
||||
CurrentOrgSerializer
|
||||
OrgSerializer, CurrentOrgSerializer
|
||||
)
|
||||
from users.models import User, UserGroup
|
||||
from assets.models import (
|
||||
|
@ -26,8 +20,6 @@ from applications.models import Application
|
|||
from perms.models import AssetPermission, ApplicationPermission
|
||||
from orgs.utils import current_org, tmp_to_root_org
|
||||
from common.utils import get_logger
|
||||
from .filters import OrgMemberRelationFilterSet
|
||||
from .models import OrganizationMember
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
@ -47,23 +39,20 @@ class OrgViewSet(BulkModelViewSet):
|
|||
search_fields = ('name', 'comment')
|
||||
queryset = Organization.objects.all()
|
||||
serializer_class = OrgSerializer
|
||||
permission_classes = (IsSuperUserOrAppUser,)
|
||||
ordering_fields = ('name',)
|
||||
ordering = ('name', )
|
||||
|
||||
def get_serializer_class(self):
|
||||
mapper = {
|
||||
'list': OrgReadSerializer,
|
||||
'retrieve': OrgRetrieveSerializer
|
||||
'list': OrgSerializer,
|
||||
'retrieve': OrgSerializer
|
||||
}
|
||||
return mapper.get(self.action, super().get_serializer_class())
|
||||
|
||||
@tmp_to_root_org()
|
||||
def get_data_from_model(self, org, model):
|
||||
if model == User:
|
||||
data = model.objects.filter(
|
||||
orgs__id=org.id, m2m_org_members__role__in=[ROLE.USER, ROLE.ADMIN, ROLE.AUDITOR]
|
||||
)
|
||||
data = model.get_org_users(org=org)
|
||||
elif model == Node:
|
||||
# 根节点不能手动删除,所以排除检查
|
||||
data = model.objects.filter(org_id=org.id).exclude(parent_key='', key__regex=r'^[0-9]+$')
|
||||
|
@ -91,77 +80,9 @@ class OrgViewSet(BulkModelViewSet):
|
|||
super().perform_destroy(instance)
|
||||
|
||||
|
||||
class OrgMemberRelationBulkViewSet(JMSBulkRelationModelViewSet):
|
||||
permission_classes = (IsSuperUserOrAppUser,)
|
||||
m2m_field = Organization.members.field
|
||||
serializer_class = OrgMemberSerializer
|
||||
filterset_class = OrgMemberRelationFilterSet
|
||||
search_fields = ('user__name', 'user__username', 'org__name')
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
queryset = queryset.exclude(user__role=User.ROLE.APP)
|
||||
return queryset
|
||||
|
||||
def perform_bulk_destroy(self, queryset):
|
||||
objs = list(queryset.all().prefetch_related('user', 'org'))
|
||||
queryset.delete()
|
||||
self.send_m2m_changed_signal(objs, action='post_remove')
|
||||
|
||||
|
||||
class OrgMemberAdminRelationBulkViewSet(JMSBulkRelationModelViewSet):
|
||||
permission_classes = (IsSuperUserOrAppUser,)
|
||||
m2m_field = Organization.members.field
|
||||
serializer_class = OrgMemberAdminSerializer
|
||||
filterset_class = OrgMemberRelationFilterSet
|
||||
search_fields = ('user__name', 'user__username', 'org__name')
|
||||
lookup_field = 'user_id'
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
org_id = self.kwargs.get('org_id')
|
||||
queryset = queryset.filter(org_id=org_id, role=ROLE.ADMIN)
|
||||
return queryset
|
||||
|
||||
def perform_bulk_create(self, serializer):
|
||||
data = serializer.validated_data
|
||||
relations = [OrganizationMember(**i) for i in data]
|
||||
OrganizationMember.objects.bulk_create(relations, ignore_conflicts=True)
|
||||
|
||||
def perform_bulk_destroy(self, queryset):
|
||||
objs = list(queryset.all().prefetch_related('user', 'org'))
|
||||
queryset.delete()
|
||||
self.send_m2m_changed_signal(objs, action='post_remove')
|
||||
|
||||
|
||||
class OrgMemberUserRelationBulkViewSet(JMSBulkRelationModelViewSet):
|
||||
permission_classes = (IsSuperUserOrAppUser,)
|
||||
m2m_field = Organization.members.field
|
||||
serializer_class = OrgMemberUserSerializer
|
||||
filterset_class = OrgMemberRelationFilterSet
|
||||
search_fields = ('user__name', 'user__username', 'org__name')
|
||||
lookup_field = 'user_id'
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
org_id = self.kwargs.get('org_id')
|
||||
queryset = queryset.filter(org_id=org_id, role=ROLE.USER)
|
||||
return queryset
|
||||
|
||||
def perform_bulk_create(self, serializer):
|
||||
data = serializer.validated_data
|
||||
relations = [OrganizationMember(**i) for i in data]
|
||||
OrganizationMember.objects.bulk_create(relations, ignore_conflicts=True)
|
||||
|
||||
def perform_bulk_destroy(self, queryset):
|
||||
objs = list(queryset.all().prefetch_related('user', 'org'))
|
||||
queryset.delete()
|
||||
self.send_m2m_changed_signal(objs, action='post_remove')
|
||||
|
||||
|
||||
class CurrentOrgDetailApi(RetrieveAPIView):
|
||||
serializer_class = CurrentOrgSerializer
|
||||
permission_classes = (IsValidUser, UserCanAnyPermCurrentOrg)
|
||||
permission_classes = (IsValidUser,)
|
||||
|
||||
def get_object(self):
|
||||
return current_org
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class OrgsConfig(AppConfig):
|
||||
name = 'orgs'
|
||||
verbose_name = _('Organizations')
|
||||
|
||||
def ready(self):
|
||||
from . import signals_handler
|
||||
|
|
|
@ -6,11 +6,10 @@ from orgs.utils import current_org, tmp_to_org
|
|||
from common.cache import Cache, IntegerField
|
||||
from common.utils import get_logger
|
||||
from users.models import UserGroup, User
|
||||
from assets.models import Node, AdminUser, SystemUser, Domain, Gateway, Asset
|
||||
from assets.models import Node, SystemUser, Domain, Gateway, Asset
|
||||
from terminal.models import Session
|
||||
from applications.models import Application
|
||||
from perms.models import AssetPermission, ApplicationPermission
|
||||
from .models import OrganizationMember
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
@ -84,13 +83,8 @@ class OrgResourceStatisticsCache(OrgRelatedCache):
|
|||
return SystemUser.objects.filter(type=SystemUser.Type.common).count()
|
||||
|
||||
def compute_users_amount(self):
|
||||
users = User.objects.exclude(role='App')
|
||||
|
||||
if not self.org.is_root():
|
||||
users = users.filter(m2m_org_members__org_id=self.org.id)
|
||||
|
||||
users_amount = users.values('id').distinct().count()
|
||||
return users_amount
|
||||
amount = User.get_org_users(self.org).count()
|
||||
return amount
|
||||
|
||||
def compute_assets_amount(self):
|
||||
if self.org.is_root():
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from .utils import current_org, get_org_from_request
|
||||
from .models import Organization
|
||||
from .utils import get_org_from_request
|
||||
|
||||
|
||||
def org_processor(request):
|
||||
context = {
|
||||
# 'ADMIN_ORGS': request.user.admin_orgs,
|
||||
# 'AUDIT_ORGS': request.user.audit_orgs,
|
||||
'ADMIN_OR_AUDIT_ORGS': Organization.get_user_admin_or_audit_orgs(request.user),
|
||||
'CURRENT_ORG': get_org_from_request(request),
|
||||
# 'HAS_ORG_PERM': current_org.can_admin_by(request.user),
|
||||
}
|
||||
return context
|
||||
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
from django_filters.rest_framework import filterset
|
||||
from django_filters.rest_framework import filters
|
||||
|
||||
from .models import OrganizationMember
|
||||
|
||||
|
||||
class UUIDInFilter(filters.BaseInFilter, filters.UUIDFilter):
|
||||
pass
|
||||
|
||||
|
||||
class OrgMemberRelationFilterSet(filterset.FilterSet):
|
||||
id = UUIDInFilter(field_name='id', lookup_expr='in')
|
||||
|
||||
class Meta:
|
||||
model = OrganizationMember
|
||||
fields = ('org_id', 'user_id', 'org', 'user', 'role', 'id')
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#
|
||||
|
||||
from .utils import get_org_from_request, set_current_org
|
||||
from rbac.models import RoleBinding
|
||||
|
||||
|
||||
class OrgMiddleware:
|
||||
|
@ -14,22 +15,19 @@ class OrgMiddleware:
|
|||
return
|
||||
if not request.user.is_authenticated:
|
||||
return
|
||||
if request.user.is_common_user:
|
||||
return
|
||||
|
||||
org = get_org_from_request(request)
|
||||
if org.can_admin_by(request.user):
|
||||
return
|
||||
if org.can_audit_by(request.user):
|
||||
return
|
||||
admin_orgs = request.user.admin_orgs
|
||||
if admin_orgs:
|
||||
request.session['oid'] = str(admin_orgs[0].id)
|
||||
return
|
||||
audit_orgs = request.user.audit_orgs
|
||||
if audit_orgs:
|
||||
request.session['oid'] = str(audit_orgs[0].id)
|
||||
|
||||
search_org = None if org.is_root() else org
|
||||
has_roles = RoleBinding.objects.filter(user=request.user, org=search_org).exists()
|
||||
if has_roles:
|
||||
return
|
||||
|
||||
roles_bindings = RoleBinding.objects.filter(user=request.user).exclude(org=None)
|
||||
if roles_bindings:
|
||||
org_id = str(list(roles_bindings.values_list('org_id', flat=True))[0])
|
||||
request.session['oid'] = org_id
|
||||
|
||||
def __call__(self, request):
|
||||
self.set_permed_org_if_need(request)
|
||||
org = get_org_from_request(request)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.1.13 on 2021-12-23 11:13
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orgs', '0010_auto_20210219_1241'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='organizationmember',
|
||||
name='role',
|
||||
field=models.CharField(default='User', max_length=16, verbose_name='Role'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,25 @@
|
|||
# Generated by Django 3.1.13 on 2022-01-18 02:54
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('rbac', '0004_auto_20211201_1901'),
|
||||
('orgs', '0011_auto_20211223_1913'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='organization',
|
||||
options={'permissions': (('view_rootorg', 'Can view root org'),), 'verbose_name': 'Organization'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organization',
|
||||
name='members',
|
||||
field=models.ManyToManyField(related_name='orgs', through='rbac.RoleBinding', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
|
@ -1,22 +1,10 @@
|
|||
import uuid
|
||||
from functools import partial
|
||||
from itertools import chain
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import signals
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.utils import lazyproperty, settings
|
||||
from common.const import choices
|
||||
from common.tree import TreeNode
|
||||
from common.db.models import TextChoices
|
||||
|
||||
|
||||
class ROLE(TextChoices):
|
||||
ADMIN = choices.ADMIN, _('Organization administrator')
|
||||
AUDITOR = choices.AUDITOR, _("Organization auditor")
|
||||
USER = choices.USER, _('User')
|
||||
|
||||
|
||||
class Organization(models.Model):
|
||||
|
@ -25,7 +13,7 @@ class Organization(models.Model):
|
|||
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
|
||||
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
|
||||
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
|
||||
members = models.ManyToManyField('users.User', related_name='orgs', through='orgs.OrganizationMember', through_fields=('org', 'user'))
|
||||
members = models.ManyToManyField('users.User', related_name='orgs', through='rbac.RoleBinding', through_fields=('org', 'user'))
|
||||
|
||||
ROOT_ID = '00000000-0000-0000-0000-000000000000'
|
||||
ROOT_NAME = _('GLOBAL')
|
||||
|
@ -35,6 +23,9 @@ class Organization(models.Model):
|
|||
|
||||
class Meta:
|
||||
verbose_name = _("Organization")
|
||||
permissions = (
|
||||
('view_rootorg', _('Can view root org')),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name)
|
||||
|
@ -79,113 +70,9 @@ class Organization(models.Model):
|
|||
def expire_orgs_mapping(cls):
|
||||
cls.orgs_mapping = None
|
||||
|
||||
def get_org_members_by_role(self, role):
|
||||
from users.models import User
|
||||
if not self.is_root():
|
||||
return self.members.filter(m2m_org_members__role=role)
|
||||
users = User.objects.filter(role=role)
|
||||
return users
|
||||
|
||||
@property
|
||||
def users(self):
|
||||
return self.get_org_members_by_role(ROLE.USER)
|
||||
|
||||
@property
|
||||
def admins(self):
|
||||
return self.get_org_members_by_role(ROLE.ADMIN)
|
||||
|
||||
@property
|
||||
def auditors(self):
|
||||
return self.get_org_members_by_role(ROLE.AUDITOR)
|
||||
|
||||
def org_id(self):
|
||||
return self.id
|
||||
|
||||
def get_members(self, exclude=()):
|
||||
from users.models import User
|
||||
if self.is_root():
|
||||
members = User.objects.exclude(role__in=exclude)
|
||||
else:
|
||||
members = self.members.exclude(m2m_org_members__role__in=exclude)
|
||||
return members.exclude(role=User.ROLE.APP).distinct()
|
||||
|
||||
def can_admin_by(self, user):
|
||||
if user.is_superuser:
|
||||
return True
|
||||
if self.admins.filter(id=user.id).exists():
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_audit_by(self, user):
|
||||
if user.is_superuser or user.is_super_auditor:
|
||||
return True
|
||||
if self.can_admin_by(user):
|
||||
return True
|
||||
if self.auditors.filter(id=user.id).exists():
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_use_by(self, user):
|
||||
if user.is_superuser or user.is_super_auditor:
|
||||
return True
|
||||
if self.can_audit_by(user):
|
||||
return True
|
||||
if self.users.filter(id=user.id).exists():
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_any_by(self, user):
|
||||
if user.is_superuser or user.is_super_auditor:
|
||||
return True
|
||||
return self.members.filter(id=user.id).exists()
|
||||
|
||||
@classmethod
|
||||
def get_user_orgs_by_role(cls, user, role):
|
||||
if not isinstance(role, (tuple, list)):
|
||||
role = (role, )
|
||||
|
||||
return cls.objects.filter(
|
||||
m2m_org_members__role__in=role,
|
||||
m2m_org_members__user_id=user.id
|
||||
).distinct()
|
||||
|
||||
@classmethod
|
||||
def get_user_all_orgs(cls, user):
|
||||
return cls.objects.filter(members=user).distinct()
|
||||
|
||||
@classmethod
|
||||
def get_user_admin_orgs(cls, user):
|
||||
if user.is_anonymous:
|
||||
return cls.objects.none()
|
||||
if user.is_superuser:
|
||||
return [cls.root(), *cls.objects.all()]
|
||||
return cls.get_user_orgs_by_role(user, ROLE.ADMIN)
|
||||
|
||||
@classmethod
|
||||
def get_user_user_orgs(cls, user):
|
||||
if user.is_anonymous:
|
||||
return cls.objects.none()
|
||||
return [
|
||||
*cls.get_user_orgs_by_role(user, ROLE.USER),
|
||||
cls.default()
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_user_audit_orgs(cls, user):
|
||||
if user.is_anonymous:
|
||||
return cls.objects.none()
|
||||
if user.is_super_auditor:
|
||||
return [cls.root(), *cls.objects.all()]
|
||||
return cls.get_user_orgs_by_role(user, ROLE.AUDITOR)
|
||||
|
||||
@classmethod
|
||||
def get_user_admin_or_audit_orgs(cls, user):
|
||||
if user.is_anonymous:
|
||||
return cls.objects.none()
|
||||
if user.is_superuser or user.is_super_auditor:
|
||||
return [cls.root(), *cls.objects.all()]
|
||||
return cls.get_user_orgs_by_role(user, (ROLE.AUDITOR, ROLE.ADMIN))
|
||||
|
||||
@classmethod
|
||||
def default(cls):
|
||||
defaults = dict(id=cls.DEFAULT_ID, name=cls.DEFAULT_NAME)
|
||||
|
@ -209,13 +96,18 @@ class Organization(models.Model):
|
|||
|
||||
@lazyproperty
|
||||
def resource_statistics_cache(self):
|
||||
# Todo: 由于 redis 问题,没能获取到
|
||||
return {}
|
||||
from .caches import OrgResourceStatisticsCache
|
||||
return OrgResourceStatisticsCache(self)
|
||||
|
||||
def get_members(self):
|
||||
return self.members.all().distinct()
|
||||
|
||||
def get_total_resources_amount(self):
|
||||
from django.apps import apps
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
summary = {'users.Members': self.members.all().count()}
|
||||
summary = {'users.Members': self.get_members().count()}
|
||||
for app_name, app_config in apps.app_configs.items():
|
||||
models_cls = app_config.get_models()
|
||||
for model in models_cls:
|
||||
|
@ -249,178 +141,24 @@ class Organization(models.Model):
|
|||
return node
|
||||
|
||||
|
||||
def _convert_to_uuid_set(users):
|
||||
rst = set()
|
||||
for user in users:
|
||||
if isinstance(user, models.Model):
|
||||
rst.add(user.id)
|
||||
elif not isinstance(user, uuid.UUID):
|
||||
rst.add(uuid.UUID(user))
|
||||
return rst
|
||||
|
||||
|
||||
def _none2list(*args):
|
||||
return ([] if v is None else v for v in args)
|
||||
|
||||
|
||||
def _users2pks_if_need(users, admins, auditors):
|
||||
pks = []
|
||||
for user in chain(users, admins, auditors):
|
||||
if hasattr(user, 'pk'):
|
||||
pks.append(user.pk)
|
||||
else:
|
||||
pks.append(user)
|
||||
return pks
|
||||
|
||||
|
||||
class UserRoleMapper(dict):
|
||||
def __init__(self, container=set):
|
||||
super().__init__()
|
||||
self.users = container()
|
||||
self.admins = container()
|
||||
self.auditors = container()
|
||||
|
||||
self[ROLE.USER] = self.users
|
||||
self[ROLE.ADMIN] = self.admins
|
||||
self[ROLE.AUDITOR] = self.auditors
|
||||
|
||||
|
||||
class OrgMemberManager(models.Manager):
|
||||
|
||||
def remove_users(self, org, users):
|
||||
from users.models import User
|
||||
pk_set = []
|
||||
for user in users:
|
||||
if hasattr(user, 'pk'):
|
||||
pk_set.append(user.pk)
|
||||
else:
|
||||
pk_set.append(user)
|
||||
|
||||
send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
|
||||
model=User, pk_set=pk_set, using=self.db)
|
||||
send(action="pre_remove")
|
||||
self.filter(org_id=org.id, user_id__in=pk_set).delete()
|
||||
send(action="post_remove")
|
||||
|
||||
def remove_users_by_role(self, org, users=None, admins=None, auditors=None):
|
||||
from users.models import User
|
||||
|
||||
if not any((users, admins, auditors)):
|
||||
return
|
||||
users, admins, auditors = _none2list(users, admins, auditors)
|
||||
|
||||
send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
|
||||
model=User, pk_set=_users2pks_if_need(users, admins, auditors), using=self.db)
|
||||
|
||||
send(action="pre_remove")
|
||||
self.filter(org_id=org.id).filter(
|
||||
Q(user__in=users, role=ROLE.USER) |
|
||||
Q(user__in=admins, role=ROLE.ADMIN) |
|
||||
Q(user__in=auditors, role=ROLE.AUDITOR)
|
||||
).delete()
|
||||
send(action="post_remove")
|
||||
|
||||
def add_users_by_role(self, org, users=None, admins=None, auditors=None):
|
||||
from users.models import User
|
||||
|
||||
if not any((users, admins, auditors)):
|
||||
return
|
||||
users, admins, auditors = _none2list(users, admins, auditors)
|
||||
|
||||
add_mapper = (
|
||||
(users, ROLE.USER),
|
||||
(admins, ROLE.ADMIN),
|
||||
(auditors, ROLE.AUDITOR)
|
||||
)
|
||||
|
||||
oms_add = []
|
||||
for _users, _role in add_mapper:
|
||||
for _user in _users:
|
||||
if isinstance(_user, models.Model):
|
||||
_user = _user.id
|
||||
oms_add.append(self.model(org_id=org.id, user_id=_user, role=_role))
|
||||
|
||||
pk_set = _users2pks_if_need(users, admins, auditors)
|
||||
send = partial(
|
||||
signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
|
||||
model=User, pk_set=pk_set, using=self.db
|
||||
)
|
||||
|
||||
send(action='pre_add')
|
||||
self.bulk_create(oms_add, ignore_conflicts=True)
|
||||
send(action='post_add')
|
||||
|
||||
def _get_remove_add_set(self, new_users, old_users):
|
||||
if new_users is None:
|
||||
return None, None
|
||||
new_users = _convert_to_uuid_set(new_users)
|
||||
return (old_users - new_users), (new_users - old_users)
|
||||
|
||||
def set_user_roles(self, org, user, roles):
|
||||
"""
|
||||
设置某个用户在某个组织里的角色
|
||||
"""
|
||||
old_roles = set(self.filter(org_id=org.id, user=user).values_list('role', flat=True))
|
||||
new_roles = set(roles)
|
||||
|
||||
roles_remove = old_roles - new_roles
|
||||
roles_add = new_roles - old_roles
|
||||
|
||||
to_remove = UserRoleMapper()
|
||||
to_add = UserRoleMapper()
|
||||
|
||||
for role in roles_remove:
|
||||
if role in to_remove:
|
||||
to_remove[role].add(user)
|
||||
for role in roles_add:
|
||||
if role in to_add:
|
||||
to_add[role].add(user)
|
||||
|
||||
# 先添加再移除 (防止用户角色由组织用户->组织管理员时从组织清除用户)
|
||||
self.add_users_by_role(
|
||||
org,
|
||||
to_add.users,
|
||||
to_add.admins,
|
||||
to_add.auditors
|
||||
)
|
||||
|
||||
self.remove_users_by_role(
|
||||
org,
|
||||
to_remove.users,
|
||||
to_remove.admins,
|
||||
to_remove.auditors
|
||||
)
|
||||
|
||||
def set_users_by_role(self, org, users=None, admins=None, auditors=None):
|
||||
"""
|
||||
给组织设置带角色的用户
|
||||
"""
|
||||
|
||||
oms = self.filter(org_id=org.id).values_list('role', 'user_id')
|
||||
|
||||
old_mapper = UserRoleMapper()
|
||||
|
||||
for role, user_id in oms:
|
||||
if role in old_mapper:
|
||||
old_mapper[role].add(user_id)
|
||||
|
||||
users_remove, users_add = self._get_remove_add_set(users, old_mapper.users)
|
||||
admins_remove, admins_add = self._get_remove_add_set(admins, old_mapper.admins)
|
||||
auditors_remove, auditors_add = self._get_remove_add_set(auditors, old_mapper.auditors)
|
||||
|
||||
self.remove_users_by_role(
|
||||
org,
|
||||
users_remove,
|
||||
admins_remove,
|
||||
auditors_remove
|
||||
)
|
||||
|
||||
self.add_users_by_role(
|
||||
org,
|
||||
users_add,
|
||||
admins_add,
|
||||
auditors_add
|
||||
)
|
||||
# class OrgMemberManager(models.Manager):
|
||||
# def remove_users(self, org, users):
|
||||
# from users.models import User
|
||||
# pk_set = []
|
||||
# for user in users:
|
||||
# if hasattr(user, 'pk'):
|
||||
# pk_set.append(user.pk)
|
||||
# else:
|
||||
# pk_set.append(user)
|
||||
#
|
||||
# send = partial(
|
||||
# signals.m2m_changed.send, sender=self.model,
|
||||
# instance=org, reverse=False, model=User,
|
||||
# pk_set=pk_set, using=self.db
|
||||
# )
|
||||
# send(action="pre_remove")
|
||||
# self.filter(org_id=org.id, user_id__in=pk_set).delete()
|
||||
# send(action="post_remove")
|
||||
|
||||
|
||||
class OrganizationMember(models.Model):
|
||||
|
@ -431,16 +169,15 @@ class OrganizationMember(models.Model):
|
|||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
org = models.ForeignKey(Organization, related_name='m2m_org_members', on_delete=models.CASCADE, verbose_name=_('Organization'))
|
||||
user = models.ForeignKey('users.User', related_name='m2m_org_members', on_delete=models.CASCADE, verbose_name=_('User'))
|
||||
role = models.CharField(max_length=16, choices=ROLE.choices, default=ROLE.USER, verbose_name=_("Role"))
|
||||
role = models.CharField(max_length=16, default='User', verbose_name=_("Role"))
|
||||
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created"))
|
||||
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
|
||||
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
|
||||
|
||||
objects = OrgMemberManager()
|
||||
# objects = OrgMemberManager()
|
||||
|
||||
class Meta:
|
||||
unique_together = [('org', 'user', 'role')]
|
||||
db_table = 'orgs_organization_members'
|
||||
|
||||
def __str__(self):
|
||||
return '{} is {}: {}'.format(self.user.name, self.org.name, self.role)
|
||||
return '{} | {}'.format(self.user, self.org)
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
from django.db.models import F
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework import serializers
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from users.models.user import User
|
||||
from common.drf.serializers import BulkModelSerializer
|
||||
from common.db.models import concated_display as display
|
||||
from .models import Organization, OrganizationMember, ROLE
|
||||
from .utils import get_current_org
|
||||
from .models import Organization
|
||||
|
||||
|
||||
class ResourceStatisticsSerializer(serializers.Serializer):
|
||||
|
@ -25,11 +21,7 @@ class ResourceStatisticsSerializer(serializers.Serializer):
|
|||
app_perms_amount = serializers.IntegerField(required=False)
|
||||
|
||||
|
||||
class OrgSerializer(BulkModelSerializer):
|
||||
users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True, required=False)
|
||||
admins = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True, required=False)
|
||||
auditors = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all(), write_only=True, required=False)
|
||||
|
||||
class OrgSerializer(ModelSerializer):
|
||||
resource_statistics = ResourceStatisticsSerializer(source='resource_statistics_cache', read_only=True)
|
||||
|
||||
class Meta:
|
||||
|
@ -38,104 +30,26 @@ class OrgSerializer(BulkModelSerializer):
|
|||
fields_small = fields_mini + [
|
||||
'resource_statistics',
|
||||
'is_default', 'is_root',
|
||||
'date_created',
|
||||
'date_created', 'created_by',
|
||||
'comment', 'created_by',
|
||||
]
|
||||
|
||||
fields_m2m = ['users', 'admins', 'auditors']
|
||||
fields_m2m = []
|
||||
fields = fields_small + fields_m2m
|
||||
read_only_fields = ['created_by', 'date_created']
|
||||
|
||||
def create(self, validated_data):
|
||||
members = self._pop_members(validated_data)
|
||||
instance = Organization.objects.create(**validated_data)
|
||||
OrganizationMember.objects.add_users_by_role(instance, *members)
|
||||
return instance
|
||||
|
||||
def _pop_members(self, validated_data):
|
||||
return (
|
||||
validated_data.pop('users', None),
|
||||
validated_data.pop('admins', None),
|
||||
validated_data.pop('auditors', None)
|
||||
)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
members = self._pop_members(validated_data)
|
||||
for attr, value in validated_data.items():
|
||||
setattr(instance, attr, value)
|
||||
instance.save()
|
||||
OrganizationMember.objects.set_users_by_role(instance, *members)
|
||||
return instance
|
||||
|
||||
|
||||
class OrgReadSerializer(OrgSerializer):
|
||||
pass
|
||||
|
||||
|
||||
class OrgMemberSerializer(BulkModelSerializer):
|
||||
org_display = serializers.CharField(read_only=True)
|
||||
user_display = serializers.CharField(read_only=True)
|
||||
role_display = serializers.CharField(source='get_role_display', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = OrganizationMember
|
||||
fields_mini = ['id']
|
||||
fields_small = fields_mini + [
|
||||
'role', 'role_display'
|
||||
]
|
||||
fields_fk = ['org', 'user', 'org_display', 'user_display',]
|
||||
fields = fields_small + fields_fk
|
||||
use_model_bulk_create = True
|
||||
model_bulk_create_kwargs = {'ignore_conflicts': True}
|
||||
|
||||
def get_unique_together_validators(self):
|
||||
if self.parent:
|
||||
return []
|
||||
return super().get_unique_together_validators()
|
||||
|
||||
@classmethod
|
||||
def setup_eager_loading(cls, queryset):
|
||||
return queryset.annotate(
|
||||
org_display=F('org__name'),
|
||||
user_display=display('user__name', 'user__username')
|
||||
).distinct()
|
||||
|
||||
|
||||
class OrgMemberOldBaseSerializer(BulkModelSerializer):
|
||||
organization = serializers.PrimaryKeyRelatedField(
|
||||
label=_('Organization'), queryset=Organization.objects.all(), required=True, source='org'
|
||||
)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
view = self.context['view']
|
||||
org_id = view.kwargs.get('org_id')
|
||||
if org_id:
|
||||
data['organization'] = org_id
|
||||
return super().to_internal_value(data)
|
||||
|
||||
class Meta:
|
||||
model = OrganizationMember
|
||||
fields = ('id', 'organization', 'user', 'role')
|
||||
|
||||
|
||||
class OrgMemberAdminSerializer(OrgMemberOldBaseSerializer):
|
||||
role = serializers.HiddenField(default=ROLE.ADMIN)
|
||||
|
||||
|
||||
class OrgMemberUserSerializer(OrgMemberOldBaseSerializer):
|
||||
role = serializers.HiddenField(default=ROLE.USER)
|
||||
|
||||
|
||||
class OrgRetrieveSerializer(OrgReadSerializer):
|
||||
admins = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
auditors = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
users = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
|
||||
class Meta(OrgReadSerializer.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class CurrentOrgSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = Organization
|
||||
fields = ['id', 'name', 'is_default', 'is_root', 'comment']
|
||||
|
||||
|
||||
class CurrentOrgDefault:
|
||||
requires_context = False
|
||||
|
||||
def __call__(self, *args):
|
||||
return get_current_org()
|
||||
|
||||
def __repr__(self):
|
||||
return '%s()' % self.__class__.__name__
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
from django.db.models.signals import m2m_changed
|
||||
from django.db.models.signals import post_save, pre_delete, pre_save, post_delete
|
||||
from django.dispatch import receiver
|
||||
|
||||
from orgs.models import Organization, OrganizationMember
|
||||
from orgs.models import Organization
|
||||
from assets.models import Node
|
||||
from perms.models import (AssetPermission, ApplicationPermission)
|
||||
from users.models import UserGroup, User
|
||||
from applications.models import Application
|
||||
from terminal.models import Session
|
||||
from assets.models import Asset, AdminUser, SystemUser, Domain, Gateway
|
||||
from common.const.signals import POST_PREFIX
|
||||
from assets.models import Asset, SystemUser, Domain, Gateway
|
||||
from orgs.caches import OrgResourceStatisticsCache
|
||||
|
||||
|
||||
|
@ -32,20 +30,20 @@ def on_user_delete_refresh_cache(sender, instance, **kwargs):
|
|||
refresh_user_amount_on_user_create_or_delete(instance.id)
|
||||
|
||||
|
||||
@receiver(m2m_changed, sender=OrganizationMember)
|
||||
def on_org_user_changed_refresh_cache(sender, action, instance, reverse, pk_set, **kwargs):
|
||||
if not action.startswith(POST_PREFIX):
|
||||
return
|
||||
|
||||
if reverse:
|
||||
orgs = Organization.objects.filter(id__in=pk_set)
|
||||
else:
|
||||
orgs = [instance]
|
||||
|
||||
for org in orgs:
|
||||
org_cache = OrgResourceStatisticsCache(org)
|
||||
org_cache.expire('users_amount')
|
||||
OrgResourceStatisticsCache(Organization.root()).expire('users_amount')
|
||||
# @receiver(m2m_changed, sender=OrganizationMember)
|
||||
# def on_org_user_changed_refresh_cache(sender, action, instance, reverse, pk_set, **kwargs):
|
||||
# if not action.startswith(POST_PREFIX):
|
||||
# return
|
||||
#
|
||||
# if reverse:
|
||||
# orgs = Organization.objects.filter(id__in=pk_set)
|
||||
# else:
|
||||
# orgs = [instance]
|
||||
#
|
||||
# for org in orgs:
|
||||
# org_cache = OrgResourceStatisticsCache(org)
|
||||
# org_cache.expire('users_amount')
|
||||
# OrgResourceStatisticsCache(Organization.root()).expire('users_amount')
|
||||
|
||||
|
||||
class OrgResourceStatisticsRefreshUtil:
|
||||
|
|
|
@ -10,7 +10,7 @@ from django.db.models.signals import m2m_changed
|
|||
from django.db.models.signals import post_save, pre_delete
|
||||
|
||||
from orgs.utils import tmp_to_org
|
||||
from orgs.models import Organization, OrganizationMember
|
||||
from orgs.models import Organization
|
||||
from orgs.hands import set_current_org, Node, get_current_org
|
||||
from perms.models import (AssetPermission, ApplicationPermission)
|
||||
from users.models import UserGroup, User
|
||||
|
@ -20,6 +20,7 @@ from common.signals import django_ready
|
|||
from common.utils import get_logger
|
||||
from common.utils.connection import RedisPubSub
|
||||
from assets.models import CommandFilterRule
|
||||
from users.signals import post_user_leave_org
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
@ -55,6 +56,7 @@ def subscribe_orgs_mapping_expire(sender, **kwargs):
|
|||
t.start()
|
||||
|
||||
|
||||
# 创建对应的root
|
||||
@receiver(post_save, sender=Organization)
|
||||
def on_org_create_or_update(sender, instance, created=False, **kwargs):
|
||||
# 必须放到最开始, 因为下面调用Node.save方法时会获取当前组织的org_id(即instance.org_id), 如果不过期会找不到
|
||||
|
@ -72,9 +74,6 @@ def on_org_create_or_update(sender, instance, created=False, **kwargs):
|
|||
def on_org_delete(sender, instance, **kwargs):
|
||||
expire_orgs_mapping_for_memory(instance.id)
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Organization)
|
||||
def on_org_delete(sender, instance, **kwargs):
|
||||
# 删除该组织下所有 节点
|
||||
with tmp_to_org(instance):
|
||||
root_node = Node.org_root()
|
||||
|
@ -144,25 +143,6 @@ def _clear_users_from_org(org, users):
|
|||
_remove_users(CommandFilterRule, users, org, user_field_name='reviewers')
|
||||
|
||||
|
||||
@receiver(m2m_changed, sender=OrganizationMember)
|
||||
def on_org_user_changed(action, instance, reverse, pk_set, **kwargs):
|
||||
if action == 'post_remove':
|
||||
if reverse:
|
||||
user = instance
|
||||
org_pk_set = pk_set
|
||||
|
||||
orgs = Organization.objects.filter(id__in=org_pk_set)
|
||||
for org in orgs:
|
||||
if not org.members.filter(id=user.id).exists():
|
||||
_clear_users_from_org(org, user)
|
||||
else:
|
||||
org = instance
|
||||
user_pk_set = pk_set
|
||||
|
||||
leaved_users = set(pk_set) - set(org.members.filter(id__in=user_pk_set).values_list('id', flat=True))
|
||||
_clear_users_from_org(org, leaved_users)
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
@on_transaction_commit
|
||||
def on_user_created_set_default_org(sender, instance, created, **kwargs):
|
||||
|
@ -171,3 +151,9 @@ def on_user_created_set_default_org(sender, instance, created, **kwargs):
|
|||
if instance.orgs.count() > 0:
|
||||
return
|
||||
Organization.default().members.add(instance)
|
||||
|
||||
|
||||
@receiver(post_user_leave_org)
|
||||
def on_user_leave_org(sender, user=None, org=None, **kwargs):
|
||||
logger.debug('User leave org signal recv: {} <> {}'.format(user, org))
|
||||
_clear_users_from_org(org, [user])
|
||||
|
|
|
@ -11,13 +11,6 @@ app_name = 'orgs'
|
|||
router = BulkRouter()
|
||||
|
||||
router.register(r'orgs', api.OrgViewSet, 'org')
|
||||
router.register(r'org-member-relation', api.OrgMemberRelationBulkViewSet, 'org-member-relation')
|
||||
|
||||
router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/admins',
|
||||
api.OrgMemberAdminRelationBulkViewSet, 'membership-admins')
|
||||
router.register(r'orgs/(?P<org_id>[0-9a-zA-Z\-]{36})/membership/users',
|
||||
api.OrgMemberUserRelationBulkViewSet, 'membership-users'),
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('orgs/current/', api.CurrentOrgDetailApi.as_view(), name='current-org-detail'),
|
||||
|
|
|
@ -9,7 +9,6 @@ from applications.models import Application
|
|||
from orgs.mixins.api import OrgRelationMixin
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.utils import current_org
|
||||
from common.permissions import IsOrgAdmin
|
||||
from perms import serializers
|
||||
from perms import models
|
||||
|
||||
|
@ -36,7 +35,6 @@ class RelationMixin(OrgRelationMixin, OrgBulkModelViewSet):
|
|||
class ApplicationPermissionUserRelationViewSet(RelationMixin):
|
||||
serializer_class = serializers.ApplicationPermissionUserRelationSerializer
|
||||
m2m_field = models.ApplicationPermission.users.field
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', "user", "applicationpermission",
|
||||
]
|
||||
|
@ -51,7 +49,6 @@ class ApplicationPermissionUserRelationViewSet(RelationMixin):
|
|||
class ApplicationPermissionUserGroupRelationViewSet(RelationMixin):
|
||||
serializer_class = serializers.ApplicationPermissionUserGroupRelationSerializer
|
||||
m2m_field = models.ApplicationPermission.user_groups.field
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', "usergroup", "applicationpermission"
|
||||
]
|
||||
|
@ -66,7 +63,6 @@ class ApplicationPermissionUserGroupRelationViewSet(RelationMixin):
|
|||
class ApplicationPermissionApplicationRelationViewSet(RelationMixin):
|
||||
serializer_class = serializers.ApplicationPermissionApplicationRelationSerializer
|
||||
m2m_field = models.ApplicationPermission.applications.field
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', 'application', 'applicationpermission',
|
||||
]
|
||||
|
@ -81,7 +77,6 @@ class ApplicationPermissionApplicationRelationViewSet(RelationMixin):
|
|||
class ApplicationPermissionSystemUserRelationViewSet(RelationMixin):
|
||||
serializer_class = serializers.ApplicationPermissionSystemUserRelationSerializer
|
||||
m2m_field = models.ApplicationPermission.system_users.field
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', 'systemuser', 'applicationpermission',
|
||||
]
|
||||
|
@ -100,7 +95,6 @@ class ApplicationPermissionSystemUserRelationViewSet(RelationMixin):
|
|||
|
||||
|
||||
class ApplicationPermissionAllApplicationListApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.ApplicationPermissionAllApplicationSerializer
|
||||
only_fields = serializers.ApplicationPermissionAllApplicationSerializer.Meta.only_fields
|
||||
filterset_fields = ('name',)
|
||||
|
@ -115,7 +109,6 @@ class ApplicationPermissionAllApplicationListApi(generics.ListAPIView):
|
|||
|
||||
|
||||
class ApplicationPermissionAllUserListApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.ApplicationPermissionAllUserSerializer
|
||||
only_fields = serializers.ApplicationPermissionAllUserSerializer.Meta.only_fields
|
||||
filterset_fields = ('username', 'name')
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
from django.db.models import Q
|
||||
from rest_framework.generics import ListAPIView
|
||||
|
||||
from common.permissions import IsOrgAdminOrAppUser
|
||||
from common.mixins.api import CommonApiMixin
|
||||
from applications.models import Application
|
||||
from perms import serializers
|
||||
|
@ -18,11 +17,13 @@ class UserGroupGrantedApplicationsApi(CommonApiMixin, ListAPIView):
|
|||
"""
|
||||
获取用户组直接授权的应用
|
||||
"""
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.AppGrantedSerializer
|
||||
only_fields = serializers.AppGrantedSerializer.Meta.only_fields
|
||||
filterset_fields = ['id', 'name', 'category', 'type', 'comment']
|
||||
search_fields = ['name', 'comment']
|
||||
rbac_perms = {
|
||||
'list': 'perms.view_applicationpermission'
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
user_group_id = self.kwargs.get('pk', '')
|
||||
|
|
|
@ -16,8 +16,7 @@ from perms.utils.application.permission import (
|
|||
get_application_system_user_ids,
|
||||
validate_permission,
|
||||
)
|
||||
from perms.api.asset.user_permission.mixin import RoleAdminMixin, RoleUserMixin
|
||||
from common.permissions import IsOrgAdminOrAppUser
|
||||
from .mixin import RoleAdminMixin, RoleUserMixin
|
||||
from perms.hands import User, SystemUser
|
||||
from perms import serializers
|
||||
|
||||
|
@ -56,7 +55,9 @@ class MyGrantedApplicationSystemUsersApi(RoleUserMixin, GrantedApplicationSystem
|
|||
|
||||
@method_decorator(tmp_to_root_org(), name='get')
|
||||
class ValidateUserApplicationPermissionApi(APIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
rbac_perms = {
|
||||
'GET': 'view_applicationpermission'
|
||||
}
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
user_id = request.query_params.get('user_id', '')
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from common.mixins.api import RoleAdminMixin as _RoleAdminMixin
|
||||
from common.mixins.api import RoleUserMixin as _RoleUserMixin
|
||||
from orgs.utils import tmp_to_root_org
|
||||
|
||||
|
||||
class RoleAdminMixin(_RoleAdminMixin):
|
||||
rbac_perms = (
|
||||
('list', 'perms.view_userapp'),
|
||||
('retrieve', 'perms.view_userapps'),
|
||||
('get_tree', 'perms.view_userapps'),
|
||||
('GET', 'perms.view_userapps'),
|
||||
)
|
||||
|
||||
|
||||
class RoleUserMixin(_RoleUserMixin):
|
||||
rbac_perms = (
|
||||
('list', 'perms.view_myapps'),
|
||||
('retrieve', 'perms.view_myapps'),
|
||||
('get_tree', 'perms.view_myapps'),
|
||||
('GET', 'perms.view_myapps'),
|
||||
)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
with tmp_to_root_org():
|
||||
return super().get(request, *args, **kwargs)
|
|
@ -4,19 +4,15 @@ from perms.filters import AssetPermissionFilter
|
|||
from perms.models import AssetPermission
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from perms import serializers
|
||||
from common.permissions import IsOrgAdmin
|
||||
|
||||
|
||||
__all__ = [
|
||||
'AssetPermissionViewSet',
|
||||
]
|
||||
__all__ = ['AssetPermissionViewSet']
|
||||
|
||||
|
||||
class AssetPermissionViewSet(OrgBulkModelViewSet):
|
||||
"""
|
||||
资产授权列表的增删改查api
|
||||
"""
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
model = AssetPermission
|
||||
serializer_class = serializers.AssetPermissionSerializer
|
||||
filterset_class = AssetPermissionFilter
|
||||
|
|
|
@ -2,15 +2,12 @@
|
|||
#
|
||||
from rest_framework import generics
|
||||
from django.db.models import F, Value
|
||||
from django.db.models import Q
|
||||
from django.db.models.functions import Concat
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from assets.models import Node, Asset
|
||||
from orgs.mixins.api import OrgRelationMixin
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.utils import current_org
|
||||
from common.permissions import IsOrgAdmin
|
||||
from perms import serializers
|
||||
from perms import models
|
||||
from perms.utils.asset.user_permission import UserGrantedAssetsQueryUtils
|
||||
|
@ -36,7 +33,6 @@ class RelationMixin(OrgRelationMixin, OrgBulkModelViewSet):
|
|||
class AssetPermissionUserRelationViewSet(RelationMixin):
|
||||
serializer_class = serializers.AssetPermissionUserRelationSerializer
|
||||
m2m_field = models.AssetPermission.users.field
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', "user", "assetpermission",
|
||||
]
|
||||
|
@ -50,7 +46,6 @@ class AssetPermissionUserRelationViewSet(RelationMixin):
|
|||
|
||||
|
||||
class AssetPermissionAllUserListApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.AssetPermissionAllUserSerializer
|
||||
filterset_fields = ("username", "name")
|
||||
search_fields = filterset_fields
|
||||
|
@ -67,7 +62,6 @@ class AssetPermissionAllUserListApi(generics.ListAPIView):
|
|||
class AssetPermissionUserGroupRelationViewSet(RelationMixin):
|
||||
serializer_class = serializers.AssetPermissionUserGroupRelationSerializer
|
||||
m2m_field = models.AssetPermission.user_groups.field
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', "usergroup", "assetpermission"
|
||||
]
|
||||
|
@ -83,7 +77,6 @@ class AssetPermissionUserGroupRelationViewSet(RelationMixin):
|
|||
class AssetPermissionAssetRelationViewSet(RelationMixin):
|
||||
serializer_class = serializers.AssetPermissionAssetRelationSerializer
|
||||
m2m_field = models.AssetPermission.assets.field
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', 'asset', 'assetpermission',
|
||||
]
|
||||
|
@ -97,7 +90,6 @@ class AssetPermissionAssetRelationViewSet(RelationMixin):
|
|||
|
||||
|
||||
class AssetPermissionAllAssetListApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.AssetPermissionAllAssetSerializer
|
||||
filterset_fields = ("hostname", "ip")
|
||||
search_fields = filterset_fields
|
||||
|
@ -112,7 +104,6 @@ class AssetPermissionAllAssetListApi(generics.ListAPIView):
|
|||
class AssetPermissionNodeRelationViewSet(RelationMixin):
|
||||
serializer_class = serializers.AssetPermissionNodeRelationSerializer
|
||||
m2m_field = models.AssetPermission.nodes.field
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', 'node', 'assetpermission',
|
||||
]
|
||||
|
@ -128,7 +119,6 @@ class AssetPermissionNodeRelationViewSet(RelationMixin):
|
|||
class AssetPermissionSystemUserRelationViewSet(RelationMixin):
|
||||
serializer_class = serializers.AssetPermissionSystemUserRelationSerializer
|
||||
m2m_field = models.AssetPermission.system_users.field
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
filterset_fields = [
|
||||
'id', 'systemuser', 'assetpermission',
|
||||
]
|
||||
|
|
|
@ -6,7 +6,6 @@ from django.db.models import Q
|
|||
from rest_framework.generics import ListAPIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from common.permissions import IsOrgAdminOrAppUser
|
||||
from common.utils import lazyproperty
|
||||
from perms.models import AssetPermission
|
||||
from assets.models import Asset, Node
|
||||
|
@ -32,11 +31,13 @@ class UserGroupMixin:
|
|||
|
||||
|
||||
class UserGroupGrantedAssetsApi(ListAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.AssetGrantedSerializer
|
||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||
filterset_fields = ['hostname', 'ip', 'id', 'comment']
|
||||
search_fields = ['hostname', 'ip', 'comment']
|
||||
rbac_perms = {
|
||||
'list': 'perms.view_userassets'
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
user_group_id = self.kwargs.get('pk', '')
|
||||
|
@ -65,11 +66,13 @@ class UserGroupGrantedAssetsApi(ListAPIView):
|
|||
|
||||
|
||||
class UserGroupGrantedNodeAssetsApi(ListAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.AssetGrantedSerializer
|
||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||
filterset_fields = ['hostname', 'ip', 'id', 'comment']
|
||||
search_fields = ['hostname', 'ip', 'comment']
|
||||
rbac_perms = {
|
||||
'list': 'perms.view_userassets'
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
if getattr(self, 'swagger_fake_view', False):
|
||||
|
@ -119,7 +122,9 @@ class UserGroupGrantedNodeAssetsApi(ListAPIView):
|
|||
|
||||
class UserGroupGrantedNodesApi(ListAPIView):
|
||||
serializer_class = serializers.NodeGrantedSerializer
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
rbac_perms = {
|
||||
'list': 'view_userassets'
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
user_group_id = self.kwargs.get('pk', '')
|
||||
|
@ -131,7 +136,9 @@ class UserGroupGrantedNodesApi(ListAPIView):
|
|||
|
||||
|
||||
class UserGroupGrantedNodeChildrenAsTreeApi(SerializeToTreeNodeMixin, ListAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
rbac_perms = {
|
||||
'list': 'view_userassets'
|
||||
}
|
||||
|
||||
def get_children_nodes(self, parent_key):
|
||||
return Node.objects.filter(parent_key=parent_key)
|
||||
|
|
|
@ -13,7 +13,7 @@ from rest_framework.generics import (
|
|||
|
||||
from orgs.utils import tmp_to_root_org
|
||||
from perms.utils.asset.permission import get_asset_system_user_ids_with_actions_by_user, validate_permission
|
||||
from common.permissions import IsOrgAdminOrAppUser, IsOrgAdmin, IsValidUser
|
||||
from common.permissions import IsValidUser
|
||||
from common.utils import get_logger, lazyproperty
|
||||
|
||||
from perms.hands import User, Asset, SystemUser
|
||||
|
@ -33,8 +33,10 @@ __all__ = [
|
|||
|
||||
@method_decorator(tmp_to_root_org(), name='get')
|
||||
class GetUserAssetPermissionActionsApi(RetrieveAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.ActionsSerializer
|
||||
rbac_perms = {
|
||||
'retrieve': 'perms.view_userassets'
|
||||
}
|
||||
|
||||
def get_user(self):
|
||||
user_id = self.request.query_params.get('user_id', '')
|
||||
|
@ -61,10 +63,9 @@ class GetUserAssetPermissionActionsApi(RetrieveAPIView):
|
|||
|
||||
@method_decorator(tmp_to_root_org(), name='get')
|
||||
class ValidateUserAssetPermissionApi(APIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
|
||||
def get_cache_policy(self):
|
||||
return 0
|
||||
rbac_perms = {
|
||||
'GET': 'perms.view_userassets'
|
||||
}
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
user_id = self.request.query_params.get('user_id', '')
|
||||
|
@ -97,16 +98,16 @@ class ValidateUserAssetPermissionApi(APIView):
|
|||
|
||||
# TODO 删除
|
||||
class RefreshAssetPermissionCacheApi(RetrieveAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
return Response({'msg': True}, status=200)
|
||||
|
||||
|
||||
class UserGrantedAssetSystemUsersForAdminApi(ListAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.AssetSystemUserSerializer
|
||||
only_fields = serializers.AssetSystemUserSerializer.Meta.only_fields
|
||||
rbac_perms = {
|
||||
'list': 'perms.view_userassets'
|
||||
}
|
||||
|
||||
@lazyproperty
|
||||
def user(self):
|
||||
|
@ -142,7 +143,5 @@ class MyGrantedAssetSystemUsersApi(UserGrantedAssetSystemUsersForAdminApi):
|
|||
|
||||
# TODO 删除
|
||||
class UserAssetPermissionsCacheApi(DestroyAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
return Response(status=204)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#
|
||||
from rest_framework.request import Request
|
||||
|
||||
from common.permissions import IsOrgAdminOrAppUser, IsValidUser
|
||||
from common.http import is_true
|
||||
from common.mixins.api import RoleAdminMixin as _RoleAdminMixin
|
||||
from common.mixins.api import RoleUserMixin as _RoleUserMixin
|
||||
|
@ -22,11 +21,21 @@ class PermBaseMixin:
|
|||
|
||||
|
||||
class RoleAdminMixin(PermBaseMixin, _RoleAdminMixin):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
rbac_perms = (
|
||||
('list', 'perms.view_userassets'),
|
||||
('retrieve', 'perms.view_userassets'),
|
||||
('get_tree', 'perms.view_userassets'),
|
||||
('GET', 'perms.view_userassets'),
|
||||
)
|
||||
|
||||
|
||||
class RoleUserMixin(PermBaseMixin, _RoleUserMixin):
|
||||
permission_classes = (IsValidUser,)
|
||||
rbac_perms = (
|
||||
('list', 'perms.view_myassets'),
|
||||
('retrieve', 'perms.view_myassets'),
|
||||
('get_tree', 'perms.view_myassets'),
|
||||
('GET', 'perms.view_myassets'),
|
||||
)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
with tmp_to_root_org():
|
||||
|
|
|
@ -113,7 +113,9 @@ class UserGrantedNodeChildrenAsTreeForAdminApi(RoleAdminMixin, UserGrantedNodeCh
|
|||
|
||||
|
||||
class MyGrantedNodeChildrenAsTreeApi(RoleUserMixin, UserGrantedNodeChildrenMixin, BaseNodeChildrenAsTreeApi):
|
||||
pass
|
||||
def get_permissions(self):
|
||||
permissions = super().get_permissions()
|
||||
return permissions
|
||||
|
||||
|
||||
class UserGrantedNodesForAdminApi(RoleAdminMixin, UserGrantedNodesMixin, BaseGrantedNodeApi):
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from django.db.models import Q
|
||||
from common.permissions import IsOrgAdmin
|
||||
from common.utils import get_object_or_none
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from assets.models import SystemUser
|
||||
|
@ -14,7 +13,6 @@ class BasePermissionViewSet(OrgBulkModelViewSet):
|
|||
'user_id', 'username', 'system_user_id', 'system_user',
|
||||
'user_group_id', 'user_group'
|
||||
]
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def filter_valid(self, queryset):
|
||||
valid_query = self.request.query_params.get('is_valid', None)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class PermsConfig(AppConfig):
|
||||
name = 'perms'
|
||||
verbose_name = _('Permissions')
|
||||
|
||||
def ready(self):
|
||||
super().ready()
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 3.1.13 on 2022-01-04 07:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('perms', '0020_auto_20210910_1103'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='applicationpermission',
|
||||
options={'ordering': ('name',), 'permissions': [('view_myapps', 'Can view my apps'), ('connect_myapps', 'Can connect my apps'), ('view_userapps', 'Can view user apps'), ('view_usergroupapps', 'Can view usergroup apps')], 'verbose_name': 'Application permission'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='assetpermission',
|
||||
options={'ordering': ('name',), 'permissions': [('view_myassets', 'Can view my assets'), ('connect_myassets', 'Can connect my assets'), ('view_userassets', 'Can view user assets'), ('view_usergroupassets', 'Can view usergroup assets')], 'verbose_name': 'Asset permission'},
|
||||
),
|
||||
]
|
|
@ -36,6 +36,12 @@ class ApplicationPermission(BasePermission):
|
|||
unique_together = [('org_id', 'name')]
|
||||
verbose_name = _('Application permission')
|
||||
ordering = ('name',)
|
||||
permissions = [
|
||||
('view_myapps', _('Can view my apps')),
|
||||
('connect_myapps', _('Can connect my apps')),
|
||||
('view_userapps', _('Can view user apps')),
|
||||
('view_usergroupapps', _('Can view usergroup apps')),
|
||||
]
|
||||
|
||||
@property
|
||||
def category_remote_app(self):
|
||||
|
|
|
@ -29,6 +29,12 @@ class AssetPermission(BasePermission):
|
|||
unique_together = [('org_id', 'name')]
|
||||
verbose_name = _("Asset permission")
|
||||
ordering = ('name',)
|
||||
permissions = [
|
||||
('view_myassets', _('Can view my assets')),
|
||||
('connect_myassets', _('Can connect my assets')),
|
||||
('view_userassets', _('Can view user assets')),
|
||||
('view_usergroupassets', _('Can view usergroup assets')),
|
||||
]
|
||||
|
||||
@lazyproperty
|
||||
def users_amount(self):
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue