diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 234bb9ca1..61bdd1d0d 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 5bff2be14..3466bb2d0 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-07-08 15:05+0800\n" +"POT-Creation-Date: 2020-07-09 10:05+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -211,7 +211,7 @@ msgstr "IP" #: assets/models/asset.py:187 assets/serializers/asset_user.py:45 #: assets/serializers/gathered_user.py:20 settings/serializers/settings.py:51 -#: tickets/serializers/request_asset_perm.py:13 +#: tickets/serializers/request_asset_perm.py:14 #: users/templates/users/_granted_assets.html:25 #: users/templates/users/user_asset_permission.html:157 msgid "Hostname" @@ -485,7 +485,7 @@ msgstr "每行一个命令" #: assets/models/cmd_filter.py:55 audits/models.py:57 #: authentication/templates/authentication/_access_key_modal.html:34 #: perms/forms/asset_permission.py:20 -#: tickets/serializers/request_asset_perm.py:51 +#: tickets/serializers/request_asset_perm.py:54 #: tickets/serializers/ticket.py:26 #: users/templates/users/_granted_assets.html:29 #: users/templates/users/user_asset_permission.html:44 @@ -540,7 +540,7 @@ msgstr "默认资产组" #: templates/index.html:78 terminal/backends/command/models.py:18 #: terminal/backends/command/serializers.py:12 terminal/models.py:185 #: tickets/models/ticket.py:35 tickets/models/ticket.py:130 -#: tickets/serializers/request_asset_perm.py:52 +#: tickets/serializers/request_asset_perm.py:55 #: tickets/serializers/ticket.py:27 users/forms/group.py:15 #: users/models/user.py:160 users/models/user.py:176 users/models/user.py:615 #: users/serializers/group.py:20 @@ -648,6 +648,7 @@ msgstr "SFTP根路径" #: perms/models/remote_app_permission.py:16 templates/_nav.html:45 #: terminal/backends/command/models.py:20 #: terminal/backends/command/serializers.py:14 terminal/models.py:189 +#: tickets/serializers/request_asset_perm.py:16 #: users/templates/users/_granted_assets.html:27 #: users/templates/users/user_asset_permission.html:42 #: users/templates/users/user_asset_permission.html:76 @@ -918,7 +919,7 @@ msgid "Success" msgstr "成功" #: audits/models.py:43 ops/models/command.py:28 perms/models/base.py:52 -#: terminal/models.py:199 tickets/serializers/request_asset_perm.py:15 +#: terminal/models.py:199 tickets/serializers/request_asset_perm.py:18 #: xpack/plugins/change_auth_plan/models.py:177 #: xpack/plugins/change_auth_plan/models.py:308 #: xpack/plugins/gathered_user/models.py:76 @@ -1009,7 +1010,7 @@ msgstr "多因子认证" msgid "Reason" msgstr "原因" -#: audits/models.py:106 tickets/serializers/request_asset_perm.py:50 +#: audits/models.py:106 tickets/serializers/request_asset_perm.py:53 #: tickets/serializers/ticket.py:25 xpack/plugins/cloud/models.py:214 #: xpack/plugins/cloud/models.py:272 msgid "Status" @@ -1699,7 +1700,7 @@ msgstr "动作" msgid "Asset permission" msgstr "资产授权" -#: perms/models/base.py:53 tickets/serializers/request_asset_perm.py:17 +#: perms/models/base.py:53 tickets/serializers/request_asset_perm.py:20 #: users/models/user.py:505 users/templates/users/user_detail.html:93 #: users/templates/users/user_profile.html:120 msgid "Date expired" @@ -2432,36 +2433,40 @@ msgstr "结束日期" msgid "Args" msgstr "参数" -#: tickets/api/request_asset_perm.py:36 +#: tickets/api/request_asset_perm.py:40 msgid "Ticket closed" msgstr "工单已关闭" -#: tickets/api/request_asset_perm.py:39 +#: tickets/api/request_asset_perm.py:43 #, python-format msgid "Ticket has %s" msgstr "工单已%s" -#: tickets/api/request_asset_perm.py:59 +#: tickets/api/request_asset_perm.py:69 +msgid "Superuser" +msgstr "超级管理员" + +#: tickets/api/request_asset_perm.py:102 msgid "Confirm assets first" msgstr "请先确认资产" -#: tickets/api/request_asset_perm.py:62 +#: tickets/api/request_asset_perm.py:105 msgid "Confirmed assets changed" msgstr "确认的资产变更了" -#: tickets/api/request_asset_perm.py:66 +#: tickets/api/request_asset_perm.py:109 msgid "Confirm system-user first" msgstr "请先确认系统用户" -#: tickets/api/request_asset_perm.py:70 +#: tickets/api/request_asset_perm.py:113 msgid "Confirmed system-user changed" msgstr "确认的系统用户变更了" -#: tickets/api/request_asset_perm.py:73 xpack/plugins/cloud/models.py:205 +#: tickets/api/request_asset_perm.py:116 xpack/plugins/cloud/models.py:205 msgid "Succeed" msgstr "成功" -#: tickets/api/request_asset_perm.py:81 +#: tickets/api/request_asset_perm.py:124 msgid "{} request assets, approved by {}" msgstr "{} 申请资产,通过人 {}" @@ -2525,19 +2530,19 @@ msgstr "{} {} 这个工单" msgid "this ticket" msgstr "这个工单" -#: tickets/serializers/request_asset_perm.py:11 +#: tickets/serializers/request_asset_perm.py:12 msgid "IP group" msgstr "IP组" -#: tickets/serializers/request_asset_perm.py:21 +#: tickets/serializers/request_asset_perm.py:24 msgid "Confirmed assets" msgstr "确认的资产" -#: tickets/serializers/request_asset_perm.py:25 +#: tickets/serializers/request_asset_perm.py:28 msgid "Confirmed system user" msgstr "确认的系统用户" -#: tickets/serializers/request_asset_perm.py:58 +#: tickets/serializers/request_asset_perm.py:65 msgid "Must be organization admin or superuser" msgstr "必须是组织管理员或者超级管理员" @@ -2778,10 +2783,6 @@ msgstr "最后更新密码日期" msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/serializers/group.py:50 -msgid "Auditors cannot be join in the user group" -msgstr "审计员不能被加入到用户组" - #: users/serializers/user.py:69 users/serializers/user.py:229 msgid "Is first login" msgstr "首次登录" @@ -3948,6 +3949,9 @@ msgstr "企业版" msgid "Ultimate edition" msgstr "旗舰版" +#~ msgid "Auditors cannot be join in the user group" +#~ msgstr "审计员不能被加入到用户组" + #~ msgid "Always update" #~ msgstr "总是更新" @@ -4758,9 +4762,6 @@ msgstr "旗舰版" #~ msgid "Tips: Some provider use token except password" #~ msgstr "提示:一些邮件提供商需要输入的是Token" -#~ msgid "Send user" -#~ msgstr "发送账号" - #~ msgid "Tips: Send mail account, default SMTP account as the send account" #~ msgstr "提示:发送邮件账号,默认使用SMTP账号作为发送账号" diff --git a/apps/tickets/api/request_asset_perm.py b/apps/tickets/api/request_asset_perm.py index 370d54b7f..40add8659 100644 --- a/apps/tickets/api/request_asset_perm.py +++ b/apps/tickets/api/request_asset_perm.py @@ -1,9 +1,13 @@ +from collections import namedtuple + from django.db.transaction import atomic +from django.db.models import F from django.utils.translation import ugettext_lazy as _ from rest_framework.decorators import action from rest_framework.response import Response -from common.const.http import POST +from users.models.user import User +from common.const.http import POST, GET from common.drf.api import JMSModelViewSet from common.permissions import IsValidUser from common.utils.django import get_object_or_none @@ -26,6 +30,7 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet): 'default': serializers.RequestAssetPermTicketSerializer, 'approve': EmptySerializer, 'reject': EmptySerializer, + 'assignees': serializers.OrgAssigneeSerializer, } permission_classes = (IsValidUser,) filter_fields = ['status', 'title', 'action', 'user_display'] @@ -38,6 +43,41 @@ class RequestAssetPermTicketViewSet(JMSModelViewSet): action_display = dict(instance.ACTION_CHOICES).get(action) raise TicketActionYet(detail=_('Ticket has %s') % action_display) + @action(detail=False, methods=[GET], permission_classes=[IsValidUser]) + def assignees(self, request, *args, **kwargs): + org_mapper = {} + UserTuple = namedtuple('UserTuple', ('id', 'name', 'username')) + user = request.user + superusers = User.objects.filter(role=User.ROLE_ADMIN) + + admins_with_org = User.objects.filter(related_admin_orgs__users=user).annotate( + org_id=F('related_admin_orgs__id'), org_name=F('related_admin_orgs__name') + ) + + for user in admins_with_org: + org_id = user.org_id + + if org_id not in org_mapper: + org_mapper[org_id] = { + 'org_name': user.org_name, + 'org_admins': set() # 去重 + } + org_mapper[org_id]['org_admins'].add(UserTuple(user.id, user.name, user.username)) + + result = [ + { + 'org_name': _('Superuser'), + 'org_admins': set(UserTuple(user.id, user.name, user.username) + for user in superusers) + } + ] + + for org in org_mapper.values(): + result.append(org) + serializer_class = self.get_serializer_class() + serilizer = serializer_class(instance=result, many=True) + return Response(data=serilizer.data) + @action(detail=True, methods=[POST], permission_classes=[IsAssignee, IsValidUser]) def reject(self, request, *args, **kwargs): instance = self.get_object() diff --git a/apps/tickets/serializers/request_asset_perm.py b/apps/tickets/serializers/request_asset_perm.py index 7519f4b1f..54e5ed79c 100644 --- a/apps/tickets/serializers/request_asset_perm.py +++ b/apps/tickets/serializers/request_asset_perm.py @@ -1,8 +1,9 @@ from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ from django.urls import reverse +from django.db.models import Q -from orgs.utils import current_org +from users.models.user import User from ..models import Ticket @@ -10,7 +11,9 @@ class RequestAssetPermTicketSerializer(serializers.ModelSerializer): ips = serializers.ListField(child=serializers.IPAddressField(), source='meta.ips', default=list, label=_('IP group')) hostname = serializers.CharField(max_length=256, source='meta.hostname', default=None, - allow_blank=True, label=_('Hostname')) + allow_blank=True, label=_('Hostname')) + system_user = serializers.CharField(max_length=256, source='meta.system_user', default='', + allow_blank=True, label=_('System user')) date_start = serializers.DateTimeField(source='meta.date_start', allow_null=True, required=False, label=_('Date start')) date_expired = serializers.DateTimeField(source='meta.date_expired', allow_null=True, @@ -33,7 +36,7 @@ class RequestAssetPermTicketSerializer(serializers.ModelSerializer): 'status', 'action', 'date_created', 'date_updated', 'system_user_waitlist_url', 'type', 'type_display', 'action_display', 'ips', 'confirmed_assets', 'date_start', 'date_expired', 'confirmed_system_user', 'hostname', - 'assets_waitlist_url' + 'assets_waitlist_url', 'system_user' ] m2m_fields = [ 'user', 'user_display', 'assignees', 'assignees_display', @@ -53,7 +56,11 @@ class RequestAssetPermTicketSerializer(serializers.ModelSerializer): } def validate_assignees(self, assignees): - count = current_org.org_admins().filter(id__in=[assignee.id for assignee in assignees]).count() + user = self.context['request'].user + + count = User.objects.filter(Q(related_admin_orgs__users=user) | Q(role=User.ROLE_ADMIN)).filter( + id__in=[assignee.id for assignee in assignees]).distinct().count() + if count != len(assignees): raise serializers.ValidationError(_('Must be organization admin or superuser')) return assignees @@ -61,7 +68,10 @@ class RequestAssetPermTicketSerializer(serializers.ModelSerializer): def get_system_user_waitlist_url(self, instance: Ticket): if not self._is_assignee(instance): return None - return {'url': reverse('api-assets:system-user-list')} + meta = instance.meta + url = reverse('api-assets:system-user-list') + query = meta.get('system_user', '') + return '{}?search={}'.format(url, query) def get_assets_waitlist_url(self, instance: Ticket): if not self._is_assignee(instance): @@ -118,3 +128,14 @@ class RequestAssetPermTicketSerializer(serializers.ModelSerializer): def _is_assignee(self, obj: Ticket): user = self.context['request'].user return obj.is_assignee(user) + + +class AssigneeSerializer(serializers.Serializer): + id = serializers.UUIDField() + name = serializers.CharField() + username = serializers.CharField() + + +class OrgAssigneeSerializer(serializers.Serializer): + org_name = serializers.CharField() + org_admins = AssigneeSerializer(many=True) diff --git a/apps/users/filters.py b/apps/users/filters.py index 4fc052c20..d12d3234e 100644 --- a/apps/users/filters.py +++ b/apps/users/filters.py @@ -1,6 +1,7 @@ from rest_framework.compat import coreapi, coreschema from rest_framework import filters +from users.models.user import User from orgs.utils import current_org @@ -11,7 +12,7 @@ class OrgRoleUserFilterBackend(filters.BaseFilterBackend): return queryset if org_role == 'admins': - return queryset & current_org.get_org_admins() + return queryset & (current_org.get_org_admins() | User.objects.filter(role=User.ROLE_ADMIN)) elif org_role == 'auditors': return queryset & current_org.get_org_auditors() elif org_role == 'users': diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index b01bddf42..351f33337 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -320,3 +320,9 @@ class UserUpdatePublicKeySerializer(serializers.ModelSerializer): new_public_key = self.validated_data.get('public_key') instance.set_public_key(new_public_key) return instance + + +class MiniUserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ['id', 'name', 'username']