diff --git a/Dockerfile b/Dockerfile index a013c074d..421f95b20 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,6 @@ WORKDIR /opt/jumpserver ADD . . RUN cd utils && bash -ixeu build.sh -# 构建运行时环境 FROM python:3.8-slim ARG PIP_MIRROR=https://pypi.douban.com/simple ENV PIP_MIRROR=$PIP_MIRROR @@ -17,38 +16,66 @@ ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR WORKDIR /opt/jumpserver -COPY ./requirements/deb_requirements.txt ./requirements/deb_requirements.txt +ARG BUILD_DEPENDENCIES=" \ + g++ \ + make \ + pkg-config" + +ARG DEPENDENCIES=" \ + default-libmysqlclient-dev \ + freetds-dev \ + libpq-dev \ + libffi-dev \ + libldap2-dev \ + libsasl2-dev \ + libxml2-dev \ + libxmlsec1-dev \ + libxmlsec1-openssl \ + libaio-dev \ + sshpass" + +ARG TOOLS=" \ + curl \ + default-mysql-client \ + iproute2 \ + iputils-ping \ + locales \ + procps \ + redis-tools \ + telnet \ + vim \ + wget" + RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list \ && sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list \ && apt update \ - && apt -y install telnet iproute2 redis-tools default-mysql-client vim wget curl locales procps \ - && apt -y install $(cat requirements/deb_requirements.txt) \ - && rm -rf /var/lib/apt/lists/* \ + && apt -y install ${BUILD_DEPENDENCIES} \ + && apt -y install ${DEPENDENCIES} \ + && apt -y install ${TOOLS} \ && localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 \ && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ - && sed -i "s@# alias l@alias l@g" ~/.bashrc \ - && echo "set mouse-=a" > ~/.vimrc - -COPY ./requirements/requirements.txt ./requirements/requirements.txt -RUN pip install --upgrade pip==20.2.4 setuptools==49.6.0 wheel==0.34.2 -i ${PIP_MIRROR} \ - && pip install --no-cache-dir $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \ - && pip install --no-cache-dir -r requirements/requirements.txt -i ${PIP_MIRROR} \ - && rm -rf ~/.cache/pip - -COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver -RUN mkdir -p /root/.ssh/ \ + && mkdir -p /root/.ssh/ \ && echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config \ + && sed -i "s@# alias l@alias l@g" ~/.bashrc \ + && echo "set mouse-=a" > ~/.vimrc \ + && rm -rf /var/lib/apt/lists/* \ && mv /bin/sh /bin/sh.bak \ && ln -s /bin/bash /bin/sh RUN mkdir -p /opt/jumpserver/oracle/ \ - && wget https://download.jumpserver.org/public/instantclient-basiclite-linux.x64-21.1.0.0.0.tar > /dev/null \ + && wget https://download.jumpserver.org/public/instantclient-basiclite-linux.x64-21.1.0.0.0.tar \ && tar xf instantclient-basiclite-linux.x64-21.1.0.0.0.tar -C /opt/jumpserver/oracle/ \ && echo "/opt/jumpserver/oracle/instantclient_21_1" > /etc/ld.so.conf.d/oracle-instantclient.conf \ && ldconfig \ && rm -f instantclient-basiclite-linux.x64-21.1.0.0.0.tar -RUN echo > config.yml +COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver + +RUN echo > config.yml \ + && pip install --upgrade pip==20.2.4 setuptools==49.6.0 wheel==0.34.2 -i ${PIP_MIRROR} \ + && pip install --no-cache-dir $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \ + && pip install --no-cache-dir -r requirements/requirements.txt -i ${PIP_MIRROR} \ + && rm -rf ~/.cache/pip VOLUME /opt/jumpserver/data VOLUME /opt/jumpserver/logs diff --git a/apps/applications/const.py b/apps/applications/const.py index 76fc51e33..8436f3ebd 100644 --- a/apps/applications/const.py +++ b/apps/applications/const.py @@ -13,13 +13,17 @@ class AppCategory(models.TextChoices): def get_label(cls, category): return dict(cls.choices).get(category, '') + @classmethod + def is_xpack(cls, category): + return category in ['remote_app'] + class AppType(models.TextChoices): # db category mysql = 'mysql', 'MySQL' + mariadb = 'mariadb', 'MariaDB' oracle = 'oracle', 'Oracle' pgsql = 'postgresql', 'PostgreSQL' - mariadb = 'mariadb', 'MariaDB' sqlserver = 'sqlserver', 'SQLServer' redis = 'redis', 'Redis' mongodb = 'mongodb', 'MongoDB' @@ -37,9 +41,13 @@ class AppType(models.TextChoices): def category_types_mapper(cls): return { AppCategory.db: [ - cls.mysql, cls.oracle, cls.pgsql, cls.mariadb, cls.sqlserver, cls.redis, cls.mongodb + cls.mysql, cls.oracle, cls.pgsql, cls.mariadb, + cls.sqlserver, cls.redis, cls.mongodb + ], + AppCategory.remote_app: [ + cls.chrome, cls.mysql_workbench, + cls.vmware_client, cls.custom ], - AppCategory.remote_app: [cls.chrome, cls.mysql_workbench, cls.vmware_client, cls.custom], AppCategory.cloud: [cls.k8s] } @@ -66,3 +74,12 @@ class AppType(models.TextChoices): @classmethod def cloud_types(cls): return [tp.value for tp in cls.category_types_mapper()[AppCategory.cloud]] + + @classmethod + def is_xpack(cls, tp): + tp_category_mapper = cls.type_category_mapper() + category = tp_category_mapper[tp] + + if AppCategory.is_xpack(category): + return True + return tp in ['oracle', 'postgresql', 'sqlserver'] diff --git a/apps/applications/models/application.py b/apps/applications/models/application.py index 467a5de39..fa8125887 100644 --- a/apps/applications/models/application.py +++ b/apps/applications/models/application.py @@ -3,6 +3,7 @@ from urllib.parse import urlencode, parse_qsl from django.db import models from django.utils.translation import ugettext_lazy as _ +from django.conf import settings from orgs.mixins.models import OrgModelMixin from common.mixins import CommonModelMixin @@ -79,6 +80,8 @@ class ApplicationTreeNodeMixin: nodes = [] categories = const.AppType.category_types_mapper().keys() for category in categories: + if not settings.XPACK_ENABLED and const.AppCategory.is_xpack(category): + continue i = cls.create_tree_id(pid, 'category', category.value) node = cls.create_choice_node( category, i, pid=pid, tp='category', @@ -97,6 +100,8 @@ class ApplicationTreeNodeMixin: type_category_mapper = const.AppType.type_category_mapper() types = const.AppType.type_category_mapper().keys() for tp in types: + if not settings.XPACK_ENABLED and const.AppType.is_xpack(tp): + continue category = type_category_mapper.get(tp) pid = cls.create_tree_id(pid, 'category', category.value) i = cls.create_tree_id(pid, 'type', tp.value) @@ -155,6 +160,8 @@ class ApplicationTreeNodeMixin: # 应用的节点 for app in queryset: + if not settings.XPACK_ENABLED and const.AppType.is_xpack(app.type): + continue node = app.as_tree_node(root_node.id) tree_nodes.append(node) return tree_nodes diff --git a/apps/audits/api.py b/apps/audits/api.py index 9a023e3dd..d57682ee3 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -131,6 +131,9 @@ class CommandExecutionHostRelationViewSet(OrgRelationMixin, OrgBulkModelViewSet) ] search_fields = ('asset__hostname', ) http_method_names = ['options', 'get'] + rbac_perms = { + 'GET': 'audits.view_commandexecution' + } def get_queryset(self): queryset = super().get_queryset() diff --git a/apps/authentication/mfa/sms.py b/apps/authentication/mfa/sms.py index 670e49378..d23b68766 100644 --- a/apps/authentication/mfa/sms.py +++ b/apps/authentication/mfa/sms.py @@ -44,13 +44,13 @@ class MFASms(BaseMFA): return settings.SMS_ENABLED def get_enable_url(self) -> str: - return '/ui/#/users/profile/?activeTab=ProfileUpdate' + return '/ui/#/profile/setting?activeTab=ProfileUpdate' def can_disable(self) -> bool: return True def disable(self): - return '/ui/#/users/profile/?activeTab=ProfileUpdate' + return '/ui/#/profile/setting?activeTab=ProfileUpdate' @staticmethod def help_text_of_enable(): @@ -61,4 +61,4 @@ class MFASms(BaseMFA): return _("Clear phone number to disable") def get_disable_url(self) -> str: - return '/ui/#/users/profile/?activeTab=ProfileUpdate' + return '/ui/#/profile/setting?activeTab=ProfileUpdate' diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 2eaac4eff..72474d430 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:675f93d2cc6b2049fdafc7f6b70edb8f73bbe132de9b91e98f2ec7acb2e89620 -size 104134 +oid sha256:a8c1155ea28b70a0eb06aa39ab6ae04619cd30d02f59698fadaa6068d91a7900 +size 104348 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index fb87ca863..a0c2964c4 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-03-14 15:58+0800\n" +"POT-Creation-Date: 2022-03-15 19:46+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -305,10 +305,14 @@ msgstr "版本" msgid "Application account" msgstr "应用账号" -#: applications/models/account.py:26 applications/models/account.py:27 +#: applications/models/account.py:26 msgid "Can view application account secret" msgstr "可以查看应用账号密码" +#: applications/models/account.py:27 +msgid "Can change application account secret" +msgstr "可以查看应用账号密码" + #: applications/models/application.py:204 #: applications/serializers/application.py:99 assets/models/label.py:21 #: perms/models/application_permission.py:21 @@ -1379,7 +1383,7 @@ msgstr "日志审计" #: audits/models.py:27 audits/models.py:57 #: authentication/templates/authentication/_access_key_modal.html:65 -#: rbac/tree.py:273 users/templates/users/user_asset_permission.html:128 +#: rbac/tree.py:158 users/templates/users/user_asset_permission.html:128 #: users/templates/users/user_database_app_permission.html:111 msgid "Delete" msgstr "删除" @@ -1433,11 +1437,11 @@ msgstr "文件管理" #: audits/models.py:55 #: authentication/templates/authentication/_access_key_modal.html:22 -#: rbac/tree.py:270 +#: rbac/tree.py:155 msgid "Create" msgstr "创建" -#: audits/models.py:56 rbac/tree.py:272 templates/_csv_import_export.html:18 +#: audits/models.py:56 rbac/tree.py:157 templates/_csv_import_export.html:18 #: templates/_csv_update_modal.html:6 #: users/templates/users/user_asset_permission.html:127 #: users/templates/users/user_database_app_permission.html:110 @@ -2117,7 +2121,7 @@ msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 #: settings/serializers/security.py:39 users/models/user.py:469 -#: users/serializers/profile.py:111 users/templates/users/mfa_setting.html:60 +#: users/serializers/profile.py:111 users/templates/users/mfa_setting.html:61 #: users/templates/users/user_verify_mfa.html:36 msgid "Disable" msgstr "禁用" @@ -2125,7 +2129,7 @@ msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 #: users/models/user.py:470 users/serializers/profile.py:112 #: users/templates/users/mfa_setting.html:26 -#: users/templates/users/mfa_setting.html:67 +#: users/templates/users/mfa_setting.html:68 msgid "Enable" msgstr "启用" @@ -2679,7 +2683,7 @@ msgstr "站内信" msgid "Waiting task start" msgstr "等待任务开始" -#: ops/api/command.py:61 +#: ops/api/command.py:56 msgid "Not has host {} permission" msgstr "没有该主机 {} 权限" @@ -2767,7 +2771,7 @@ msgstr "创建者" #: ops/models/adhoc.py:243 msgid "AdHoc" -msgstr "" +msgstr "任务各版本" #: ops/models/adhoc.py:252 msgid "Task display" @@ -2800,7 +2804,7 @@ msgstr "汇总" #: ops/models/adhoc.py:339 msgid "AdHoc execution" -msgstr "命令执行" +msgstr "任务执行历史" #: ops/models/command.py:32 msgid "Date finished" @@ -2874,7 +2878,7 @@ msgstr "当前组织 ({}) 不能被删除" msgid "The organization have resource ({}) cannot be deleted" msgstr "组织存在资源 ({}) 不能被删除" -#: orgs/apps.py:7 rbac/tree.py:112 +#: orgs/apps.py:7 rbac/tree.py:111 msgid "App organizations" msgstr "组织管理" @@ -2917,11 +2921,15 @@ msgstr "可以查看授权给用户的应用" msgid "Permed application" msgstr "授权的应用" -#: perms/models/application_permission.py:117 +#: perms/models/application_permission.py:115 +msgid "Can view my apps" +msgstr "可以查看我的应用" + +#: perms/models/application_permission.py:116 msgid "Can view user apps" msgstr "可以查看用户授权的应用" -#: perms/models/application_permission.py:118 +#: perms/models/application_permission.py:117 msgid "Can view usergroup apps" msgstr "可以查看用户组授权的应用" @@ -2950,14 +2958,10 @@ msgid "Can view my assets" msgstr "可以查看我的资产" #: perms/models/asset_permission.py:188 -msgid "Can connect my assets" -msgstr "可以连接我的资产" - -#: perms/models/asset_permission.py:189 msgid "Can view user assets" msgstr "可以查看用户授权的资产" -#: perms/models/asset_permission.py:190 +#: perms/models/asset_permission.py:189 msgid "Can view usergroup assets" msgstr "可以查看用户组授权的资产" @@ -3173,7 +3177,7 @@ msgstr "Web终端" msgid "Can view file manager" msgstr "文件管理" -#: rbac/models/permission.py:22 +#: rbac/models/permission.py:26 msgid "Permission" msgstr "授权" @@ -3299,15 +3303,15 @@ msgstr "我的资产" msgid "My apps" msgstr "我的应用" -#: rbac/tree.py:113 +#: rbac/tree.py:112 msgid "Ticket comment" msgstr "工单评论" -#: rbac/tree.py:114 +#: rbac/tree.py:113 msgid "Common setting" msgstr "一般设置" -#: rbac/tree.py:271 +#: rbac/tree.py:156 msgid "View" msgstr "查看" @@ -4962,6 +4966,10 @@ msgstr "HTTP端口" msgid "Terminal" msgstr "终端" +#: terminal/models/terminal.py:185 +msgid "Can view terminal config" +msgstr "可以查看终端配置" + #: terminal/notifications.py:22 msgid "Sessions" msgstr "会话管理" @@ -6799,11 +6807,11 @@ msgstr "退出页面logo" msgid "Interface setting" msgstr "界面设置" -#: xpack/plugins/license/api.py:41 +#: xpack/plugins/license/api.py:43 msgid "License import successfully" msgstr "许可证导入成功" -#: xpack/plugins/license/api.py:42 +#: xpack/plugins/license/api.py:44 msgid "License is invalid" msgstr "无效的许可证" @@ -6827,6 +6835,9 @@ msgstr "旗舰版" msgid "Community edition" msgstr "社区版" +#~ msgid "Can connect my assets" +#~ msgstr "可以连接我的资产" + #~ msgid "Can view dashboard" #~ msgstr "仪表盘" diff --git a/apps/ops/test_utils.py b/apps/ops/test_utils.py index 5e5905519..96190dee0 100644 --- a/apps/ops/test_utils.py +++ b/apps/ops/test_utils.py @@ -6,7 +6,6 @@ import os from django.test import TestCase from ops.models import Task, AdHoc -from ops.utils import run_adhoc_object class TestRunAdHoc(TestCase): diff --git a/apps/perms/api/asset/user_permission/common.py b/apps/perms/api/asset/user_permission/common.py index 609fb52c3..1594cedb9 100644 --- a/apps/perms/api/asset/user_permission/common.py +++ b/apps/perms/api/asset/user_permission/common.py @@ -22,7 +22,6 @@ from perms import serializers logger = get_logger(__name__) __all__ = [ - 'RefreshAssetPermissionCacheApi', 'UserGrantedAssetSystemUsersForAdminApi', 'ValidateUserAssetPermissionApi', 'GetUserAssetPermissionActionsApi', @@ -97,12 +96,6 @@ class ValidateUserAssetPermissionApi(APIView): return Response(data, status=status_code) -# TODO 删除 -class RefreshAssetPermissionCacheApi(RetrieveAPIView): - def retrieve(self, request, *args, **kwargs): - return Response({'msg': True}, status=200) - - class UserGrantedAssetSystemUsersForAdminApi(ListAPIView): serializer_class = serializers.AssetSystemUserSerializer only_fields = serializers.AssetSystemUserSerializer.Meta.only_fields diff --git a/apps/perms/models/application_permission.py b/apps/perms/models/application_permission.py index d5d02faee..7cefec448 100644 --- a/apps/perms/models/application_permission.py +++ b/apps/perms/models/application_permission.py @@ -112,7 +112,7 @@ class PermedApplication(Application): verbose_name = _('Permed application') default_permissions = [] permissions = [ - ('view_myapps', 'Can view my apps'), + ('view_myapps', _('Can view my apps')), ('view_userapps', _('Can view user apps')), ('view_usergroupapps', _('Can view usergroup apps')), ] diff --git a/apps/perms/serializers/base.py b/apps/perms/serializers/base.py index 63cdd8d72..3d48447fb 100644 --- a/apps/perms/serializers/base.py +++ b/apps/perms/serializers/base.py @@ -12,7 +12,7 @@ class ActionsField(serializers.MultipleChoiceField): super().__init__(*args, **kwargs) def run_validation(self, data=empty): - data = super(ActionsField, self).run_validation() + data = super(ActionsField, self).run_validation(data) if isinstance(data, list): data = Action.choices_to_value(value=data) return data diff --git a/apps/perms/urls/asset_permission.py b/apps/perms/urls/asset_permission.py index 7b251dea7..f24b1b8ba 100644 --- a/apps/perms/urls/asset_permission.py +++ b/apps/perms/urls/asset_permission.py @@ -100,8 +100,6 @@ permission_urlpatterns = [ path('user/validate/', api.ValidateUserAssetPermissionApi.as_view(), name='validate-user-asset-permission'), path('user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'), - # 刷新缓存 - path('cache/refresh/', api.RefreshAssetPermissionCacheApi.as_view(), name='refresh-asset-permission-cache'), ] asset_permission_urlpatterns = [ diff --git a/apps/rbac/const.py b/apps/rbac/const.py index 88adf1199..d7f58e841 100644 --- a/apps/rbac/const.py +++ b/apps/rbac/const.py @@ -55,7 +55,7 @@ exclude_permissions = ( ('ops', 'task', 'add,change', 'task'), ('ops', 'commandexecution', 'delete,change', 'commandexecution'), ('orgs', 'organizationmember', '*', '*'), - ('settings', 'setting', 'add,delete', 'setting'), + ('settings', 'setting', 'add,change,delete', 'setting'), ('audits', 'operatelog', 'add,delete,change', 'operatelog'), ('audits', 'passwordchangelog', 'add,change,delete', 'passwordchangelog'), ('audits', 'userloginlog', 'add,change,delete,change', 'userloginlog'), @@ -71,15 +71,16 @@ exclude_permissions = ( ('xpack', 'interface', '*', '*'), ('xpack', 'license', '*', '*'), ('xpack', 'syncinstancedetail', 'add,delete,change', 'syncinstancedetail'), - ('xpack', 'syncinstancetaskexecution', 'add,delete,change', 'syncinstancetaskexecution'), - ('xpack', 'changeauthplanexecution', 'add,delete,change', 'changeauthplanexecution'), + ('xpack', 'syncinstancetaskexecution', 'delete,change', 'syncinstancetaskexecution'), + ('xpack', 'changeauthplanexecution', 'delete,change', 'changeauthplanexecution'), ('xpack', 'changeauthplantask', 'add,delete', 'changeauthplantask'), ('common', 'permission', 'add,delete,view,change', 'permission'), ('terminal', 'command', 'delete,change', 'command'), ('terminal', 'status', 'delete,change', 'status'), ('terminal', 'sessionjoinrecord', 'delete', 'sessionjoinrecord'), ('terminal', 'sessionreplay', 'add,change,delete', 'sessionreplay'), - ('terminal', 'session', 'delete', 'session'), + ('terminal', 'sessionsharing', 'view,add,change,delete', 'sessionsharing'), + ('terminal', 'session', 'delete,share', 'session'), ('terminal', 'session', 'delete,change', 'command'), ) diff --git a/apps/rbac/tree.py b/apps/rbac/tree.py index 092f6ce90..ee742d865 100644 --- a/apps/rbac/tree.py +++ b/apps/rbac/tree.py @@ -99,7 +99,6 @@ special_pid_mapper = { "xpack.interface": "view_setting", "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", @@ -151,6 +150,21 @@ def sort_nodes(node): class PermissionTreeUtil: get_permissions: Callable + action_mapper = { + 'add': ugettext('Create'), + 'view': ugettext('View'), + 'change': ugettext('Update'), + 'delete': ugettext('Delete') + } + action_icon = { + 'add': 'add', + 'view': 'view', + 'change': 'change', + 'delete': 'delete', + 'invite': 'invite', + 'match': 'match', + 'remove': 'remove' + } def __init__(self, permissions, scope, check_disabled=False): self.permissions = self.prefetch_permissions(permissions) @@ -262,38 +276,17 @@ class PermissionTreeUtil: nodes.append(node) return nodes - @staticmethod - def _get_permission_name(p, content_types_name_mapper): - p: Permission - code_name = p.codename - action_mapper = { - 'add': ugettext('Create'), - 'view': ugettext('View'), - 'change': ugettext('Update'), - 'delete': ugettext('Delete') - } - name = '' - ct = '' - if 'add_' in p.codename: - name = action_mapper['add'] - ct = code_name.replace('add_', '') - elif 'view_' in p.codename: - name = action_mapper['view'] - ct = code_name.replace('view_', '') - elif 'change_' in p.codename: - name = action_mapper['change'] - ct = code_name.replace('change_', '') - elif 'delete' in code_name: - name = action_mapper['delete'] - ct = code_name.replace('delete_', '') - - app_model = '%s.%s' % (p.content_type.app_label, ct) - if app_model in content_types_name_mapper: - name += content_types_name_mapper[app_model] + def _get_permission_name_icon(self, p: Permission, content_types_name_mapper: dict): + action, resource = p.codename.split('_', 1) + app_model = '%s.%s' % (p.content_type.app_label, resource) + if action in self.action_mapper and app_model in content_types_name_mapper: + action_name = self.action_mapper[action] + name = action_name + content_types_name_mapper[app_model] else: name = gettext(p.name) - name = name.replace('Can ', '').replace('可以', '') - return name + icon = self.action_icon.get(action, 'file') + name = name.replace('Can ', '').replace('可以', '') + return name, icon def _create_perms_nodes(self): permissions_id = self.permissions.values_list('id', flat=True) @@ -306,7 +299,7 @@ class PermissionTreeUtil: if not self._check_model_xpack(model_id): continue # name 要特殊处理,解决 i18n 问题 - name = self._get_permission_name(p, content_types_name_mapper) + name, icon = self._get_permission_name_icon(p, content_types_name_mapper) if settings.DEBUG: name += '[{}]'.format(p.app_label_codename) @@ -328,7 +321,7 @@ class PermissionTreeUtil: 'pId': pid, 'isParent': False, 'chkDisabled': self.check_disabled, - 'iconSkin': 'file', + 'iconSkin': icon, 'checked': p.id in permissions_id, 'open': False, 'meta': { diff --git a/apps/settings/api/alibaba_sms.py b/apps/settings/api/alibaba_sms.py index b00487d51..8240ba0e0 100644 --- a/apps/settings/api/alibaba_sms.py +++ b/apps/settings/api/alibaba_sms.py @@ -14,7 +14,7 @@ from .. import serializers class AlibabaSMSTestingAPI(GenericAPIView): serializer_class = serializers.AlibabaSMSSettingSerializer rbac_perms = { - 'POST': 'settings.change_setting' + 'POST': 'settings.change_sms' } def post(self, request): diff --git a/apps/settings/api/email.py b/apps/settings/api/email.py index 78a406b24..495e4a15c 100644 --- a/apps/settings/api/email.py +++ b/apps/settings/api/email.py @@ -19,7 +19,7 @@ class MailTestingAPI(APIView): serializer_class = serializers.MailTestSerializer success_message = _("Test mail sent to {}, please check") rbac_perms = { - 'POST': 'settings.change_setting' + 'POST': 'settings.change_email' } def post(self, request): diff --git a/apps/settings/api/feishu.py b/apps/settings/api/feishu.py index 07d1d4ebd..ed3b51f9e 100644 --- a/apps/settings/api/feishu.py +++ b/apps/settings/api/feishu.py @@ -13,7 +13,7 @@ from .. import serializers class FeiShuTestingAPI(GenericAPIView): serializer_class = serializers.FeiShuSettingSerializer rbac_perms = { - 'POST': 'settings.change_setting' + 'POST': 'settings.change_auth' } def post(self, request): diff --git a/apps/settings/api/tencent_sms.py b/apps/settings/api/tencent_sms.py index 7b3d41061..83a87a474 100644 --- a/apps/settings/api/tencent_sms.py +++ b/apps/settings/api/tencent_sms.py @@ -16,7 +16,7 @@ from .. import serializers class TencentSMSTestingAPI(GenericAPIView): serializer_class = serializers.TencentSMSSettingSerializer rbac_perms = { - 'POST': 'settings.change_setting' + 'POST': 'settings.change_sms' } def post(self, request): diff --git a/apps/settings/api/wecom.py b/apps/settings/api/wecom.py index 622fde923..7fb4a0aed 100644 --- a/apps/settings/api/wecom.py +++ b/apps/settings/api/wecom.py @@ -13,7 +13,7 @@ from .. import serializers class WeComTestingAPI(GenericAPIView): serializer_class = serializers.WeComSettingSerializer rbac_perms = { - 'POST': 'settings.change_setting' + 'POST': 'settings.change_auth' } def post(self, request): diff --git a/apps/templates/_mfa_login_field.html b/apps/templates/_mfa_login_field.html index d73df5499..29c3ea17b 100644 --- a/apps/templates/_mfa_login_field.html +++ b/apps/templates/_mfa_login_field.html @@ -121,7 +121,8 @@ url: url, method: "POST", body: JSON.stringify(data), - success: onSuccess + success: onSuccess, + flash_message: false }) } diff --git a/apps/terminal/api/storage.py b/apps/terminal/api/storage.py index f00611bb2..f6222d13a 100644 --- a/apps/terminal/api/storage.py +++ b/apps/terminal/api/storage.py @@ -131,7 +131,13 @@ class BaseStorageTestConnectiveMixin: class CommandStorageTestConnectiveApi(BaseStorageTestConnectiveMixin, generics.RetrieveAPIView): queryset = CommandStorage.objects.all() + rbac_perms = { + 'retrieve': 'terminal.view_commandstorage' + } class ReplayStorageTestConnectiveApi(BaseStorageTestConnectiveMixin, generics.RetrieveAPIView): queryset = ReplayStorage.objects.all() + rbac_perms = { + 'retrieve': 'terminal.view_replaystorage' + } diff --git a/apps/terminal/models/terminal.py b/apps/terminal/models/terminal.py index 8578ab599..b5a753c96 100644 --- a/apps/terminal/models/terminal.py +++ b/apps/terminal/models/terminal.py @@ -182,6 +182,6 @@ class Terminal(StorageMixin, TerminalStatusMixin, models.Model): db_table = "terminal" verbose_name = _("Terminal") permissions = ( - ('view_terminalconfig', 'Can view terminal config'), + ('view_terminalconfig', _('Can view terminal config')), ) diff --git a/apps/tickets/api/comment.py b/apps/tickets/api/comment.py index 382ad2c99..3bdf55079 100644 --- a/apps/tickets/api/comment.py +++ b/apps/tickets/api/comment.py @@ -15,7 +15,7 @@ __all__ = ['CommentViewSet'] class CommentViewSet(mixins.CreateModelMixin, viewsets.ReadOnlyModelViewSet): serializer_class = serializers.CommentSerializer - permission_classes = (RBACPermission| IsSwagger | IsAssignee | IsApplicant) + permission_classes = (RBACPermission, IsSwagger | IsAssignee | IsApplicant) @lazyproperty def ticket(self): diff --git a/apps/tickets/api/relation.py b/apps/tickets/api/relation.py index 674be316c..cbb661259 100644 --- a/apps/tickets/api/relation.py +++ b/apps/tickets/api/relation.py @@ -17,6 +17,7 @@ class TicketSessionRelationViewSet(CreateModelMixin, JMSGenericViewSet): # Todo: 放到上面的 ViewSet 中 class TicketSessionApi(views.APIView): + perm_model = TicketSession def get(self, request, *args, **kwargs): with tmp_to_root_org(): diff --git a/apps/users/templates/users/mfa_setting.html b/apps/users/templates/users/mfa_setting.html index e1c6067f3..cb7b5a313 100644 --- a/apps/users/templates/users/mfa_setting.html +++ b/apps/users/templates/users/mfa_setting.html @@ -49,8 +49,9 @@
{% for b in mfa_backends %}
-
  • {{ b.display_name }} - {{ b.enable }}
  • +
  • + {{ b.display_name }} +
  • {% if b.is_active %}