diff --git a/apps/jumpserver/api.py b/apps/jumpserver/api.py index eb09f7214..2de05b9c6 100644 --- a/apps/jumpserver/api.py +++ b/apps/jumpserver/api.py @@ -214,7 +214,7 @@ class DatesLoginMetricMixin: class IndexApi(DatesLoginMetricMixin, APIView): http_method_names = ['get'] rbac_perms = { - 'GET': 'rbac.view_dashboard' + 'GET': 'rbac.view_audit | rbac.view_console' } def get(self, request, *args, **kwargs): diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 919fc5642..4af31bffb 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -390,7 +390,6 @@ class Config(dict): 'HELP_DOCUMENT_URL': 'http://docs.jumpserver.org', 'HELP_SUPPORT_URL': 'http://www.jumpserver.org/support/', - 'TICKETS_ENABLED': True, 'FORGOT_PASSWORD_URL': '', 'HEALTH_CHECK_TOKEN': '', } diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 4eff9d9fe..794180fd2 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -119,7 +119,6 @@ CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED = CONFIG.CHANGE_AUTH_PLAN_SECURE_MODE_ENABL DATETIME_DISPLAY_FORMAT = '%Y-%m-%d %H:%M:%S' -TICKETS_ENABLED = CONFIG.TICKETS_ENABLED REFERER_CHECK_ENABLED = CONFIG.REFERER_CHECK_ENABLED CONNECTION_TOKEN_ENABLED = CONFIG.CONNECTION_TOKEN_ENABLED diff --git a/apps/orgs/signal_handlers/common.py b/apps/orgs/signal_handlers/common.py index dfd29955d..48576a828 100644 --- a/apps/orgs/signal_handlers/common.py +++ b/apps/orgs/signal_handlers/common.py @@ -14,6 +14,7 @@ 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 +from assets.models import SystemUser from common.const.signals import PRE_REMOVE, POST_REMOVE from common.decorator import on_transaction_commit from common.signals import django_ready @@ -135,7 +136,7 @@ def _clear_users_from_org(org, users): if not users: return - models = (AssetPermission, ApplicationPermission, UserGroup) + models = (AssetPermission, ApplicationPermission, UserGroup, SystemUser) for m in models: _remove_users(m, users, org) diff --git a/apps/perms/serializers/application/permission.py b/apps/perms/serializers/application/permission.py index c7ebdf769..ae65ea869 100644 --- a/apps/perms/serializers/application/permission.py +++ b/apps/perms/serializers/application/permission.py @@ -5,7 +5,7 @@ from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from perms.models import ApplicationPermission +from perms.models import ApplicationPermission, Action from ..base import ActionsField, BasePermissionSerializer __all__ = [ diff --git a/apps/perms/serializers/base.py b/apps/perms/serializers/base.py index 81707dbd1..63cdd8d72 100644 --- a/apps/perms/serializers/base.py +++ b/apps/perms/serializers/base.py @@ -1,6 +1,7 @@ from rest_framework import serializers from perms.models import Action from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from rest_framework.fields import empty __all__ = ['ActionsDisplayField', 'ActionsField', 'BasePermissionSerializer'] @@ -10,6 +11,12 @@ class ActionsField(serializers.MultipleChoiceField): kwargs['choices'] = Action.CHOICES super().__init__(*args, **kwargs) + def run_validation(self, data=empty): + data = super(ActionsField, self).run_validation() + if isinstance(data, list): + data = Action.choices_to_value(value=data) + return data + def to_representation(self, value): return Action.value_to_choices(value) diff --git a/apps/rbac/builtin.py b/apps/rbac/builtin.py index 64a73d546..0fe12195e 100644 --- a/apps/rbac/builtin.py +++ b/apps/rbac/builtin.py @@ -22,6 +22,7 @@ auditor_perms = user_perms + ( ('terminal', 'sessionreplay', 'view,download', 'sessionreplay'), ('terminal', 'session', '*', '*'), ('terminal', 'command', '*', '*'), + ('ops', 'commandexecution', 'view', 'commandexecution') ) diff --git a/apps/rbac/const.py b/apps/rbac/const.py index c575cbfac..9e25279fb 100644 --- a/apps/rbac/const.py +++ b/apps/rbac/const.py @@ -23,6 +23,10 @@ exclude_permissions = ( ('common', 'setting', '*', '*'), ('authentication', 'privatetoken', '*', '*'), + ('authentication', 'accesskey', 'change,delete', 'accesskey'), + ('authentication', 'connectiontoken', 'change,delete', 'connectiontoken'), + ('authentication', 'ssotoken', 'change,delete', 'ssotoken'), + ('authentication', 'superconnectiontoken', 'change,delete', 'superconnectiontoken'), ('users', 'userpasswordhistory', '*', '*'), ('applications', 'applicationuser', '*', '*'), ('applications', 'historicalaccount', '*', '*'), @@ -56,10 +60,14 @@ exclude_permissions = ( ('audits', 'passwordchangelog', 'add,change,delete', 'passwordchangelog'), ('audits', 'userloginlog', 'add,change,delete,change', 'userloginlog'), ('audits', 'ftplog', 'change,delete', 'ftplog'), - ('tickets', 'ticket', '*', '*'), + ('tickets', 'ticketassignee', '*', 'ticketassignee'), + ('tickets', 'ticketflow', 'add,delete', 'ticketflow'), ('tickets', 'comment', 'change,delete', 'comment'), + ('tickets', 'ticket', 'delete', 'ticket'), ('tickets', 'ticketstep', '*', '*'), - ('tickets', 'ticketapprovalrule', '*', '*'), + ('tickets', 'approvalrule', '*', '*'), + ('tickets', 'superticket', 'delete', 'superticket'), + ('tickets', 'ticketsession', 'delete', 'ticketsession'), ('xpack', 'interface', '*', '*'), ('xpack', 'license', '*', '*'), ('xpack', 'syncinstancedetail', 'add,delete,change', 'syncinstancedetail'), @@ -75,13 +83,13 @@ exclude_permissions = ( only_system_permissions = ( + ('assets', 'platform', '*', '*'), ('users', 'user', 'delete', 'user'), ('rbac', 'role', 'delete,add,change', 'role'), ('rbac', 'systemrole', '*', '*'), ('rbac', 'rolebinding', '*', '*'), ('rbac', 'systemrolebinding', '*', '*'), ('rbac', 'orgrole', 'delete,add,change', '*'), - ('rbac', 'orgrolebinding', 'delete,add,change', '*'), ('orgs', 'organization', '*', '*'), ('xpack', 'license', '*', '*'), ('settings', 'setting', '*', '*'), diff --git a/apps/rbac/migrations/0005_auto_20220307_1524.py b/apps/rbac/migrations/0005_auto_20220307_1524.py index 88b9a0f91..afc8ea8ba 100644 --- a/apps/rbac/migrations/0005_auto_20220307_1524.py +++ b/apps/rbac/migrations/0005_auto_20220307_1524.py @@ -12,6 +12,6 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='menupermission', - options={'default_permissions': [], 'permissions': [('view_dashboard', 'Can view resource statistics'), ('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager')], 'verbose_name': 'Menu permission'}, + options={'default_permissions': [], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager')], 'verbose_name': 'Menu permission'}, ), ] diff --git a/apps/rbac/migrations/0006_auto_20220310_0616.py b/apps/rbac/migrations/0006_auto_20220310_0616.py index aa76969bd..395b73f03 100644 --- a/apps/rbac/migrations/0006_auto_20220310_0616.py +++ b/apps/rbac/migrations/0006_auto_20220310_0616.py @@ -12,6 +12,6 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='menupermission', - options={'default_permissions': [], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager'), ('view_dashboard', 'Can view dashboard')], 'verbose_name': 'Menu permission'}, + options={'default_permissions': [], 'permissions': [('view_console', 'Can view console view'), ('view_audit', 'Can view audit view'), ('view_workspace', 'Can view workspace view'), ('view_webterminal', 'Can view web terminal'), ('view_filemanager', 'Can view file manager') ], 'verbose_name': 'Menu permission'}, ), ] diff --git a/apps/rbac/models/menu.py b/apps/rbac/models/menu.py index b13c3d99e..524894664 100644 --- a/apps/rbac/models/menu.py +++ b/apps/rbac/models/menu.py @@ -17,5 +17,4 @@ class MenuPermission(models.Model): ('view_workspace', _('Can view workspace view')), ('view_webterminal', _('Can view web terminal')), ('view_filemanager', _('Can view file manager')), - ('view_dashboard', _('Can view dashboard')), ] diff --git a/apps/rbac/tree.py b/apps/rbac/tree.py index 38ca93d78..68fb3fddc 100644 --- a/apps/rbac/tree.py +++ b/apps/rbac/tree.py @@ -98,7 +98,14 @@ special_pid_mapper = { "perms.view_mydatabaseapp": "my_apps", "perms.connect_mydatabaseapp": "my_apps", "xpack.interface": "view_setting", - "settings.change_terminal": "terminal_node" + "settings.change_terminal": "terminal_node", + "settings.view_setting": "view_setting", + "settings.change_setting": "view_setting", + "rbac.view_console": "view_console", + "rbac.view_audit": "view_audit", + "rbac.view_workspace": "view_workspace", + "rbac.view_webterminal": "view_workspace", + "rbac.view_filemanager": "view_workspace", } verbose_name_mapper = { @@ -115,6 +122,32 @@ xpack_nodes = [ ] +def _sort_action(node): + value = 0 + + if 'view' in node.title: + value += 2 + elif 'add' in node.title: + value += 4 + elif 'change' in node.title: + value += 6 + elif 'delete' in node.title: + value += 8 + else: + value += 10 + return value + + +def sort_nodes(node): + value = 0 + + if node.isParent: + value += 50 + else: + value += _sort_action(node) + return value + + class PermissionTreeUtil: get_permissions: Callable @@ -122,7 +155,7 @@ class PermissionTreeUtil: self.permissions = self.prefetch_permissions(permissions) self.all_permissions = self.prefetch_permissions( Permission.get_permissions(scope) - ).order_by('-codename') + ) self.check_disabled = check_disabled self.total_counts = defaultdict(int) self.checked_counts = defaultdict(int) @@ -272,7 +305,7 @@ class PermissionTreeUtil: # name 要特殊处理,解决 i18n 问题 name = self._get_permission_name(p, content_types_name_mapper) if settings.DEBUG: - name += '({})'.format(p.app_label_codename) + name += '[{}]'.format(p.app_label_codename) title = p.app_label_codename pid = model_id @@ -320,9 +353,12 @@ class PermissionTreeUtil: }, **data } - if not node_data.get('title'): - node_data['title'] = node_data['name'] + node_data['title'] = node_data['id'] node = TreeNode(**node_data) + if settings.DEBUG: + node.name += ('[' + node.id + ']') + if settings.DEBUG: + node.name += ('-' + node.id) node.name += f'({checked_count}/{total_count})' return node @@ -367,12 +403,12 @@ class PermissionTreeUtil: return nodes def create_tree_nodes(self): - nodes = [self._create_root_tree_node()] - perms_nodes = self._create_perms_nodes() - models_nodes = self._create_models_nodes() - apps_nodes = self.create_apps_nodes() - extra_nodes = self._create_extra_nodes() - views_nodes = self._create_views_node() + nodes = self._create_perms_nodes() + nodes += self._create_models_nodes() + nodes += self.create_apps_nodes() + nodes += self._create_extra_nodes() + nodes += self._create_views_node() + nodes += [self._create_root_tree_node()] - nodes += views_nodes + apps_nodes + models_nodes + perms_nodes + extra_nodes + nodes.sort(key=sort_nodes) return nodes diff --git a/apps/settings/api/dingtalk.py b/apps/settings/api/dingtalk.py index 196f259dc..216aeee0b 100644 --- a/apps/settings/api/dingtalk.py +++ b/apps/settings/api/dingtalk.py @@ -11,6 +11,9 @@ from .. import serializers class DingTalkTestingAPI(GenericAPIView): serializer_class = serializers.DingTalkSettingSerializer + rbac_perms = { + 'POST': 'settings.change_auth' + } def post(self, request): serializer = self.serializer_class(data=request.data) diff --git a/apps/settings/api/public.py b/apps/settings/api/public.py index 349955007..314b7c9fa 100644 --- a/apps/settings/api/public.py +++ b/apps/settings/api/public.py @@ -43,7 +43,6 @@ class PublicSettingApi(generics.RetrieveAPIView): "XPACK_LICENSE_INFO": get_xpack_license_info(), "LOGIN_TITLE": self.get_login_title(), "LOGO_URLS": self.get_logo_urls(), - "TICKETS_ENABLED": settings.TICKETS_ENABLED, "PASSWORD_RULE": { 'SECURITY_PASSWORD_MIN_LENGTH': settings.SECURITY_PASSWORD_MIN_LENGTH, 'SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH': settings.SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH, diff --git a/apps/settings/api/settings.py b/apps/settings/api/settings.py index 7864b7c1c..3f81832e0 100644 --- a/apps/settings/api/settings.py +++ b/apps/settings/api/settings.py @@ -41,9 +41,41 @@ class SettingsApi(generics.RetrieveUpdateAPIView): 'tencent': serializers.TencentSMSSettingSerializer, } + rbac_category_permissions = { + # 'all': 'view_setting', + 'basic': 'view_setting', + 'terminal': 'change_terminal', + 'security': 'change_security', + 'ldap': 'change_auth', + 'email': 'change_email', + 'email_content': 'change_email', + 'wecom': 'change_auth', + 'dingtalk': 'change_auth', + 'feishu': 'change_auth', + 'auth': 'change_auth', + 'oidc': 'change_auth', + 'keycloak': 'change_auth', + 'radius': 'change_auth', + 'cas': 'change_auth', + 'sso': 'change_auth', + 'saml2': 'change_auth', + 'clean': 'change_clean', + 'other': 'change_other', + 'sms': 'change_sms', + 'alibaba': 'change_sms', + 'tencent': 'change_sms', + } + def get_queryset(self): return Setting.objects.all() + def check_permissions(self, request): + category = request.query_params.get('category', 'basic') + require_perm = self.rbac_category_permissions.get(category) + if not request.user.has_perm(require_perm): + self.permission_denied(request) + return super().check_permissions(request) + def get_serializer_class(self): category = self.request.query_params.get('category', 'basic') default = serializers.BasicSettingSerializer diff --git a/apps/settings/migrations/0005_auto_20220310_0616.py b/apps/settings/migrations/0005_auto_20220310_0616.py index f29f017c5..5e7d2c747 100644 --- a/apps/settings/migrations/0005_auto_20220310_0616.py +++ b/apps/settings/migrations/0005_auto_20220310_0616.py @@ -12,6 +12,6 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='setting', - options={'permissions': [('change_basic', 'Can change basic setting'), ('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_other', 'Can change other setting'), ('change_terminal_basic_setting', 'Can change terminal basic setting')], 'verbose_name': 'System setting'}, + options={'permissions': [('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_systemmsgsubscription', 'Can sys msg sub setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_interface', 'Can change interface setting'), ('change_license', 'Can change license setting'), ('change_terminal', 'Can change terminal setting'), ('change_other', 'Can change other setting')], 'verbose_name': 'System setting'}, ), ] diff --git a/apps/settings/migrations/0006_auto_20220310_1952.py b/apps/settings/migrations/0006_auto_20220310_1952.py deleted file mode 100644 index 55e4572bc..000000000 --- a/apps/settings/migrations/0006_auto_20220310_1952.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.1.14 on 2022-03-10 11:52 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('settings', '0005_auto_20220310_0616'), - ] - - operations = [ - migrations.AlterModelOptions( - name='setting', - options={'permissions': [('change_basic', 'Can change basic setting'), ('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_other', 'Can change other setting'), ('change_interface', 'Can change interface setting'), ('change_license', 'Can change license setting'), ('change_terminal_basic_setting', 'Can change terminal basic setting')], 'verbose_name': 'System setting'}, - ), - ] diff --git a/apps/settings/migrations/0007_auto_20220310_2006.py b/apps/settings/migrations/0007_auto_20220310_2006.py deleted file mode 100644 index 257abde35..000000000 --- a/apps/settings/migrations/0007_auto_20220310_2006.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.1.14 on 2022-03-10 12:06 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('settings', '0006_auto_20220310_1952'), - ] - - operations = [ - migrations.AlterModelOptions( - name='setting', - options={'permissions': [('change_basic', 'Can change basic setting'), ('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_sys_msg_sub', 'Can sys msg sub setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_interface', 'Can change interface setting'), ('change_license', 'Can change license setting'), ('change_terminal', 'Can change terminal setting'), ('change_other', 'Can change other setting')], 'verbose_name': 'System setting'}, - ), - ] diff --git a/apps/settings/models.py b/apps/settings/models.py index eee1a0d94..9fb07a18f 100644 --- a/apps/settings/models.py +++ b/apps/settings/models.py @@ -139,7 +139,6 @@ class Setting(models.Model): db_table = "settings_setting" verbose_name = _("System setting") permissions = [ - ('change_basic', _('Can change basic setting')), ('change_email', _('Can change email setting')), ('change_auth', _('Can change auth setting')), ('change_systemmsgsubscription', _('Can sys msg sub setting')), diff --git a/apps/settings/serializers/basic.py b/apps/settings/serializers/basic.py index 97ef96cbe..e0672f0df 100644 --- a/apps/settings/serializers/basic.py +++ b/apps/settings/serializers/basic.py @@ -41,7 +41,6 @@ class BasicSettingSerializer(serializers.Serializer): required=False, max_length=1024, allow_blank=True, allow_null=True, label=_("Global organization name"), help_text=_('The name of global organization to display') ) - TICKETS_ENABLED = serializers.BooleanField(required=False, default=True, label=_("Enable tickets")) ANNOUNCEMENT_ENABLED = serializers.BooleanField(label=_('Enable announcement'), default=True) ANNOUNCEMENT = AnnouncementSerializer(label=_("Announcement")) diff --git a/apps/tickets/api/comment.py b/apps/tickets/api/comment.py index dc7740d1a..382ad2c99 100644 --- a/apps/tickets/api/comment.py +++ b/apps/tickets/api/comment.py @@ -4,6 +4,7 @@ from rest_framework import viewsets, mixins from common.exceptions import JMSException from common.utils import lazyproperty +from rbac.permissions import RBACPermission from tickets import serializers from tickets.models import Ticket from tickets.permissions.comment import IsAssignee, IsApplicant, IsSwagger @@ -14,7 +15,7 @@ __all__ = ['CommentViewSet'] class CommentViewSet(mixins.CreateModelMixin, viewsets.ReadOnlyModelViewSet): serializer_class = serializers.CommentSerializer - permission_classes = (IsSwagger | IsAssignee | IsApplicant,) + permission_classes = (RBACPermission| IsSwagger | IsAssignee | IsApplicant) @lazyproperty def ticket(self): diff --git a/apps/tickets/api/ticket.py b/apps/tickets/api/ticket.py index e61dda569..f112dae0c 100644 --- a/apps/tickets/api/ticket.py +++ b/apps/tickets/api/ticket.py @@ -19,7 +19,6 @@ __all__ = ['TicketViewSet', 'TicketFlowViewSet'] class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet): - permission_classes = (IsValidUser,) serializer_class = serializers.TicketDisplaySerializer serializer_classes = { 'open': serializers.TicketApplySerializer, diff --git a/apps/users/api/relation.py b/apps/users/api/relation.py index 40f5566ae..23260a5ae 100644 --- a/apps/users/api/relation.py +++ b/apps/users/api/relation.py @@ -5,12 +5,13 @@ from django.db.models import F from common.drf.api import JMSBulkRelationModelViewSet from .. import serializers -from ..models import User +from ..models import User, UserGroup __all__ = ['UserUserGroupRelationViewSet'] class UserUserGroupRelationViewSet(JMSBulkRelationModelViewSet): + perm_model = UserGroup filterset_fields = ('user', 'usergroup') search_fields = filterset_fields serializer_class = serializers.UserUserGroupRelationSerializer diff --git a/utils/clean_db_content_types.py b/utils/clean_db_content_types.py index 585c72314..6b317bcc5 100644 --- a/utils/clean_db_content_types.py +++ b/utils/clean_db_content_types.py @@ -53,9 +53,11 @@ def clean_db_content_types(): ('applications', 'remoteapp', 'view_remoteapp'), ('settings', 'setting', 'change_terminal_basic_setting'), - ('rbac', 'menupermission', 'view_resourcestatistics'), - - + ('settings', 'setting', 'change_sys_msg_sub'), + ('settings', 'setting', 'change_basic'), + ('rbac', 'menupermission', 'view_userview'), + ('rbac', 'menupermission', 'view_adminview'), + ('rbac', 'menupermission', 'view_auditview'), ] for app, model, codename in permissions_delete_required: print('delete {}.{} ({})'.format(app, codename, model))