Commit a5b874e2 authored by ibuler's avatar ibuler

[Update] 改密支持windows

parent 75fb37d2
...@@ -16,7 +16,7 @@ from django.urls import reverse_lazy ...@@ -16,7 +16,7 @@ from django.urls import reverse_lazy
from django.core.cache import cache from django.core.cache import cache
from django.db.models import Q from django.db.models import Q
from common.mixins import IDInCacheFilterMixin from common.mixins import IDInCacheFilterMixin, ApiMessageMixin
from common.utils import get_logger, get_object_or_none from common.utils import get_logger, get_object_or_none
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
...@@ -36,7 +36,7 @@ __all__ = [ ...@@ -36,7 +36,7 @@ __all__ = [
] ]
class AssetViewSet(IDInCacheFilterMixin, LabelFilter, BulkModelViewSet): class AssetViewSet(IDInCacheFilterMixin, LabelFilter, ApiMessageMixin, BulkModelViewSet):
""" """
API endpoint that allows Asset to be viewed or edited. API endpoint that allows Asset to be viewed or edited.
""" """
...@@ -47,6 +47,7 @@ class AssetViewSet(IDInCacheFilterMixin, LabelFilter, BulkModelViewSet): ...@@ -47,6 +47,7 @@ class AssetViewSet(IDInCacheFilterMixin, LabelFilter, BulkModelViewSet):
serializer_class = serializers.AssetSerializer serializer_class = serializers.AssetSerializer
pagination_class = LimitOffsetPagination pagination_class = LimitOffsetPagination
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
success_message = _("%(hostname)s was %(action)s successfully")
def set_assets_node(self, assets): def set_assets_node(self, assets):
if not isinstance(assets, list): if not isinstance(assets, list):
......
...@@ -6,21 +6,39 @@ from django.utils.translation import gettext_lazy as _ ...@@ -6,21 +6,39 @@ from django.utils.translation import gettext_lazy as _
from common.utils import get_logger from common.utils import get_logger
from orgs.mixins import OrgModelForm from orgs.mixins import OrgModelForm
from ..models import Asset, AdminUser from ..models import Asset, AdminUser, Protocol
logger = get_logger(__file__) logger = get_logger(__file__)
__all__ = ['AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm'] __all__ = [
'AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm',
'ProtocolForm'
]
class ProtocolForm(forms.ModelForm):
class Meta:
model = Protocol
fields = ['name', 'port']
widgets = {
'name': forms.Select(attrs={
'class': 'form-control protocol-name'
}),
'port': forms.TextInput(attrs={
'class': 'form-control protocol-port'
}),
}
class AssetCreateForm(OrgModelForm): class AssetCreateForm(OrgModelForm):
PROTOCOL_CHOICES = Protocol.PROTOCOL_CHOICES
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'hostname', 'ip', 'public_ip', 'port', 'comment', 'hostname', 'ip', 'public_ip', 'protocols', 'comment',
'nodes', 'is_active', 'admin_user', 'labels', 'platform', 'nodes', 'is_active', 'admin_user', 'labels', 'platform',
'domain', 'protocol', 'domain',
] ]
widgets = { widgets = {
'nodes': forms.SelectMultiple(attrs={ 'nodes': forms.SelectMultiple(attrs={
...@@ -32,7 +50,6 @@ class AssetCreateForm(OrgModelForm): ...@@ -32,7 +50,6 @@ class AssetCreateForm(OrgModelForm):
'labels': forms.SelectMultiple(attrs={ 'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Label') 'class': 'select2', 'data-placeholder': _('Label')
}), }),
'port': forms.TextInput(),
'domain': forms.Select(attrs={ 'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain') 'class': 'select2', 'data-placeholder': _('Domain')
}), }),
...@@ -54,9 +71,9 @@ class AssetUpdateForm(OrgModelForm): ...@@ -54,9 +71,9 @@ class AssetUpdateForm(OrgModelForm):
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'hostname', 'ip', 'port', 'nodes', 'is_active', 'platform', 'hostname', 'ip', 'protocols', 'nodes', 'is_active', 'platform',
'public_ip', 'number', 'comment', 'admin_user', 'labels', 'public_ip', 'number', 'comment', 'admin_user', 'labels',
'domain', 'protocol', 'domain',
] ]
widgets = { widgets = {
'nodes': forms.SelectMultiple(attrs={ 'nodes': forms.SelectMultiple(attrs={
...@@ -68,7 +85,6 @@ class AssetUpdateForm(OrgModelForm): ...@@ -68,7 +85,6 @@ class AssetUpdateForm(OrgModelForm):
'labels': forms.SelectMultiple(attrs={ 'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Label') 'class': 'select2', 'data-placeholder': _('Label')
}), }),
'port': forms.TextInput(),
'domain': forms.Select(attrs={ 'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain') 'class': 'select2', 'data-placeholder': _('Domain')
}), }),
...@@ -101,8 +117,8 @@ class AssetBulkUpdateForm(OrgModelForm): ...@@ -101,8 +117,8 @@ class AssetBulkUpdateForm(OrgModelForm):
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'assets', 'port', 'admin_user', 'labels', 'platform', 'assets', 'admin_user', 'labels', 'platform',
'protocol', 'domain', 'domain',
] ]
widgets = { widgets = {
'labels': forms.SelectMultiple( 'labels': forms.SelectMultiple(
......
...@@ -15,4 +15,9 @@ class Migration(migrations.Migration): ...@@ -15,4 +15,9 @@ class Migration(migrations.Migration):
name='ip', name='ip',
field=models.CharField(db_index=True, max_length=128, verbose_name='IP'), field=models.CharField(db_index=True, max_length=128, verbose_name='IP'),
), ),
migrations.AlterField(
model_name='asset',
name='public_ip',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Public IP'),
),
] ]
# Generated by Django 2.1.7 on 2019-05-22 02:58
import django.core.validators
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0027_auto_20190521_1703'),
]
operations = [
migrations.CreateModel(
name='Protocol',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)'), ('vnc', 'vnc')], default='ssh', max_length=16, verbose_name='Name')),
('port', models.IntegerField(default=22, validators=[django.core.validators.MaxValueValidator(65535), django.core.validators.MinValueValidator(1)], verbose_name='Port')),
],
),
migrations.AddField(
model_name='asset',
name='protocols',
field=models.ManyToManyField(to='assets.Protocol',
verbose_name='Protocol'),
),
]
# Generated by Django 2.1.7 on 2019-05-22 03:14
from django.db import migrations
def migrate_assets_protocol(apps, schema_editor):
asset_model = apps.get_model("assets", "Asset")
db_alias = schema_editor.connection.alias
assets = asset_model.objects.using(db_alias).all()
for asset in assets:
asset.protocols.create(name=asset.protocol, port=asset.port)
class Migration(migrations.Migration):
dependencies = [
('assets', '0028_protocol'),
]
operations = [
migrations.RunPython(migrate_assets_protocol),
]
...@@ -8,4 +8,3 @@ from .asset import * ...@@ -8,4 +8,3 @@ from .asset import *
from .cmd_filter import * from .cmd_filter import *
from .utils import * from .utils import *
from .authbook import * from .authbook import *
from applications.models.remote_app import *
...@@ -12,11 +12,12 @@ from django.db import models ...@@ -12,11 +12,12 @@ from django.db import models
from django.db.models import Q from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache from django.core.cache import cache
from django.core.validators import MinValueValidator, MaxValueValidator
from .user import AdminUser, SystemUser from .user import AdminUser, SystemUser
from orgs.mixins import OrgModelMixin, OrgManager from orgs.mixins import OrgModelMixin, OrgManager
__all__ = ['Asset'] __all__ = ['Asset', 'Protocol']
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -47,6 +48,29 @@ class AssetQuerySet(models.QuerySet): ...@@ -47,6 +48,29 @@ class AssetQuerySet(models.QuerySet):
return self.active() return self.active()
class Protocol(models.Model):
PROTOCOL_SSH = 'ssh'
PROTOCOL_RDP = 'rdp'
PROTOCOL_TELNET = 'telnet'
PROTOCOL_VNC = 'vnc'
PROTOCOL_CHOICES = (
(PROTOCOL_SSH, 'ssh'),
(PROTOCOL_RDP, 'rdp'),
(PROTOCOL_TELNET, 'telnet (beta)'),
(PROTOCOL_VNC, 'vnc'),
)
PORT_VALIDATORS = [MaxValueValidator(65535), MinValueValidator(1)]
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=16, choices=PROTOCOL_CHOICES,
default=PROTOCOL_SSH, verbose_name=_("Name"))
port = models.IntegerField(default=22, verbose_name=_("Port"),
validators=PORT_VALIDATORS)
def __str__(self):
return "{}:{}".format(self.name, self.port)
class Asset(OrgModelMixin): class Asset(OrgModelMixin):
# Important # Important
PLATFORM_CHOICES = ( PLATFORM_CHOICES = (
...@@ -59,22 +83,15 @@ class Asset(OrgModelMixin): ...@@ -59,22 +83,15 @@ class Asset(OrgModelMixin):
('Other', 'Other'), ('Other', 'Other'),
) )
PROTOCOL_SSH = 'ssh'
PROTOCOL_RDP = 'rdp'
PROTOCOL_TELNET = 'telnet'
PROTOCOL_VNC = 'vnc'
PROTOCOL_CHOICES = (
(PROTOCOL_SSH, 'ssh'),
(PROTOCOL_RDP, 'rdp'),
(PROTOCOL_TELNET, 'telnet (beta)'),
(PROTOCOL_VNC, 'vnc'),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True) ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
hostname = models.CharField(max_length=128, verbose_name=_('Hostname')) hostname = models.CharField(max_length=128, verbose_name=_('Hostname'))
protocol = models.CharField(max_length=128, default=PROTOCOL_SSH, choices=PROTOCOL_CHOICES, verbose_name=_('Protocol')) protocol = models.CharField(max_length=128, default=Protocol.PROTOCOL_SSH,
choices=Protocol.PROTOCOL_CHOICES,
verbose_name=_('Protocol'))
port = models.IntegerField(default=22, verbose_name=_('Port')) port = models.IntegerField(default=22, verbose_name=_('Port'))
protocols = models.ManyToManyField('Protocol', verbose_name=_("Protocol"))
platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform')) platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform'))
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL) domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL)
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes")) nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
...@@ -84,7 +101,7 @@ class Asset(OrgModelMixin): ...@@ -84,7 +101,7 @@ class Asset(OrgModelMixin):
admin_user = models.ForeignKey('assets.AdminUser', on_delete=models.PROTECT, null=True, verbose_name=_("Admin user")) admin_user = models.ForeignKey('assets.AdminUser', on_delete=models.PROTECT, null=True, verbose_name=_("Admin user"))
# Some information # Some information
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP')) public_ip = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Public IP'))
number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number')) number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number'))
# Collect # Collect
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from rest_framework import serializers from rest_framework import serializers
from rest_framework.validators import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgResourceSerializerMixin from orgs.mixins import OrgResourceSerializerMixin
from common.mixins import BulkSerializerMixin from common.mixins import BulkSerializerMixin
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from ..models import Asset from common.validators import ProjectUniqueValidator
from ..models import Asset, Protocol
from .system_user import AssetSystemUserSerializer from .system_user import AssetSystemUserSerializer
__all__ = [ __all__ = [
...@@ -16,25 +18,32 @@ __all__ = [ ...@@ -16,25 +18,32 @@ __all__ = [
] ]
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResourceSerializerMixin): class ProtocolSerializer(serializers.ModelSerializer):
class Meta:
model = Protocol
fields = ["name", "port"]
class AssetSerializer(BulkSerializerMixin, OrgResourceSerializerMixin, serializers.ModelSerializer):
protocols = ProtocolSerializer(many=True)
""" """
资产的数据结构 资产的数据结构
""" """
class Meta: class Meta:
model = Asset model = Asset
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
# validators = [] # 解决批量导入时unique_together字段校验失败
fields = [ fields = [
'id', 'org_id', 'org_name', 'ip', 'hostname', 'protocol', 'port', 'id', 'org_id', 'org_name', 'ip', 'hostname', 'protocol', 'port',
'platform', 'is_active', 'public_ip', 'domain', 'admin_user', 'protocols', 'platform', 'is_active', 'public_ip', 'domain',
'nodes', 'labels', 'number', 'vendor', 'model', 'sn', 'admin_user', 'nodes', 'labels', 'number', 'vendor', 'model', 'sn',
'cpu_model', 'cpu_count', 'cpu_cores', 'cpu_vcpus', 'memory', 'cpu_model', 'cpu_count', 'cpu_cores', 'cpu_vcpus', 'memory',
'disk_total', 'disk_info', 'os', 'os_version', 'os_arch', 'disk_total', 'disk_info', 'os', 'os_version', 'os_arch',
'hostname_raw', 'comment', 'created_by', 'date_created', 'hostname_raw', 'comment', 'created_by', 'date_created',
'hardware_info', 'connectivity' 'hardware_info', 'connectivity'
] ]
read_only_fields = ( read_only_fields = (
'number', 'vendor', 'model', 'sn', 'cpu_model', 'cpu_count', 'vendor', 'model', 'sn', 'cpu_model', 'cpu_count',
'cpu_cores', 'cpu_vcpus', 'memory', 'disk_total', 'disk_info', 'cpu_cores', 'cpu_vcpus', 'memory', 'disk_total', 'disk_info',
'os', 'os_version', 'os_arch', 'hostname_raw', 'os', 'os_version', 'os_arch', 'hostname_raw',
'created_by', 'date_created', 'created_by', 'date_created',
...@@ -43,7 +52,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResou ...@@ -43,7 +52,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResou
'hardware_info': {'label': _('Hardware info')}, 'hardware_info': {'label': _('Hardware info')},
'connectivity': {'label': _('Connectivity')}, 'connectivity': {'label': _('Connectivity')},
'org_name': {'label': _('Org name')} 'org_name': {'label': _('Org name')}
} }
@classmethod @classmethod
...@@ -53,18 +61,64 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResou ...@@ -53,18 +61,64 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResou
.select_related('admin_user') .select_related('admin_user')
return queryset return queryset
def get_field_names(self, declared_fields, info): @staticmethod
fields = super().get_field_names(declared_fields, info) def validate_protocols(attr):
fields.extend([ protocols_name = [i.get("name", "ssh") for i in attr]
'hardware_info', 'connectivity', 'org_name' errors = [{} for i in protocols_name]
]) for i, name in enumerate(protocols_name):
return fields if name in protocols_name[:i]:
errors[i] = {"name": _("Protocol duplicate: {}").format(name)}
if any(errors):
raise ValidationError(errors)
return attr
def create(self, validated_data):
protocols_data = validated_data.pop("protocols")
# 兼容老的api
protocol = validated_data.get("protocol")
port = validated_data.get("port")
if not protocols_data and protocol and port:
protocols_data = [{"name": protocol, "port": port}]
if not protocol and not port and protocols_data:
validated_data["protocol"] = protocols_data[0]["name"]
validated_data["port"] = protocols_data[0]["port"]
protocols_serializer = ProtocolSerializer(data=protocols_data, many=True)
protocols_serializer.is_valid(raise_exception=True)
protocols = protocols_serializer.save()
instance = super().create(validated_data)
instance.protocols.set(protocols)
return instance
def update(self, instance, validated_data):
protocols_data = validated_data.pop("protocols")
# 兼容老的api
protocol = validated_data.get("protocol")
port = validated_data.get("port")
if not protocols_data and protocol and port:
protocols_data = [{"name": protocol, "port": port}]
if not protocol and not port and protocols_data:
validated_data["protocol"] = protocols_data[0]["name"]
validated_data["port"] = protocols_data[0]["port"]
protocols_serializer = ProtocolSerializer(data=protocols_data, many=True)
protocols_serializer.is_valid(raise_exception=True)
protocols = protocols_serializer.save()
instance = super().update(instance, validated_data)
instance.protocols.all().delete()
instance.protocols.set(protocols)
return instance
class AssetAsNodeSerializer(serializers.ModelSerializer): class AssetAsNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Asset model = Asset
fields = ['id', 'hostname', 'ip', 'port', 'platform', 'protocol'] fields = ['id', 'hostname', 'ip', 'platform', 'protocols']
class AssetGrantedSerializer(serializers.ModelSerializer): class AssetGrantedSerializer(serializers.ModelSerializer):
...@@ -78,9 +132,9 @@ class AssetGrantedSerializer(serializers.ModelSerializer): ...@@ -78,9 +132,9 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Asset model = Asset
fields = ( fields = (
"id", "hostname", "ip", "port", "system_users_granted", "id", "hostname", "ip", "system_users_granted",
"is_active", "system_users_join", "os", 'domain', "is_active", "system_users_join", "os", 'domain',
"platform", "comment", "protocol", "org_id", "org_name", "platform", "comment", "protocols", "org_id", "org_name",
) )
@staticmethod @staticmethod
......
...@@ -5,6 +5,7 @@ from django.db.models.signals import post_save, m2m_changed, post_delete ...@@ -5,6 +5,7 @@ from django.db.models.signals import post_save, m2m_changed, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from common.utils import get_logger from common.utils import get_logger
from common.decorator import on_transaction_commit
from .models import Asset, SystemUser, Node, AuthBook from .models import Asset, SystemUser, Node, AuthBook
from .tasks import ( from .tasks import (
update_assets_hardware_info_util, update_assets_hardware_info_util,
...@@ -32,9 +33,12 @@ def set_asset_root_node(asset): ...@@ -32,9 +33,12 @@ def set_asset_root_node(asset):
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier") @receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
@on_transaction_commit
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs): def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
if created: if created:
logger.info("Asset `{}` create signal received".format(instance)) logger.info("Asset `{}` create signal received".format(instance))
# 获取资产硬件信息
update_asset_hardware_info_on_created(instance) update_asset_hardware_info_on_created(instance)
test_asset_conn_on_created(instance) test_asset_conn_on_created(instance)
......
...@@ -16,12 +16,24 @@ ...@@ -16,12 +16,24 @@
<h3>{% trans 'Basic' %}</h3> <h3>{% trans 'Basic' %}</h3>
{% bootstrap_field form.hostname layout="horizontal" %} {% bootstrap_field form.hostname layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %} {% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.protocol layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.platform layout="horizontal" %} {% bootstrap_field form.platform layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %} {% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.domain layout="horizontal" %} {% bootstrap_field form.domain layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Protocols' %}</h3>
<div class="protocols">
{% for fm in formset.forms %}
<div class="form-group">
<div class="col-md-2 col-md-offset-2" style="text-align: right">{{ fm.name }}</div>
<div class="col-md-6">{{ fm.port }}</div>
<div class="col-md-1" style="padding: 6px 0">
<a class="btn btn-danger btn-xs btn-protocol btn-del"><span class="fa fa-minus"></span> </a>
<a class="btn btn-primary btn-xs btn-protocol btn-add" style="display: none"><span class="fa fa-plus"></span></a>
</div>
</div>
{% endfor %}
</div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Auth' %}</h3> <h3>{% trans 'Auth' %}</h3>
{% bootstrap_field form.admin_user layout="horizontal" %} {% bootstrap_field form.admin_user layout="horizontal" %}
...@@ -55,6 +67,8 @@ ...@@ -55,6 +67,8 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% block extra %}
{% endblock %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
...@@ -73,11 +87,25 @@ ...@@ -73,11 +87,25 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var instanceId = "{{ object.id }}";
var protocolLen = 0;
function format(item) { function format(item) {
var group = item.element.parentElement.label; var group = item.element.parentElement.label;
return group + ':' + item.text; return group + ':' + item.text;
} }
function protocolBtnShow() {
$(".btn-protocol.btn-add").hide();
$(".btn-protocol.btn-add:last").show();
var btnDel = $(".btn-protocol.btn-del");
if (btnDel.length === 1) {
btnDel.addClass("disabled")
} else {
btnDel.removeClass("disabled")
}
}
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2({ $('.select2').select2({
allowClear: true allowClear: true
...@@ -89,20 +117,130 @@ $(document).ready(function () { ...@@ -89,20 +117,130 @@ $(document).ready(function () {
$('#id_nodes.select2').select2({ $('#id_nodes.select2').select2({
closeOnSelect: false closeOnSelect: false
}); });
$("#id_protocol").change(function (){ protocolBtnShow()
var protocol = $("#id_protocol option:selected").text(); })
var port = 22; .on("change", "#id_platform", function () {
if(protocol === 'rdp'){ if (instanceId !== "") {
port = 3389; return
} }
else if(protocol === 'telnet (beta)'){ var platform = $(this).val();
port = 23; var protocolRef = $(".protocols").find("select").first()
var protocol = protocolRef.val();
var protocolShould = "";
if (platform.startsWith("Windows")){
protocolShould = "rdp"
} else {
protocolShould = "ssh"
}
if (protocol !== protocolShould) {
protocolRef.val(protocolShould);
protocolRef.trigger("change")
}
})
.on("click", ".btn-protocol.btn-del", function () {
$(this).parent().parent().remove();
protocolBtnShow()
})
.on("click", ".btn-protocol.btn-add", function () {
var protocol = "";
var protocolsRef = $(".protocols");
var firstProtocolForm = protocolsRef.children().first();
var newProtocolForm = firstProtocolForm.clone();
var protocolChoices = $.map($(firstProtocolForm.find('select option')), function (option) {
return option.value
});
var protocolsSet = $.map(protocolsRef.find('select option:selected'), function(option) {
return option.value
});
for (var i=0;i<protocolChoices.length;i++) {
var p = protocolChoices[i];
if (protocolsSet.indexOf(p) === -1) {
protocol = p;
break
} }
else if(protocol === 'vnc'){ }
if (protocol === "") {
return
}
if (protocolLen === 0) {
protocolLen = protocolsRef.length;
}
var selectName = "form-" + protocolLen + "-name";
var selectId = "id_" + selectName;
var portName = "form-" + protocolLen + "-port";
var portId = "id_" + portName;
newProtocolForm.find("select").prop("name", selectName).prop("id", selectId);
newProtocolForm.find("input").prop("name", portName).prop("id", portId);
newProtocolForm.find("option[value='" + protocol + "']").attr("selected", true);
protocolsRef.append(newProtocolForm);
protocolLen += 1;
$("#" + selectId).trigger("change");
protocolBtnShow()
})
.on("change", ".protocol-name", function () {
var name = $(this).val();
var port = 22;
switch (name) {
case "ssh":
port = 22;
break;
case "rdp":
port = 3389;
break;
case "telnet":
port = 21;
break;
case "vnc":
port = 5901; port = 5901;
} break;
$("#id_port").val(port); default:
}); port = 22;
break
}
$(this).parent().parent().find(".protocol-port").val(port);
}) })
</script> </script>
{% block form_submit %}
<script>
$(document).ready(function () {
})
.on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url 'api-assets:asset-list' %}';
var redirect_to = '{% url "assets:asset-list" %}';
var form = $("form");
var protocols = {};
var data = form.serializeObject();
$.each(data, function (k, v) {
if (k.startsWith("form")){
delete data[k];
var _k = k.split("-");
var formName = _k.slice(0, 2).join("-");
var key = _k[_k.length-1];
if (!protocols[formName]) {
protocols[formName] = {}
}
protocols[formName][key] = v
}
});
protocols = $.map(protocols, function ( v) {
return v
});
data["protocols"] = protocols;
if (typeof data["nodes"] == "string") {
data["nodes"] = [data["nodes"]]
}
var props = {
url: the_url,
data: data,
method: "POST",
form: form,
redirect_to: redirect_to
};
formSubmit(props);
})
</script>
{% endblock %}
{% endblock %} {% endblock %}
\ No newline at end of file
{% extends '_base_create_update.html' %} {% extends 'assets/asset_create.html' %}
{% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load i18n %} {% load i18n %}
{% load asset_tags %}
{% load common_tags %}
{% block custom_head_css_js_create %}
<link href="{% static "css/plugins/inputTags.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/inputTags.jquery.min.js" %}"></script>
{% endblock %}
{% block form %}
<form action="" method="post" class="form-horizontal">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
<h3>{% trans 'Basic' %}</h3>
{% bootstrap_field form.hostname layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.protocol layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.platform layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.domain layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Auth' %}</h3>
{% bootstrap_field form.admin_user layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Node' %}</h3>
{% bootstrap_field form.nodes layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Labels' %}</h3>
<div class="form-group">
<label for="{{ form.labels.id_for_label }}" class="col-md-2 control-label">{% trans 'Label' %}</label>
<div class="col-md-9">
<select name="labels" class="select2 labels" data-placeholder="{% trans 'Label' %}" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
{% for name, labels in form.labels.field.queryset|group_labels %}
<optgroup label="{{ name }}">
{% for label in labels %}
{% if label in form.labels.initial %}
<option value="{{ label.id }}" selected>{{ label.value }}</option>
{% else %}
<option value="{{ label.id }}">{{ label.value }}</option>
{% endif %}
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
</div>
{% block extra %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Configuration' %}</h3> <h3>{% trans 'Configuration' %}</h3>
{% bootstrap_field form.number layout="horizontal" %} {% bootstrap_field form.number layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3>
{% bootstrap_field form.comment layout="horizontal" %}
{% bootstrap_field form.is_active layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block form_submit %}
<script> <script>
function format(item) {
var group = item.element.parentElement.label;
return group + ':' + item.text;
}
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2({
allowClear: true
});
$(".labels").select2({
allowClear: true,
templateSelection: format
});
}) })
</script> .on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url 'api-assets:asset-detail' pk=object.id %}';
var redirect_to = '{% url "assets:asset-list" %}';
var form = $("form");
var protocols = {};
var data = form.serializeObject();
$.each(data, function (k, v) {
if (k.startsWith("form")){
delete data[k];
var _k = k.split("-");
var formName = _k.slice(0, 2).join("-");
var key = _k[_k.length-1];
if (!protocols[formName]) {
protocols[formName] = {}
}
protocols[formName][key] = v
}
});
protocols = $.map(protocols, function ( v) {
return v
});
data["protocols"] = protocols;
if (typeof data["nodes"] == "string") {
data["nodes"] = [data["nodes"]]
}
var props = {
url: the_url,
data: data,
method: "PUT",
form: form,
redirect_to: redirect_to
};
formSubmit(props);
});
</script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -23,6 +23,7 @@ from django.utils import timezone ...@@ -23,6 +23,7 @@ from django.utils import timezone
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect from django.shortcuts import redirect
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.forms.formsets import formset_factory
from common.mixins import JSONResponseMixin from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger from common.utils import get_object_or_none, get_logger
...@@ -30,8 +31,6 @@ from common.permissions import AdminUserRequiredMixin ...@@ -30,8 +31,6 @@ from common.permissions import AdminUserRequiredMixin
from common.const import ( from common.const import (
create_success_msg, update_success_msg, KEY_CACHE_RESOURCES_ID create_success_msg, update_success_msg, KEY_CACHE_RESOURCES_ID
) )
from ..const import CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX
from orgs.utils import current_org
from .. import forms from .. import forms
from ..models import Asset, AdminUser, SystemUser, Label, Node, Domain from ..models import Asset, AdminUser, SystemUser, Label, Node, Domain
...@@ -101,10 +100,30 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): ...@@ -101,10 +100,30 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
form["nodes"].initial = node form["nodes"].initial = node
return form return form
def get_protocol_formset(self):
ProtocolFormset = formset_factory(forms.ProtocolForm, extra=0, min_num=1, max_num=5)
if self.request.method == "POST":
formset = ProtocolFormset(self.request.POST)
else:
formset = ProtocolFormset()
return formset
def form_valid(self, form):
formset = self.get_protocol_formset()
valid = formset.is_valid()
if not valid:
return self.form_invalid(form)
protocols = formset.save()
instance = super().form_valid(form)
instance.protocols.set(protocols)
return instance
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
formset = self.get_protocol_formset()
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create asset'), 'action': _('Create asset'),
'formset': formset,
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -159,10 +178,21 @@ class AssetUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView): ...@@ -159,10 +178,21 @@ class AssetUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
template_name = 'assets/asset_update.html' template_name = 'assets/asset_update.html'
success_url = reverse_lazy('assets:asset-list') success_url = reverse_lazy('assets:asset-list')
def get_protocol_formset(self):
ProtocolFormset = formset_factory(forms.ProtocolForm, extra=0, min_num=1, max_num=5)
if self.request.method == "POST":
formset = ProtocolFormset(self.request.POST)
else:
initial_data = [{"name": p.name, "port": p.port} for p in self.object.protocols.all()]
formset = ProtocolFormset(initial=initial_data)
return formset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
formset = self.get_protocol_formset()
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Update asset'), 'action': _('Update asset'),
'formset': formset,
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
......
# -*- coding: utf-8 -*-
#
from django.db import transaction
def on_transaction_commit(func):
"""
如果不调用on_commit, 对象创建时添加多对多字段值失败
"""
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))
return inner
...@@ -5,6 +5,7 @@ from django.http import JsonResponse ...@@ -5,6 +5,7 @@ from django.http import JsonResponse
from django.utils import timezone from django.utils import timezone
from django.core.cache import cache from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from rest_framework.utils import html from rest_framework.utils import html
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
...@@ -203,3 +204,26 @@ class DatetimeSearchMixin: ...@@ -203,3 +204,26 @@ class DatetimeSearchMixin:
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.get_date_range() self.get_date_range()
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
class ApiMessageMixin:
success_message = _("%(name)s was %(action)s successfully")
_action_map = {"create": _("create"), "update": _("update")}
def get_success_message(self, cleaned_data):
data = {k: v for k, v in cleaned_data.items()}
action = getattr(self, "action", "create")
data["action"] = self._action_map.get(action)
message = self.success_message % data
return message
def dispatch(self, request, *args, **kwargs):
resp = super().dispatch(request, *args, **kwargs)
if request.method.lower() in ("get", "delete"):
return resp
if resp.status_code >= 400:
return resp
message = self.get_success_message(resp.data)
if message:
messages.success(request, message)
return resp
...@@ -3,5 +3,22 @@ ...@@ -3,5 +3,22 @@
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.validators import (
UniqueTogetherValidator, ValidationError
)
alphanumeric = RegexValidator(r'^[0-9a-zA-Z_@\-\.]*$', _('Special char not allowed'))
\ No newline at end of file alphanumeric = RegexValidator(r'^[0-9a-zA-Z_@\-\.]*$', _('Special char not allowed'))
class ProjectUniqueValidator(UniqueTogetherValidator):
def __call__(self, attrs):
try:
super().__call__(attrs)
except ValidationError as e:
errors = {}
for field in self.fields:
if field == "org_id":
continue
errors[field] = _('This field must be unique.')
raise ValidationError(errors)
...@@ -274,7 +274,10 @@ class Config(dict): ...@@ -274,7 +274,10 @@ class Config(dict):
return v return v
tp = type(default_value) tp = type(default_value)
try: try:
v = tp(v) if tp in [list, dict]:
v = json.loads(v)
else:
v = tp(v)
except Exception: except Exception:
pass pass
return v return v
......
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n" "Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-05-21 21:09+0800\n" "POT-Creation-Date: 2019-05-24 10:29+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n" "Language-Team: Jumpserver team<ibuler@qq.com>\n"
...@@ -76,7 +76,7 @@ msgstr "运行参数" ...@@ -76,7 +76,7 @@ msgstr "运行参数"
#: applications/templates/applications/remote_app_list.html:22 #: applications/templates/applications/remote_app_list.html:22
#: applications/templates/applications/user_remote_app_list.html:18 #: applications/templates/applications/user_remote_app_list.html:18
#: assets/forms/domain.py:15 assets/forms/label.py:13 #: assets/forms/domain.py:15 assets/forms/label.py:13
#: assets/models/asset.py:279 assets/models/authbook.py:27 #: assets/models/asset.py:296 assets/models/authbook.py:27
#: assets/serializers/admin_user.py:23 assets/serializers/system_user.py:28 #: assets/serializers/admin_user.py:23 assets/serializers/system_user.py:28
#: assets/templates/assets/admin_user_list.html:49 #: assets/templates/assets/admin_user_list.html:49
#: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_detail.html:60
...@@ -133,10 +133,10 @@ msgstr "系统用户" ...@@ -133,10 +133,10 @@ msgstr "系统用户"
#: applications/templates/applications/remote_app_list.html:20 #: applications/templates/applications/remote_app_list.html:20
#: applications/templates/applications/user_remote_app_list.html:16 #: applications/templates/applications/user_remote_app_list.html:16
#: assets/forms/domain.py:73 assets/forms/user.py:84 assets/forms/user.py:146 #: assets/forms/domain.py:73 assets/forms/user.py:84 assets/forms/user.py:146
#: assets/models/base.py:26 assets/models/cluster.py:18 #: assets/models/asset.py:66 assets/models/base.py:26
#: assets/models/cmd_filter.py:20 assets/models/domain.py:20 #: assets/models/cluster.py:18 assets/models/cmd_filter.py:20
#: assets/models/group.py:20 assets/models/label.py:18 #: assets/models/domain.py:20 assets/models/group.py:20
#: assets/templates/assets/admin_user_detail.html:56 #: assets/models/label.py:18 assets/templates/assets/admin_user_detail.html:56
#: assets/templates/assets/admin_user_list.html:47 #: assets/templates/assets/admin_user_list.html:47
#: assets/templates/assets/cmd_filter_detail.html:61 #: assets/templates/assets/cmd_filter_detail.html:61
#: assets/templates/assets/cmd_filter_list.html:24 #: assets/templates/assets/cmd_filter_list.html:24
...@@ -204,7 +204,7 @@ msgstr "参数" ...@@ -204,7 +204,7 @@ msgstr "参数"
#: applications/models/remote_app.py:43 #: applications/models/remote_app.py:43
#: applications/templates/applications/remote_app_detail.html:77 #: applications/templates/applications/remote_app_detail.html:77
#: assets/models/asset.py:109 assets/models/base.py:34 #: assets/models/asset.py:126 assets/models/base.py:34
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:25 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:25
#: assets/models/cmd_filter.py:58 assets/models/group.py:21 #: assets/models/cmd_filter.py:58 assets/models/group.py:21
#: assets/templates/assets/admin_user_detail.html:68 #: assets/templates/assets/admin_user_detail.html:68
...@@ -226,7 +226,7 @@ msgstr "创建者" ...@@ -226,7 +226,7 @@ msgstr "创建者"
#: applications/models/remote_app.py:46 #: applications/models/remote_app.py:46
#: applications/templates/applications/remote_app_detail.html:73 #: applications/templates/applications/remote_app_detail.html:73
#: assets/models/asset.py:110 assets/models/cluster.py:26 #: assets/models/asset.py:127 assets/models/cluster.py:26
#: assets/models/domain.py:23 assets/models/group.py:22 #: assets/models/domain.py:23 assets/models/group.py:22
#: assets/models/label.py:25 assets/serializers/admin_user.py:37 #: assets/models/label.py:25 assets/serializers/admin_user.py:37
#: assets/templates/assets/admin_user_detail.html:64 #: assets/templates/assets/admin_user_detail.html:64
...@@ -252,7 +252,7 @@ msgstr "创建日期" ...@@ -252,7 +252,7 @@ msgstr "创建日期"
#: applications/templates/applications/remote_app_detail.html:81 #: applications/templates/applications/remote_app_detail.html:81
#: applications/templates/applications/remote_app_list.html:24 #: applications/templates/applications/remote_app_list.html:24
#: applications/templates/applications/user_remote_app_list.html:20 #: applications/templates/applications/user_remote_app_list.html:20
#: assets/models/asset.py:111 assets/models/base.py:31 #: assets/models/asset.py:128 assets/models/base.py:31
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:22 #: assets/models/cluster.py:29 assets/models/cmd_filter.py:22
#: assets/models/cmd_filter.py:55 assets/models/domain.py:21 #: assets/models/cmd_filter.py:55 assets/models/domain.py:21
#: assets/models/domain.py:53 assets/models/group.py:23 #: assets/models/domain.py:53 assets/models/group.py:23
...@@ -306,7 +306,7 @@ msgstr "远程应用" ...@@ -306,7 +306,7 @@ msgstr "远程应用"
#: assets/templates/assets/_system_user.html:75 #: assets/templates/assets/_system_user.html:75
#: assets/templates/assets/admin_user_create_update.html:45 #: assets/templates/assets/admin_user_create_update.html:45
#: assets/templates/assets/asset_bulk_update.html:23 #: assets/templates/assets/asset_bulk_update.html:23
#: assets/templates/assets/asset_create.html:67 #: assets/templates/assets/asset_create.html:79
#: assets/templates/assets/asset_update.html:71 #: assets/templates/assets/asset_update.html:71
#: assets/templates/assets/cmd_filter_create_update.html:15 #: assets/templates/assets/cmd_filter_create_update.html:15
#: assets/templates/assets/cmd_filter_rule_create_update.html:40 #: assets/templates/assets/cmd_filter_rule_create_update.html:40
...@@ -342,7 +342,7 @@ msgstr "重置" ...@@ -342,7 +342,7 @@ msgstr "重置"
#: assets/templates/assets/_system_user.html:76 #: assets/templates/assets/_system_user.html:76
#: assets/templates/assets/admin_user_create_update.html:46 #: assets/templates/assets/admin_user_create_update.html:46
#: assets/templates/assets/asset_bulk_update.html:24 #: assets/templates/assets/asset_bulk_update.html:24
#: assets/templates/assets/asset_create.html:68 #: assets/templates/assets/asset_create.html:80
#: assets/templates/assets/asset_list.html:125 #: assets/templates/assets/asset_list.html:125
#: assets/templates/assets/asset_update.html:72 #: assets/templates/assets/asset_update.html:72
#: assets/templates/assets/cmd_filter_create_update.html:16 #: assets/templates/assets/cmd_filter_create_update.html:16
...@@ -556,9 +556,9 @@ msgstr "连接" ...@@ -556,9 +556,9 @@ msgstr "连接"
#: assets/templates/assets/system_user_detail.html:22 #: assets/templates/assets/system_user_detail.html:22
#: assets/views/admin_user.py:29 assets/views/admin_user.py:47 #: assets/views/admin_user.py:29 assets/views/admin_user.py:47
#: assets/views/admin_user.py:63 assets/views/admin_user.py:78 #: assets/views/admin_user.py:63 assets/views/admin_user.py:78
#: assets/views/admin_user.py:102 assets/views/asset.py:53 #: assets/views/admin_user.py:102 assets/views/asset.py:52
#: assets/views/asset.py:69 assets/views/asset.py:106 assets/views/asset.py:147 #: assets/views/asset.py:68 assets/views/asset.py:124 assets/views/asset.py:166
#: assets/views/asset.py:164 assets/views/asset.py:188 #: assets/views/asset.py:183 assets/views/asset.py:207
#: assets/views/cmd_filter.py:30 assets/views/cmd_filter.py:46 #: assets/views/cmd_filter.py:30 assets/views/cmd_filter.py:46
#: assets/views/cmd_filter.py:62 assets/views/cmd_filter.py:78 #: assets/views/cmd_filter.py:62 assets/views/cmd_filter.py:78
#: assets/views/cmd_filter.py:97 assets/views/cmd_filter.py:130 #: assets/views/cmd_filter.py:97 assets/views/cmd_filter.py:130
...@@ -589,7 +589,12 @@ msgstr "远程应用详情" ...@@ -589,7 +589,12 @@ msgstr "远程应用详情"
msgid "My RemoteApp" msgid "My RemoteApp"
msgstr "我的远程应用" msgstr "我的远程应用"
#: assets/api/asset.py:126 #: assets/api/asset.py:50
#, python-format
msgid "%(hostname)s was %(action)s successfully"
msgstr "%(hostname)s %(action)s成功"
#: assets/api/asset.py:127
msgid "Please select assets that need to be updated" msgid "Please select assets that need to be updated"
msgstr "请选择需要更新的资产" msgstr "请选择需要更新的资产"
...@@ -605,7 +610,7 @@ msgstr "更新节点资产硬件信息: {}" ...@@ -605,7 +610,7 @@ msgstr "更新节点资产硬件信息: {}"
msgid "Test if the assets under the node are connectable: {}" msgid "Test if the assets under the node are connectable: {}"
msgstr "测试节点下资产是否可连接: {}" msgstr "测试节点下资产是否可连接: {}"
#: assets/forms/asset.py:27 assets/models/asset.py:80 assets/models/user.py:133 #: assets/forms/asset.py:45 assets/models/asset.py:97 assets/models/user.py:133
#: assets/templates/assets/asset_detail.html:194 #: assets/templates/assets/asset_detail.html:194
#: assets/templates/assets/asset_detail.html:202 #: assets/templates/assets/asset_detail.html:202
#: assets/templates/assets/system_user_asset.html:95 #: assets/templates/assets/system_user_asset.html:95
...@@ -614,7 +619,7 @@ msgstr "测试节点下资产是否可连接: {}" ...@@ -614,7 +619,7 @@ msgstr "测试节点下资产是否可连接: {}"
msgid "Nodes" msgid "Nodes"
msgstr "节点管理" msgstr "节点管理"
#: assets/forms/asset.py:30 assets/forms/asset.py:66 assets/models/asset.py:84 #: assets/forms/asset.py:48 assets/forms/asset.py:83 assets/models/asset.py:101
#: assets/models/cluster.py:19 assets/models/user.py:91 #: assets/models/cluster.py:19 assets/models/user.py:91
#: assets/templates/assets/asset_detail.html:80 templates/_nav.html:24 #: assets/templates/assets/asset_detail.html:80 templates/_nav.html:24
#: xpack/plugins/cloud/models.py:124 #: xpack/plugins/cloud/models.py:124
...@@ -623,9 +628,9 @@ msgstr "节点管理" ...@@ -623,9 +628,9 @@ msgstr "节点管理"
msgid "Admin user" msgid "Admin user"
msgstr "管理用户" msgstr "管理用户"
#: assets/forms/asset.py:33 assets/forms/asset.py:69 assets/forms/asset.py:109 #: assets/forms/asset.py:51 assets/forms/asset.py:86 assets/forms/asset.py:125
#: assets/templates/assets/asset_create.html:36 #: assets/templates/assets/asset_create.html:48
#: assets/templates/assets/asset_create.html:38 #: assets/templates/assets/asset_create.html:50
#: assets/templates/assets/asset_list.html:93 #: assets/templates/assets/asset_list.html:93
#: assets/templates/assets/asset_update.html:41 #: assets/templates/assets/asset_update.html:41
#: assets/templates/assets/asset_update.html:43 #: assets/templates/assets/asset_update.html:43
...@@ -634,7 +639,7 @@ msgstr "管理用户" ...@@ -634,7 +639,7 @@ msgstr "管理用户"
msgid "Label" msgid "Label"
msgstr "标签" msgstr "标签"
#: assets/forms/asset.py:37 assets/forms/asset.py:73 assets/models/asset.py:79 #: assets/forms/asset.py:54 assets/forms/asset.py:89 assets/models/asset.py:96
#: assets/models/domain.py:26 assets/models/domain.py:52 #: assets/models/domain.py:26 assets/models/domain.py:52
#: assets/templates/assets/asset_detail.html:84 #: assets/templates/assets/asset_detail.html:84
#: assets/templates/assets/user_asset_list.html:169 #: assets/templates/assets/user_asset_list.html:169
...@@ -642,9 +647,9 @@ msgstr "标签" ...@@ -642,9 +647,9 @@ msgstr "标签"
msgid "Domain" msgid "Domain"
msgstr "网域" msgstr "网域"
#: assets/forms/asset.py:41 assets/forms/asset.py:63 assets/forms/asset.py:77 #: assets/forms/asset.py:58 assets/forms/asset.py:80 assets/forms/asset.py:93
#: assets/forms/asset.py:112 assets/models/node.py:31 #: assets/forms/asset.py:128 assets/models/node.py:31
#: assets/templates/assets/asset_create.html:30 #: assets/templates/assets/asset_create.html:42
#: assets/templates/assets/asset_update.html:35 #: assets/templates/assets/asset_update.html:35
#: perms/forms/asset_permission.py:49 perms/forms/asset_permission.py:59 #: perms/forms/asset_permission.py:49 perms/forms/asset_permission.py:59
#: perms/models/asset_permission.py:57 #: perms/models/asset_permission.py:57
...@@ -660,7 +665,7 @@ msgstr "网域" ...@@ -660,7 +665,7 @@ msgstr "网域"
msgid "Node" msgid "Node"
msgstr "节点" msgstr "节点"
#: assets/forms/asset.py:45 assets/forms/asset.py:81 #: assets/forms/asset.py:62 assets/forms/asset.py:97
msgid "" msgid ""
"root or other NOPASSWD sudo privilege user existed in asset,If asset is " "root or other NOPASSWD sudo privilege user existed in asset,If asset is "
"windows or other set any one, more see admin user left menu" "windows or other set any one, more see admin user left menu"
...@@ -668,17 +673,17 @@ msgstr "" ...@@ -668,17 +673,17 @@ msgstr ""
"root或其他拥有NOPASSWD: ALL权限的用户, 如果是windows或其它硬件可以随意设置一" "root或其他拥有NOPASSWD: ALL权限的用户, 如果是windows或其它硬件可以随意设置一"
"个, 更多信息查看左侧 `管理用户` 菜单" "个, 更多信息查看左侧 `管理用户` 菜单"
#: assets/forms/asset.py:48 assets/forms/asset.py:84 #: assets/forms/asset.py:65 assets/forms/asset.py:100
msgid "Windows 2016 RDP protocol is different, If is window 2016, set it" msgid "Windows 2016 RDP protocol is different, If is window 2016, set it"
msgstr "Windows 2016的RDP协议与之前不同,如果是请设置" msgstr "Windows 2016的RDP协议与之前不同,如果是请设置"
#: assets/forms/asset.py:49 assets/forms/asset.py:85 #: assets/forms/asset.py:66 assets/forms/asset.py:101
msgid "" msgid ""
"If your have some network not connect with each other, you can set domain" "If your have some network not connect with each other, you can set domain"
msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,使用网域网关跳转登录" msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,使用网域网关跳转登录"
#: assets/forms/asset.py:92 assets/forms/asset.py:96 assets/forms/domain.py:17 #: assets/forms/asset.py:108 assets/forms/asset.py:112
#: assets/forms/label.py:15 #: assets/forms/domain.py:17 assets/forms/label.py:15
#: perms/templates/perms/asset_permission_asset.html:88 #: perms/templates/perms/asset_permission_asset.html:88
#: xpack/plugins/change_auth_plan/forms.py:105 #: xpack/plugins/change_auth_plan/forms.py:105
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:84 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:84
...@@ -794,7 +799,17 @@ msgstr "如果选择手动登录模式,用户名和密码可以不填写" ...@@ -794,7 +799,17 @@ msgstr "如果选择手动登录模式,用户名和密码可以不填写"
msgid "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig" msgid "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig"
msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
#: assets/models/asset.py:74 assets/models/domain.py:49 #: assets/models/asset.py:67 assets/models/asset.py:92
#: assets/models/domain.py:50 assets/templates/assets/admin_user_assets.html:50
#: assets/templates/assets/asset_detail.html:72
#: assets/templates/assets/domain_gateway_list.html:69
#: assets/templates/assets/system_user_asset.html:52
#: assets/templates/assets/user_asset_list.html:164
#: settings/templates/settings/replay_storage_create.html:59
msgid "Port"
msgstr "端口"
#: assets/models/asset.py:87 assets/models/domain.py:49
#: assets/templates/assets/_asset_list_modal.html:46 #: assets/templates/assets/_asset_list_modal.html:46
#: assets/templates/assets/admin_user_assets.html:49 #: assets/templates/assets/admin_user_assets.html:49
#: assets/templates/assets/asset_detail.html:64 #: assets/templates/assets/asset_detail.html:64
...@@ -811,7 +826,7 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" ...@@ -811,7 +826,7 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
msgid "IP" msgid "IP"
msgstr "IP" msgstr "IP"
#: assets/models/asset.py:75 assets/templates/assets/_asset_list_modal.html:45 #: assets/models/asset.py:88 assets/templates/assets/_asset_list_modal.html:45
#: assets/templates/assets/_asset_user_auth_modal.html:9 #: assets/templates/assets/_asset_user_auth_modal.html:9
#: assets/templates/assets/_asset_user_view_auth_modal.html:25 #: assets/templates/assets/_asset_user_view_auth_modal.html:25
#: assets/templates/assets/admin_user_assets.html:48 #: assets/templates/assets/admin_user_assets.html:48
...@@ -828,8 +843,9 @@ msgstr "IP" ...@@ -828,8 +843,9 @@ msgstr "IP"
msgid "Hostname" msgid "Hostname"
msgstr "主机名" msgstr "主机名"
#: assets/models/asset.py:76 assets/models/domain.py:51 #: assets/models/asset.py:91 assets/models/asset.py:94
#: assets/models/user.py:136 assets/templates/assets/asset_detail.html:76 #: assets/models/domain.py:51 assets/models/user.py:136
#: assets/templates/assets/asset_detail.html:76
#: assets/templates/assets/domain_gateway_list.html:70 #: assets/templates/assets/domain_gateway_list.html:70
#: assets/templates/assets/system_user_detail.html:70 #: assets/templates/assets/system_user_detail.html:70
#: assets/templates/assets/system_user_list.html:53 #: assets/templates/assets/system_user_list.html:53
...@@ -838,108 +854,98 @@ msgstr "主机名" ...@@ -838,108 +854,98 @@ msgstr "主机名"
msgid "Protocol" msgid "Protocol"
msgstr "协议" msgstr "协议"
#: assets/models/asset.py:77 assets/models/domain.py:50 #: assets/models/asset.py:95 assets/templates/assets/asset_detail.html:108
#: assets/templates/assets/admin_user_assets.html:50
#: assets/templates/assets/asset_detail.html:72
#: assets/templates/assets/domain_gateway_list.html:69
#: assets/templates/assets/system_user_asset.html:52
#: assets/templates/assets/user_asset_list.html:164
#: settings/templates/settings/replay_storage_create.html:59
msgid "Port"
msgstr "端口"
#: assets/models/asset.py:78 assets/templates/assets/asset_detail.html:108
#: assets/templates/assets/user_asset_list.html:166 #: assets/templates/assets/user_asset_list.html:166
msgid "Platform" msgid "Platform"
msgstr "系统平台" msgstr "系统平台"
#: assets/models/asset.py:81 assets/models/cmd_filter.py:21 #: assets/models/asset.py:98 assets/models/cmd_filter.py:21
#: assets/models/domain.py:54 assets/models/label.py:22 #: assets/models/domain.py:54 assets/models/label.py:22
#: assets/templates/assets/asset_detail.html:116 #: assets/templates/assets/asset_detail.html:116
#: assets/templates/assets/user_asset_list.html:170 #: assets/templates/assets/user_asset_list.html:170
msgid "Is active" msgid "Is active"
msgstr "激活" msgstr "激活"
#: assets/models/asset.py:87 assets/templates/assets/asset_detail.html:68 #: assets/models/asset.py:104 assets/templates/assets/asset_detail.html:68
msgid "Public IP" msgid "Public IP"
msgstr "公网IP" msgstr "公网IP"
#: assets/models/asset.py:88 assets/templates/assets/asset_detail.html:124 #: assets/models/asset.py:105 assets/templates/assets/asset_detail.html:124
msgid "Asset number" msgid "Asset number"
msgstr "资产编号" msgstr "资产编号"
#: assets/models/asset.py:91 assets/templates/assets/asset_detail.html:88 #: assets/models/asset.py:108 assets/templates/assets/asset_detail.html:88
msgid "Vendor" msgid "Vendor"
msgstr "制造商" msgstr "制造商"
#: assets/models/asset.py:92 assets/templates/assets/asset_detail.html:92 #: assets/models/asset.py:109 assets/templates/assets/asset_detail.html:92
msgid "Model" msgid "Model"
msgstr "型号" msgstr "型号"
#: assets/models/asset.py:93 assets/templates/assets/asset_detail.html:120 #: assets/models/asset.py:110 assets/templates/assets/asset_detail.html:120
msgid "Serial number" msgid "Serial number"
msgstr "序列号" msgstr "序列号"
#: assets/models/asset.py:95 #: assets/models/asset.py:112
msgid "CPU model" msgid "CPU model"
msgstr "CPU型号" msgstr "CPU型号"
#: assets/models/asset.py:96 #: assets/models/asset.py:113
#: xpack/plugins/license/templates/license/license_detail.html:80 #: xpack/plugins/license/templates/license/license_detail.html:80
msgid "CPU count" msgid "CPU count"
msgstr "CPU数量" msgstr "CPU数量"
#: assets/models/asset.py:97 #: assets/models/asset.py:114
msgid "CPU cores" msgid "CPU cores"
msgstr "CPU核数" msgstr "CPU核数"
#: assets/models/asset.py:98 #: assets/models/asset.py:115
msgid "CPU vcpus" msgid "CPU vcpus"
msgstr "CPU总数" msgstr "CPU总数"
#: assets/models/asset.py:99 assets/templates/assets/asset_detail.html:100 #: assets/models/asset.py:116 assets/templates/assets/asset_detail.html:100
msgid "Memory" msgid "Memory"
msgstr "内存" msgstr "内存"
#: assets/models/asset.py:100 #: assets/models/asset.py:117
msgid "Disk total" msgid "Disk total"
msgstr "硬盘大小" msgstr "硬盘大小"
#: assets/models/asset.py:101 #: assets/models/asset.py:118
msgid "Disk info" msgid "Disk info"
msgstr "硬盘信息" msgstr "硬盘信息"
#: assets/models/asset.py:103 assets/templates/assets/asset_detail.html:112 #: assets/models/asset.py:120 assets/templates/assets/asset_detail.html:112
#: assets/templates/assets/user_asset_list.html:167 #: assets/templates/assets/user_asset_list.html:167
msgid "OS" msgid "OS"
msgstr "操作系统" msgstr "操作系统"
#: assets/models/asset.py:104 #: assets/models/asset.py:121
msgid "OS version" msgid "OS version"
msgstr "系统版本" msgstr "系统版本"
#: assets/models/asset.py:105 #: assets/models/asset.py:122
msgid "OS arch" msgid "OS arch"
msgstr "系统架构" msgstr "系统架构"
#: assets/models/asset.py:106 #: assets/models/asset.py:123
msgid "Hostname raw" msgid "Hostname raw"
msgstr "主机名原始" msgstr "主机名原始"
#: assets/models/asset.py:108 assets/templates/assets/asset_create.html:34 #: assets/models/asset.py:125 assets/templates/assets/asset_create.html:46
#: assets/templates/assets/asset_detail.html:231 #: assets/templates/assets/asset_detail.html:231
#: assets/templates/assets/asset_update.html:39 templates/_nav.html:26 #: assets/templates/assets/asset_update.html:39 templates/_nav.html:26
msgid "Labels" msgid "Labels"
msgstr "标签管理" msgstr "标签管理"
#: assets/models/asset.py:117 assets/models/base.py:38 #: assets/models/asset.py:134 assets/models/base.py:38
#: assets/serializers/admin_user.py:22 assets/serializers/system_user.py:19 #: assets/serializers/admin_user.py:22 assets/serializers/system_user.py:19
#: assets/templates/assets/admin_user_list.html:51 #: assets/templates/assets/admin_user_list.html:51
#: assets/templates/assets/system_user_list.html:57 #: assets/templates/assets/system_user_list.html:57
msgid "Unreachable" msgid "Unreachable"
msgstr "不可达" msgstr "不可达"
#: assets/models/asset.py:118 assets/models/base.py:39 #: assets/models/asset.py:135 assets/models/base.py:39
#: assets/serializers/admin_user.py:24 assets/serializers/system_user.py:27 #: assets/serializers/admin_user.py:24 assets/serializers/system_user.py:27
#: assets/templates/assets/admin_user_assets.html:51 #: assets/templates/assets/admin_user_assets.html:51
#: assets/templates/assets/admin_user_list.html:50 #: assets/templates/assets/admin_user_list.html:50
...@@ -951,7 +957,7 @@ msgstr "不可达" ...@@ -951,7 +957,7 @@ msgstr "不可达"
msgid "Reachable" msgid "Reachable"
msgstr "可连接" msgstr "可连接"
#: assets/models/asset.py:119 assets/models/base.py:40 #: assets/models/asset.py:136 assets/models/base.py:40
#: authentication/utils.py:9 xpack/plugins/license/models.py:78 #: authentication/utils.py:9 xpack/plugins/license/models.py:78
msgid "Unknown" msgid "Unknown"
msgstr "未知" msgstr "未知"
...@@ -1198,18 +1204,22 @@ msgstr "%(value)s is not an even number" ...@@ -1198,18 +1204,22 @@ msgstr "%(value)s is not an even number"
msgid "Date updated" msgid "Date updated"
msgstr "更新日期" msgstr "更新日期"
#: assets/serializers/asset.py:43 #: assets/serializers/asset.py:52
msgid "Hardware info" msgid "Hardware info"
msgstr "硬件信息" msgstr "硬件信息"
#: assets/serializers/asset.py:44 #: assets/serializers/asset.py:53
msgid "Connectivity" msgid "Connectivity"
msgstr "连接" msgstr "连接"
#: assets/serializers/asset.py:45 #: assets/serializers/asset.py:54
msgid "Org name" msgid "Org name"
msgstr "组织名" msgstr "组织名"
#: assets/serializers/asset.py:70
msgid "Protocol duplicate: {}"
msgstr "协议重复: {}"
#: assets/serializers/asset_user.py:23 users/forms.py:230 #: assets/serializers/asset_user.py:23 users/forms.py:230
#: users/models/user.py:91 users/templates/users/first_login.html:42 #: users/models/user.py:91 users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:46 #: users/templates/users/user_password_update.html:46
...@@ -1341,7 +1351,7 @@ msgstr "启用MFA" ...@@ -1341,7 +1351,7 @@ msgstr "启用MFA"
msgid "Import assets" msgid "Import assets"
msgstr "导入资产" msgstr "导入资产"
#: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:54 #: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:53
#: templates/_nav.html:22 xpack/plugins/change_auth_plan/views.py:110 #: templates/_nav.html:22 xpack/plugins/change_auth_plan/views.py:110
msgid "Asset list" msgid "Asset list"
msgstr "资产列表" msgstr "资产列表"
...@@ -1437,7 +1447,7 @@ msgid "Basic" ...@@ -1437,7 +1447,7 @@ msgid "Basic"
msgstr "基本" msgstr "基本"
#: assets/templates/assets/_system_user.html:44 #: assets/templates/assets/_system_user.html:44
#: assets/templates/assets/asset_create.html:26 #: assets/templates/assets/asset_create.html:38
#: assets/templates/assets/asset_update.html:31 #: assets/templates/assets/asset_update.html:31
#: assets/templates/assets/gateway_create_update.html:45 #: assets/templates/assets/gateway_create_update.html:45
#: users/templates/users/_user.html:21 #: users/templates/users/_user.html:21
...@@ -1449,7 +1459,7 @@ msgid "Auto generate key" ...@@ -1449,7 +1459,7 @@ msgid "Auto generate key"
msgstr "自动生成密钥" msgstr "自动生成密钥"
#: assets/templates/assets/_system_user.html:69 #: assets/templates/assets/_system_user.html:69
#: assets/templates/assets/asset_create.html:60 #: assets/templates/assets/asset_create.html:72
#: assets/templates/assets/asset_update.html:64 #: assets/templates/assets/asset_update.html:64
#: assets/templates/assets/gateway_create_update.html:53 #: assets/templates/assets/gateway_create_update.html:53
#: perms/templates/perms/asset_permission_create_update.html:53 #: perms/templates/perms/asset_permission_create_update.html:53
...@@ -1470,7 +1480,7 @@ msgstr "更新系统用户" ...@@ -1470,7 +1480,7 @@ msgstr "更新系统用户"
#: assets/templates/assets/_user_asset_detail_modal.html:11 #: assets/templates/assets/_user_asset_detail_modal.html:11
#: assets/templates/assets/asset_asset_user_list.html:13 #: assets/templates/assets/asset_asset_user_list.html:13
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:189 #: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:208
msgid "Asset detail" msgid "Asset detail"
msgstr "资产详情" msgstr "资产详情"
...@@ -1611,7 +1621,7 @@ msgid "Please select file" ...@@ -1611,7 +1621,7 @@ msgid "Please select file"
msgstr "选择文件" msgstr "选择文件"
#: assets/templates/assets/asset_asset_user_list.html:16 #: assets/templates/assets/asset_asset_user_list.html:16
#: assets/templates/assets/asset_detail.html:23 assets/views/asset.py:70 #: assets/templates/assets/asset_detail.html:23 assets/views/asset.py:69
msgid "Asset user list" msgid "Asset user list"
msgstr "资产用户列表" msgstr "资产用户列表"
...@@ -1643,6 +1653,10 @@ msgstr "选择需要修改属性" ...@@ -1643,6 +1653,10 @@ msgstr "选择需要修改属性"
msgid "Select all" msgid "Select all"
msgstr "全选" msgstr "全选"
#: assets/templates/assets/asset_create.html:24
msgid "Protocols"
msgstr "协议"
#: assets/templates/assets/asset_detail.html:96 #: assets/templates/assets/asset_detail.html:96
msgid "CPU" msgid "CPU"
msgstr "CPU" msgstr "CPU"
...@@ -1690,7 +1704,7 @@ msgstr "" ...@@ -1690,7 +1704,7 @@ msgstr ""
"左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的," "左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,"
"右侧是属于该节点下的资产" "右侧是属于该节点下的资产"
#: assets/templates/assets/asset_list.html:69 assets/views/asset.py:107 #: assets/templates/assets/asset_list.html:69 assets/views/asset.py:125
msgid "Create asset" msgid "Create asset"
msgstr "创建资产" msgstr "创建资产"
...@@ -2033,23 +2047,23 @@ msgstr "管理用户列表" ...@@ -2033,23 +2047,23 @@ msgstr "管理用户列表"
msgid "Admin user detail" msgid "Admin user detail"
msgstr "管理用户详情" msgstr "管理用户详情"
#: assets/views/asset.py:81 templates/_nav_user.html:4 #: assets/views/asset.py:80 templates/_nav_user.html:4
msgid "My assets" msgid "My assets"
msgstr "我的资产" msgstr "我的资产"
#: assets/views/asset.py:121 #: assets/views/asset.py:140
msgid "Bulk update asset success" msgid "Bulk update asset success"
msgstr "批量更新资产成功" msgstr "批量更新资产成功"
#: assets/views/asset.py:148 #: assets/views/asset.py:167
msgid "Bulk update asset" msgid "Bulk update asset"
msgstr "批量更新资产" msgstr "批量更新资产"
#: assets/views/asset.py:165 #: assets/views/asset.py:184
msgid "Update asset" msgid "Update asset"
msgstr "更新资产" msgstr "更新资产"
#: assets/views/asset.py:306 #: assets/views/asset.py:325
msgid "already exists" msgid "already exists"
msgstr "已经存在" msgstr "已经存在"
...@@ -2597,18 +2611,35 @@ msgstr "" ...@@ -2597,18 +2611,35 @@ msgstr ""
msgid "Encrypt field using Secret Key" msgid "Encrypt field using Secret Key"
msgstr "" msgstr ""
#: common/mixins.py:35 #: common/mixins.py:36
msgid "is discard" msgid "is discard"
msgstr "" msgstr ""
#: common/mixins.py:36 #: common/mixins.py:37
msgid "discard time" msgid "discard time"
msgstr "" msgstr ""
#: common/validators.py:7 #: common/mixins.py:210
#, fuzzy, python-format
msgid "%(name)s was %(action)s successfully"
msgstr "%(name)s %(action)s成功"
#: common/mixins.py:211
msgid "create"
msgstr "创建"
#: common/mixins.py:211
msgid "update"
msgstr "更新"
#: common/validators.py:11
msgid "Special char not allowed" msgid "Special char not allowed"
msgstr "不能包含特殊字符" msgstr "不能包含特殊字符"
#: common/validators.py:23
msgid "This field must be unique."
msgstr ""
#: jumpserver/views.py:185 #: jumpserver/views.py:185
msgid "" msgid ""
"<div>Luna is a separately deployed program, you need to deploy Luna, coco, " "<div>Luna is a separately deployed program, you need to deploy Luna, coco, "
...@@ -2960,7 +2991,7 @@ msgstr "命令执行列表" ...@@ -2960,7 +2991,7 @@ msgstr "命令执行列表"
msgid "Command execution" msgid "Command execution"
msgstr "命令执行" msgstr "命令执行"
#: orgs/mixins.py:81 orgs/models.py:24 #: orgs/mixins.py:83 orgs/models.py:24
msgid "Organization" msgid "Organization"
msgstr "组织管理" msgstr "组织管理"
...@@ -3161,12 +3192,12 @@ msgstr "添加用户组" ...@@ -3161,12 +3192,12 @@ msgstr "添加用户组"
#: perms/views/asset_permission.py:33 perms/views/asset_permission.py:65 #: perms/views/asset_permission.py:33 perms/views/asset_permission.py:65
#: perms/views/asset_permission.py:80 perms/views/asset_permission.py:95 #: perms/views/asset_permission.py:80 perms/views/asset_permission.py:95
#: perms/views/asset_permission.py:130 perms/views/asset_permission.py:162 #: perms/views/asset_permission.py:130 perms/views/asset_permission.py:162
#: perms/views/remote_app_permission.py:33 #: perms/views/remote_app_permission.py:32
#: perms/views/remote_app_permission.py:48 #: perms/views/remote_app_permission.py:47
#: perms/views/remote_app_permission.py:63 #: perms/views/remote_app_permission.py:62
#: perms/views/remote_app_permission.py:76 #: perms/views/remote_app_permission.py:75
#: perms/views/remote_app_permission.py:102 #: perms/views/remote_app_permission.py:101
#: perms/views/remote_app_permission.py:138 templates/_nav.html:39 #: perms/views/remote_app_permission.py:137 templates/_nav.html:39
#: xpack/plugins/orgs/templates/orgs/org_list.html:21 #: xpack/plugins/orgs/templates/orgs/org_list.html:21
msgid "Perms" msgid "Perms"
msgstr "权限管理" msgstr "权限管理"
...@@ -3195,27 +3226,27 @@ msgstr "资产授权用户列表" ...@@ -3195,27 +3226,27 @@ msgstr "资产授权用户列表"
msgid "Asset permission asset list" msgid "Asset permission asset list"
msgstr "资产授权资产列表" msgstr "资产授权资产列表"
#: perms/views/remote_app_permission.py:34 #: perms/views/remote_app_permission.py:33
msgid "RemoteApp permission list" msgid "RemoteApp permission list"
msgstr "远程应用授权列表" msgstr "远程应用授权列表"
#: perms/views/remote_app_permission.py:49 #: perms/views/remote_app_permission.py:48
msgid "Create RemoteApp permission" msgid "Create RemoteApp permission"
msgstr "创建远程应用授权规则" msgstr "创建远程应用授权规则"
#: perms/views/remote_app_permission.py:64 #: perms/views/remote_app_permission.py:63
msgid "Update RemoteApp permission" msgid "Update RemoteApp permission"
msgstr "更新远程应用授权规则" msgstr "更新远程应用授权规则"
#: perms/views/remote_app_permission.py:77 #: perms/views/remote_app_permission.py:76
msgid "RemoteApp permission detail" msgid "RemoteApp permission detail"
msgstr "远程应用授权详情" msgstr "远程应用授权详情"
#: perms/views/remote_app_permission.py:103 #: perms/views/remote_app_permission.py:102
msgid "RemoteApp permission user list" msgid "RemoteApp permission user list"
msgstr "远程应用授权用户列表" msgstr "远程应用授权用户列表"
#: perms/views/remote_app_permission.py:139 #: perms/views/remote_app_permission.py:138
msgid "RemoteApp permission RemoteApp list" msgid "RemoteApp permission RemoteApp list"
msgstr "远程应用授权远程应用列表" msgstr "远程应用授权远程应用列表"
...@@ -3861,7 +3892,7 @@ msgid "File manager" ...@@ -3861,7 +3892,7 @@ msgid "File manager"
msgstr "文件管理" msgstr "文件管理"
#: templates/_nav.html:68 terminal/views/command.py:50 #: templates/_nav.html:68 terminal/views/command.py:50
#: terminal/views/session.py:75 terminal/views/session.py:93 #: terminal/views/session.py:74 terminal/views/session.py:92
#: terminal/views/session.py:115 terminal/views/terminal.py:31 #: terminal/views/session.py:115 terminal/views/terminal.py:31
#: terminal/views/terminal.py:46 terminal/views/terminal.py:58 #: terminal/views/terminal.py:46 terminal/views/terminal.py:58
msgid "Terminal" msgid "Terminal"
...@@ -4241,11 +4272,11 @@ msgstr "接受终端注册" ...@@ -4241,11 +4272,11 @@ msgstr "接受终端注册"
msgid "Info" msgid "Info"
msgstr "信息" msgstr "信息"
#: terminal/views/session.py:76 #: terminal/views/session.py:75
msgid "Session online list" msgid "Session online list"
msgstr "在线会话" msgstr "在线会话"
#: terminal/views/session.py:94 #: terminal/views/session.py:93
msgid "Session offline list" msgid "Session offline list"
msgstr "离线会话" msgstr "离线会话"
......
...@@ -9,8 +9,10 @@ from django.forms import ModelForm ...@@ -9,8 +9,10 @@ from django.forms import ModelForm
from django.http.response import HttpResponseForbidden from django.http.response import HttpResponseForbidden
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from rest_framework import serializers from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from common.utils import get_logger from common.utils import get_logger
from common.validators import ProjectUniqueValidator
from .utils import ( from .utils import (
current_org, set_current_org, set_to_root_org, get_current_org_id current_org, set_current_org, set_to_root_org, get_current_org_id
) )
...@@ -214,3 +216,14 @@ class OrgResourceSerializerMixin(serializers.Serializer): ...@@ -214,3 +216,14 @@ class OrgResourceSerializerMixin(serializers.Serializer):
(同时为serializer.is_valid()对Model的unique_together校验做准备) (同时为serializer.is_valid()对Model的unique_together校验做准备)
""" """
org_id = serializers.HiddenField(default=get_current_org_id) org_id = serializers.HiddenField(default=get_current_org_id)
def get_validators(self):
_validators = super().get_validators()
validators = []
for v in _validators:
if isinstance(v, UniqueTogetherValidator) \
and "org_id" in v.fields:
v = ProjectUniqueValidator(v.queryset, v.fields)
validators.append(v)
return validators
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
from common.permissions import AdminUserRequiredMixin from common.permissions import AdminUserRequiredMixin
from users.models import User, UserGroup from users.models import User, UserGroup
from assets.models import Asset, SystemUser, Node, RemoteApp from assets.models import Asset, SystemUser, Node
from assets.serializers import ( from assets.serializers import (
AssetGrantedSerializer, NodeSerializer AssetGrantedSerializer, NodeSerializer
) )
from applications.serializers import RemoteAppSerializer from applications.serializers import RemoteAppSerializer
from applications.models import RemoteApp
...@@ -11,9 +11,8 @@ from django.conf import settings ...@@ -11,9 +11,8 @@ from django.conf import settings
from common.permissions import AdminUserRequiredMixin from common.permissions import AdminUserRequiredMixin
from orgs.utils import current_org from orgs.utils import current_org
from users.models import UserGroup
from assets.models import RemoteApp
from ..hands import RemoteApp, UserGroup
from ..models import RemoteAppPermission from ..models import RemoteAppPermission
from ..forms import RemoteAppPermissionCreateUpdateForm from ..forms import RemoteAppPermissionCreateUpdateForm
......
...@@ -453,4 +453,16 @@ div.dataTables_wrapper div.dataTables_filter { ...@@ -453,4 +453,16 @@ div.dataTables_wrapper div.dataTables_filter {
#tree-refresh .fa-refresh { #tree-refresh .fa-refresh {
font: normal normal normal 14px/1 FontAwesome !important; font: normal normal normal 14px/1 FontAwesome !important;
} }
\ No newline at end of file
.select2-selection__rendered span.select2-selection, .select2-container .select2-selection--single, .select2-selection__arrow {
height: 34px !important;
}
.select2-selection {
border-radius: 0 !important;
}
span.select2-selection__placeholder {
line-height: 34px !important;
}
...@@ -165,11 +165,13 @@ function formSubmit(props) { ...@@ -165,11 +165,13 @@ function formSubmit(props) {
/* /*
{ {
"form": $("form"), "form": $("form"),
"data": {},
"url": "", "url": "",
"method": "POST", "method": "POST",
"redirect_to": "", "redirect_to": "",
"success": function(data, textStatue, jqXHR){}, "success": function(data, textStatue, jqXHR){},
"error": function(jqXHR, textStatus, errorThrown) {} "error": function(jqXHR, textStatus, errorThrown) {},
"message": "",
} }
*/ */
props = props || {}; props = props || {};
...@@ -183,6 +185,10 @@ function formSubmit(props) { ...@@ -183,6 +185,10 @@ function formSubmit(props) {
dataType: props.data_type || "json" dataType: props.data_type || "json"
}).done(function (data, textState, jqXHR) { }).done(function (data, textState, jqXHR) {
if (redirect_to) { if (redirect_to) {
if (props.message) {
var messages="ed65330a45559c87345a0eb6ac7812d18d0d8976$[[\"__json_message\"\0540\05425\054\"asdfasdf \\u521b\\u5efa\\u6210\\u529f\"]]"
setCookie("messages", messages)
}
location.href = redirect_to; location.href = redirect_to;
} else if (typeof props.success === 'function') { } else if (typeof props.success === 'function') {
return props.success(data, textState, jqXHR); return props.success(data, textState, jqXHR);
...@@ -230,7 +236,15 @@ function formSubmit(props) { ...@@ -230,7 +236,15 @@ function formSubmit(props) {
var help_msg = v.join("<br/>") ; var help_msg = v.join("<br/>") ;
helpBlockRef.html(help_msg); helpBlockRef.html(help_msg);
} else { } else {
noneFieldErrorMsg += v + '<br/>'; $.each(v, function (kk, vv) {
if (typeof errors === "object") {
$.each(vv, function (kkk, vvv) {
noneFieldErrorMsg += " " + vvv + '<br/>';
})
} else{
noneFieldErrorMsg += vv + '<br/>';
}
})
} }
}); });
if (noneFieldErrorRef.length === 1 && noneFieldErrorMsg !== '') { if (noneFieldErrorRef.length === 1 && noneFieldErrorMsg !== '') {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment