diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index fc4e8e96f..cf3c41fc8 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 eff28a6d0..92e081d08 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: 2019-01-15 17:56+0800\n" +"POT-Creation-Date: 2019-01-16 17:58+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -67,8 +67,8 @@ msgstr "网域" #: assets/templates/assets/asset_update.html:35 perms/forms.py:45 #: perms/forms.py:52 perms/models.py:85 #: perms/templates/perms/asset_permission_list.html:57 -#: perms/templates/perms/asset_permission_list.html:77 -#: perms/templates/perms/asset_permission_list.html:126 +#: perms/templates/perms/asset_permission_list.html:78 +#: perms/templates/perms/asset_permission_list.html:128 #: xpack/plugins/cloud/models.py:123 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:63 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:66 @@ -119,7 +119,7 @@ msgstr "端口" #: perms/models.py:31 #: perms/templates/perms/asset_permission_create_update.html:45 #: perms/templates/perms/asset_permission_list.html:56 -#: perms/templates/perms/asset_permission_list.html:123 +#: perms/templates/perms/asset_permission_list.html:125 #: terminal/backends/command/models.py:13 terminal/models.py:143 #: terminal/templates/terminal/command_list.html:40 #: terminal/templates/terminal/command_list.html:73 @@ -185,7 +185,7 @@ msgstr "名称" #: assets/templates/assets/system_user_detail.html:62 #: assets/templates/assets/system_user_list.html:30 #: audits/templates/audits/login_log_list.html:49 -#: perms/templates/perms/asset_permission_list.html:76 +#: perms/templates/perms/asset_permission_list.html:74 #: perms/templates/perms/asset_permission_user.html:55 users/forms.py:15 #: users/forms.py:33 users/models/authentication.py:77 users/models/user.py:53 #: users/templates/users/_select_user_modal.html:14 @@ -277,7 +277,7 @@ msgstr "IP" #: assets/templates/assets/user_asset_list.html:45 #: assets/templates/assets/user_asset_list.html:150 common/forms.py:130 #: perms/templates/perms/asset_permission_asset.html:54 -#: perms/templates/perms/asset_permission_list.html:75 +#: perms/templates/perms/asset_permission_list.html:77 #: users/templates/users/user_granted_asset.html:44 #: users/templates/users/user_group_granted_asset.html:44 msgid "Hostname" @@ -637,7 +637,7 @@ msgstr "默认资产组" #: perms/models.py:29 #: perms/templates/perms/asset_permission_create_update.html:41 #: perms/templates/perms/asset_permission_list.html:54 -#: perms/templates/perms/asset_permission_list.html:117 templates/index.html:87 +#: perms/templates/perms/asset_permission_list.html:119 templates/index.html:87 #: terminal/backends/command/models.py:12 terminal/models.py:142 #: terminal/templates/terminal/command_list.html:32 #: terminal/templates/terminal/command_list.html:72 @@ -724,7 +724,8 @@ msgstr "登录模式" #: perms/models.py:33 perms/models.py:87 #: perms/templates/perms/asset_permission_detail.html:140 #: perms/templates/perms/asset_permission_list.html:58 -#: perms/templates/perms/asset_permission_list.html:129 templates/_nav.html:25 +#: perms/templates/perms/asset_permission_list.html:79 +#: perms/templates/perms/asset_permission_list.html:131 templates/_nav.html:25 #: terminal/backends/command/models.py:14 terminal/models.py:144 #: terminal/templates/terminal/command_list.html:48 #: terminal/templates/terminal/command_list.html:74 @@ -884,7 +885,7 @@ msgstr "自动生成密钥" #: assets/templates/assets/asset_update.html:64 #: assets/templates/assets/gateway_create_update.html:53 #: perms/templates/perms/asset_permission_create_update.html:50 -#: terminal/templates/terminal/terminal_update.html:42 +#: terminal/templates/terminal/terminal_update.html:40 msgid "Other" msgstr "其它" @@ -906,7 +907,7 @@ msgstr "其它" #: common/templates/common/security_setting.html:70 #: common/templates/common/terminal_setting.html:68 #: perms/templates/perms/asset_permission_create_update.html:80 -#: terminal/templates/terminal/terminal_update.html:47 +#: terminal/templates/terminal/terminal_update.html:45 #: users/templates/users/_user.html:50 #: users/templates/users/user_bulk_update.html:23 #: users/templates/users/user_detail.html:176 @@ -941,7 +942,7 @@ msgstr "重置" #: perms/templates/perms/asset_permission_create_update.html:81 #: terminal/templates/terminal/command_list.html:103 #: terminal/templates/terminal/session_list.html:127 -#: terminal/templates/terminal/terminal_update.html:48 +#: terminal/templates/terminal/terminal_update.html:46 #: users/templates/users/_user.html:51 #: users/templates/users/forgot_password.html:45 #: users/templates/users/user_bulk_update.html:24 @@ -1025,7 +1026,7 @@ msgstr "测试" #: assets/templates/assets/system_user_detail.html:26 #: assets/templates/assets/system_user_list.html:92 audits/models.py:32 #: perms/templates/perms/asset_permission_detail.html:30 -#: perms/templates/perms/asset_permission_list.html:175 +#: perms/templates/perms/asset_permission_list.html:177 #: terminal/templates/terminal/terminal_detail.html:16 #: terminal/templates/terminal/terminal_list.html:71 #: users/templates/users/user_detail.html:25 @@ -1060,7 +1061,7 @@ msgstr "更新" #: common/templates/common/terminal_setting.html:112 #: ops/templates/ops/task_list.html:72 #: perms/templates/perms/asset_permission_detail.html:34 -#: perms/templates/perms/asset_permission_list.html:176 +#: perms/templates/perms/asset_permission_list.html:178 #: terminal/templates/terminal/terminal_list.html:73 #: users/templates/users/user_detail.html:30 #: users/templates/users/user_group_detail.html:32 @@ -1173,7 +1174,6 @@ msgstr "快速修改" #: perms/models.py:88 #: perms/templates/perms/asset_permission_create_update.html:52 #: perms/templates/perms/asset_permission_detail.html:120 -#: perms/templates/perms/asset_permission_list.html:73 #: terminal/templates/terminal/terminal_list.html:34 #: users/templates/users/_select_user_modal.html:18 #: users/templates/users/user_detail.html:144 @@ -2572,7 +2572,8 @@ msgstr "组织管理" #: perms/forms.py:39 perms/models.py:30 perms/models.py:86 #: perms/templates/perms/asset_permission_list.html:55 -#: perms/templates/perms/asset_permission_list.html:120 templates/_nav.html:14 +#: perms/templates/perms/asset_permission_list.html:75 +#: perms/templates/perms/asset_permission_list.html:122 templates/_nav.html:14 #: users/forms.py:273 users/models/group.py:26 users/models/user.py:61 #: users/templates/users/_select_user_modal.html:16 #: users/templates/users/user_detail.html:213 @@ -2658,6 +2659,7 @@ msgid "Create permission" msgstr "创建授权规则" #: perms/templates/perms/asset_permission_list.html:59 +#: perms/templates/perms/asset_permission_list.html:73 #: users/templates/users/user_list.html:28 xpack/plugins/cloud/models.py:53 #: xpack/plugins/cloud/templates/cloud/account_detail.html:60 #: xpack/plugins/cloud/templates/cloud/account_list.html:14 @@ -3545,6 +3547,10 @@ msgstr "用户认证源来自 {}, 请去相应系统修改密码" msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" +#: users/serializers/v2.py:40 +msgid "name not unique" +msgstr "名称重复" + #: users/templates/users/_base_otp.html:27 msgid "Home page" msgstr "首页" diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 9a570d841..5e602dd4f 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -161,6 +161,87 @@ function activeNav() { } } +function formSubmit(props) { + /* + { + "form": $("form"), + "url": "", + "method": "POST", + "redirect_to": "", + "success": function(data, textStatue, jqXHR){}, + "error": function(jqXHR, textStatus, errorThrown) {} + } + */ + props = props || {}; + var data = props.data || props.form.serializeObject(); + var redirect_to = props.redirect_to; + $.ajax({ + url: props.url, + type: props.method || 'POST', + data: JSON.stringify(data), + contentType: props.content_type || "application/json; charset=utf-8", + dataType: props.data_type || "json" + }).done(function (data, textState, jqXHR) { + if (redirect_to) { + location.href = redirect_to; + } else if (typeof props.success === 'function') { + return props.success(data, textState, jqXHR); + } + }).fail(function(jqXHR, textStatus, errorThrown) { + if (typeof props.error === 'function') { + return props.error(jqXHR, textStatus, errorThrown) + } + if (!props.form) { + alert(jqXHR.responseText); + return + } + if (jqXHR.status === 400) { + var errors = jqXHR.responseJSON; + var noneFieldErrorRef = props.form.children('.alert-danger'); + if (noneFieldErrorRef.length !== 1) { + props.form.prepend(''); + noneFieldErrorRef = props.form.children('.alert-danger'); + } + var noneFieldErrorMsg = ""; + noneFieldErrorRef.css("display", "none"); + noneFieldErrorRef.html(""); + props.form.find(".help-block.error").html(""); + props.form.find(".form-group.has-error").removeClass("has-error"); + + if (typeof errors !== "object") { + noneFieldErrorMsg = errors; + if (noneFieldErrorRef.length === 1) { + noneFieldErrorRef.css('display', 'block'); + noneFieldErrorRef.html(noneFieldErrorMsg); + } + return + } + $.each(errors, function (k, v) { + var fieldRef = props.form.find('input[name="' + k + '"]'); + var formGroupRef = fieldRef.parents('.form-group'); + var parentRef = fieldRef.parent(); + var helpBlockRef = parentRef.children('.help-block.error'); + if (helpBlockRef.length === 0) { + parentRef.append('
'); + helpBlockRef = parentRef.children('.help-block.error'); + } + if (fieldRef.length === 1 && formGroupRef.length === 1) { + formGroupRef.addClass('has-error'); + var help_msg = v.join("
") ; + helpBlockRef.html(help_msg); + } else { + noneFieldErrorMsg += v + '
'; + } + }); + if (noneFieldErrorRef.length === 1 && noneFieldErrorMsg !== '') { + noneFieldErrorRef.css('display', 'block'); + noneFieldErrorRef.html(noneFieldErrorMsg); + } + } + + }) +} + function APIUpdateAttr(props) { // props = {url: .., body: , success: , error: , method: ,} props = props || {}; @@ -195,9 +276,6 @@ function APIUpdateAttr(props) { }).fail(function(jqXHR, textStatus, errorThrown) { if (flash_message) { var msg = ""; - console.log(jqXHR); - console.log(textStatus); - console.log(errorThrown); if (user_fail_message) { msg = user_fail_message; } else if (jqXHR.responseJSON) { @@ -213,6 +291,7 @@ function APIUpdateAttr(props) { toastr.error(msg); } if (typeof props.error === 'function') { + console.log(jqXHR); return props.error(jqXHR.responseText, jqXHR.status); } }); diff --git a/apps/terminal/api/v2/terminal.py b/apps/terminal/api/v2/terminal.py index 3fd42a6ab..7e7a61c2a 100644 --- a/apps/terminal/api/v2/terminal.py +++ b/apps/terminal/api/v2/terminal.py @@ -1,12 +1,16 @@ # -*- coding: utf-8 -*- # -from rest_framework import viewsets +from rest_framework import viewsets, generics +from rest_framework import status +from rest_framework.response import Response from common.permissions import IsSuperUser, WithBootstrapToken + + from ...models import Terminal from ...serializers import v2 as serializers -__all__ = ['TerminalViewSet', 'TerminalRegistrationViewSet'] +__all__ = ['TerminalViewSet', 'TerminalRegistrationApi'] class TerminalViewSet(viewsets.ModelViewSet): @@ -15,8 +19,19 @@ class TerminalViewSet(viewsets.ModelViewSet): permission_classes = [IsSuperUser] -class TerminalRegistrationViewSet(viewsets.ModelViewSet): - queryset = Terminal.objects.filter(is_deleted=False) +class TerminalRegistrationApi(generics.CreateAPIView): serializer_class = serializers.TerminalRegistrationSerializer permission_classes = [WithBootstrapToken] http_method_names = ['post'] + + def create(self, request, *args, **kwargs): + data = request.data + serializer = serializers.TerminalSerializer( + data=data, context={'request': request} + ) + serializer.is_valid(raise_exception=True) + terminal = serializer.save() + sa_serializer = serializer.sa_serializer_class(instance=terminal.user) + data['service_account'] = sa_serializer.data + return Response(data, status=status.HTTP_201_CREATED) + diff --git a/apps/terminal/forms.py b/apps/terminal/forms.py index 207363ab0..b2f81fbb5 100644 --- a/apps/terminal/forms.py +++ b/apps/terminal/forms.py @@ -39,5 +39,3 @@ class TerminalForm(forms.ModelForm): 'name', 'remote_addr', 'comment', 'command_storage', 'replay_storage', ] - help_texts = { - } diff --git a/apps/terminal/serializers/v1.py b/apps/terminal/serializers/v1.py index ac3324097..688453ac1 100644 --- a/apps/terminal/serializers/v1.py +++ b/apps/terminal/serializers/v1.py @@ -31,8 +31,6 @@ class TerminalSerializer(serializers.ModelSerializer): return cache.get(key) - - class SessionSerializer(BulkSerializerMixin, serializers.ModelSerializer): command_amount = serializers.SerializerMethodField() command_store = get_multi_command_storage() diff --git a/apps/terminal/serializers/v2.py b/apps/terminal/serializers/v2.py index 7e0365800..242c07268 100644 --- a/apps/terminal/serializers/v2.py +++ b/apps/terminal/serializers/v2.py @@ -3,7 +3,7 @@ from rest_framework import serializers from common.utils import get_request_ip -from users.serializers.v2 import ServiceAccountRegistrationSerializer +from users.serializers.v2 import ServiceAccountSerializer from ..models import Terminal @@ -11,36 +11,48 @@ __all__ = ['TerminalSerializer', 'TerminalRegistrationSerializer'] class TerminalSerializer(serializers.ModelSerializer): - class Meta: - model = Terminal - fields = [ - 'id', 'name', 'remote_addr', 'comment', - ] - read_only_fields = ['id', 'remote_addr'] - - -class TerminalRegistrationSerializer(serializers.ModelSerializer): - service_account = ServiceAccountRegistrationSerializer(read_only=True) - service_account_serializer = None + sa_serializer_class = ServiceAccountSerializer + sa_serializer = None class Meta: model = Terminal fields = [ - 'id', 'name', 'remote_addr', 'comment', 'service_account' + 'id', 'name', 'remote_addr', 'command_storage', + 'replay_storage', 'user', 'is_accepted', 'is_deleted', + 'date_created', 'comment' ] - read_only_fields = ['id', 'remote_addr', 'service_account'] - - def validate(self, attrs): - self.service_account_serializer = ServiceAccountRegistrationSerializer(data=attrs) - self.service_account_serializer.is_valid(raise_exception=True) - return attrs + read_only_fields = ['id', 'remote_addr', 'user', 'date_created'] + + def is_valid(self, raise_exception=False): + valid = super().is_valid(raise_exception=raise_exception) + if not valid: + return valid + data = {'name': self.validated_data.get('name')} + kwargs = {'data': data} + if self.instance and self.instance.user: + kwargs['instance'] = self.instance.user + self.sa_serializer = ServiceAccountSerializer(**kwargs) + valid = self.sa_serializer.is_valid(raise_exception=True) + return valid + + def save(self, **kwargs): + instance = super().save(**kwargs) + sa = self.sa_serializer.save() + instance.user = sa + instance.save() + return instance def create(self, validated_data): request = self.context.get('request') - sa = self.service_account_serializer.save() instance = super().create(validated_data) instance.is_accepted = True - instance.user = sa - instance.remote_addr = get_request_ip(request) + if request: + instance.remote_addr = get_request_ip(request) instance.save() return instance + + +class TerminalRegistrationSerializer(serializers.Serializer): + name = serializers.CharField(max_length=128) + comment = serializers.CharField(max_length=128) + service_account = ServiceAccountSerializer(read_only=True) diff --git a/apps/terminal/templates/terminal/terminal_update.html b/apps/terminal/templates/terminal/terminal_update.html index bde86563d..f9554fda4 100644 --- a/apps/terminal/templates/terminal/terminal_update.html +++ b/apps/terminal/templates/terminal/terminal_update.html @@ -33,8 +33,6 @@

{% trans 'Info' %}

{% bootstrap_field form.name layout="horizontal" %} {% bootstrap_field form.remote_addr layout="horizontal" %} -{# {% bootstrap_field form.ssh_port layout="horizontal" %}#} -{# {% bootstrap_field form.http_port layout="horizontal" %}#} {% bootstrap_field form.command_storage layout="horizontal" %} {% bootstrap_field form.replay_storage layout="horizontal" %} @@ -60,14 +58,14 @@ diff --git a/apps/terminal/urls/api_urls_v2.py b/apps/terminal/urls/api_urls_v2.py index 600ad3611..15254d216 100644 --- a/apps/terminal/urls/api_urls_v2.py +++ b/apps/terminal/urls/api_urls_v2.py @@ -11,10 +11,11 @@ app_name = 'terminal' router = BulkRouter() router.register(r'terminal', api.TerminalViewSet, 'terminal') -router.register(r'terminal-registrations', api.TerminalRegistrationViewSet, 'terminal-registration') urlpatterns = [ + path('terminal-registrations/', api.TerminalRegistrationApi.as_view(), + name='terminal-registration') ] urlpatterns += router.urls diff --git a/apps/users/api/v2/user.py b/apps/users/api/v2/user.py index 6c883861e..b0f121809 100644 --- a/apps/users/api/v2/user.py +++ b/apps/users/api/v2/user.py @@ -7,6 +7,6 @@ from ...serializers import v2 as serializers class ServiceAccountRegistrationViewSet(viewsets.ModelViewSet): - serializer_class = serializers.ServiceAccountRegistrationSerializer + serializer_class = serializers.ServiceAccountSerializer permission_classes = (WithBootstrapToken,) http_method_names = ['post'] diff --git a/apps/users/serializers/v2.py b/apps/users/serializers/v2.py index f9931d5dc..f936dc7bc 100644 --- a/apps/users/serializers/v2.py +++ b/apps/users/serializers/v2.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # +from django.utils.translation import ugettext as _ from rest_framework import serializers from ..models import User, AccessKey @@ -12,7 +13,7 @@ class AccessKeySerializer(serializers.ModelSerializer): read_only_fields = ['id', 'secret'] -class ServiceAccountRegistrationSerializer(serializers.ModelSerializer): +class ServiceAccountSerializer(serializers.ModelSerializer): access_key = AccessKeySerializer(read_only=True) class Meta: @@ -30,15 +31,22 @@ class ServiceAccountRegistrationSerializer(serializers.ModelSerializer): def validate_name(self, name): email = self.get_email() username = self.get_username() - if User.objects.filter(email=email) or \ - User.objects.filter(username=username): - raise serializers.ValidationError('name not unique', code='unique') + if self.instance: + users = User.objects.exclude(id=self.instance.id) + else: + users = User.objects.all() + if users.filter(email=email) or \ + users.filter(username=username): + raise serializers.ValidationError(_('name not unique'), code='unique') return name + def save(self, **kwargs): + self.validated_data['email'] = self.get_email() + self.validated_data['username'] = self.get_username() + self.validated_data['role'] = User.ROLE_APP + return super().save(**kwargs) + def create(self, validated_data): - validated_data['email'] = self.get_email() - validated_data['username'] = self.get_username() - validated_data['role'] = User.ROLE_APP instance = super().create(validated_data) instance.create_access_key() return instance