mirror of https://github.com/jumpserver/jumpserver
Merge with dev
commit
f37b331630
31
README.md
31
README.md
|
@ -26,36 +26,7 @@ Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互
|
||||||
|
|
||||||
### Install 安装
|
### Install 安装
|
||||||
|
|
||||||
1. 安装 Python3
|
[详细安装](https://github.com/jumpserver/jumpserver/wiki/v0.5.0-%E5%9F%BA%E4%BA%8E-CentOS7)
|
||||||
略
|
|
||||||
|
|
||||||
2. 安装依赖
|
|
||||||
|
|
||||||
```
|
|
||||||
$ cd requirements && yum -y install $(cat rpm_requirements.txt) && pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
3. 修改配置文件
|
|
||||||
|
|
||||||
```
|
|
||||||
$ cp config_example.py config.py
|
|
||||||
```
|
|
||||||
|
|
||||||
4. 修改表结构
|
|
||||||
|
|
||||||
```
|
|
||||||
$ cd apps && python manage.py makemigrations && python manage.py migrate
|
|
||||||
```
|
|
||||||
|
|
||||||
5. 运行
|
|
||||||
|
|
||||||
```
|
|
||||||
$ python run_server.py
|
|
||||||
```
|
|
||||||
|
|
||||||
6. 其它
|
|
||||||
|
|
||||||
整合luna,coco需要nginx来配合, 详见详细安装文档
|
|
||||||
|
|
||||||
|
|
||||||
### Usage 使用
|
### Usage 使用
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
# Copyright (C) 2014-2017 Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved.
|
# Copyright (C) 2014-2018 Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the GNU General Public License v2.0 (the "License");
|
# Licensed under the GNU General Public License v2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
@ -87,7 +87,7 @@ class AssetGroupViewSet(CustomFilterMixin, BulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
Asset group api set, for add,delete,update,list,retrieve resource
|
Asset group api set, for add,delete,update,list,retrieve resource
|
||||||
"""
|
"""
|
||||||
queryset = AssetGroup.objects.all()
|
queryset = AssetGroup.objects.all().annotate(asset_count=Count("assets"))
|
||||||
serializer_class = serializers.AssetGroupSerializer
|
serializer_class = serializers.AssetGroupSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
@ -298,9 +298,7 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class LabelViewSet(BulkModelViewSet):
|
class LabelViewSet(BulkModelViewSet):
|
||||||
queryset = Label.objects.annotate(asset_count=Count("assets"))\
|
queryset = Label.objects.annotate(asset_count=Count("assets"))
|
||||||
.annotate(admin_user_count=Count("adminuser")) \
|
|
||||||
.annotate(system_user_count=Count("systemuser"))
|
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsSuperUser,)
|
||||||
serializer_class = serializers.LabelSerializer
|
serializer_class = serializers.LabelSerializer
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
Other module of this app shouldn't connect with other app.
|
Other module of this app shouldn't connect with other app.
|
||||||
|
|
||||||
:copyright: (c) 2014-2017 by Jumpserver Team.
|
:copyright: (c) 2014-2018 by Jumpserver Team.
|
||||||
:license: GPL v2, see LICENSE for more details.
|
:license: GPL v2, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ class Label(models.Model):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
name = models.CharField(max_length=128, verbose_name=_("Name"))
|
name = models.CharField(max_length=128, verbose_name=_("Name"))
|
||||||
value = models.CharField(max_length=128, verbose_name=_("Value"))
|
value = models.CharField(max_length=128, verbose_name=_("Value"))
|
||||||
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES, verbose_name=_("Category"))
|
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES, default=USER_CATEGORY, verbose_name=_("Category"))
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
||||||
comment = models.TextField(blank=True, null=True, verbose_name=_("Comment"))
|
comment = models.TextField(blank=True, null=True, verbose_name=_("Comment"))
|
||||||
date_created = models.DateTimeField(
|
date_created = models.DateTimeField(
|
||||||
|
|
|
@ -30,7 +30,6 @@ class AssetUser(models.Model):
|
||||||
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
||||||
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
|
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
|
||||||
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
||||||
labels = models.ManyToManyField('assets.Label', blank=True, verbose_name=_("Labels"))
|
|
||||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
||||||
date_created = models.DateTimeField(auto_now_add=True)
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
date_updated = models.DateTimeField(auto_now=True)
|
date_updated = models.DateTimeField(auto_now=True)
|
||||||
|
|
|
@ -22,7 +22,7 @@ class AssetGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_assets_amount(obj):
|
def get_assets_amount(obj):
|
||||||
return obj.assets.count()
|
return obj.asset_count
|
||||||
|
|
||||||
|
|
||||||
class AssetUpdateSystemUserSerializer(serializers.ModelSerializer):
|
class AssetUpdateSystemUserSerializer(serializers.ModelSerializer):
|
||||||
|
@ -288,8 +288,6 @@ class MyAssetGroupGrantedSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class LabelSerializer(serializers.ModelSerializer):
|
class LabelSerializer(serializers.ModelSerializer):
|
||||||
asset_count = serializers.SerializerMethodField()
|
asset_count = serializers.SerializerMethodField()
|
||||||
admin_user_count = serializers.SerializerMethodField()
|
|
||||||
system_user_count = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Label
|
model = Label
|
||||||
|
@ -300,14 +298,6 @@ class LabelSerializer(serializers.ModelSerializer):
|
||||||
def get_asset_count(obj):
|
def get_asset_count(obj):
|
||||||
return obj.asset_count
|
return obj.asset_count
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_admin_user_count(obj):
|
|
||||||
return obj.admin_user_count
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_system_user_count(obj):
|
|
||||||
return obj.system_user_count
|
|
||||||
|
|
||||||
def get_field_names(self, declared_fields, info):
|
def get_field_names(self, declared_fields, info):
|
||||||
fields = super().get_field_names(declared_fields, info)
|
fields = super().get_field_names(declared_fields, info)
|
||||||
fields.extend(['get_category_display'])
|
fields.extend(['get_category_display'])
|
||||||
|
|
|
@ -121,7 +121,7 @@ function initTable() {
|
||||||
{data: "type" }, {data: "is_connective" }],
|
{data: "type" }, {data: "is_connective" }],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
|
@ -184,7 +184,7 @@ function initTable() {
|
||||||
{data: "get_type_display" }, {data: "is_connective" }, {data: "id"}],
|
{data: "get_type_display" }, {data: "is_connective" }, {data: "id"}],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,6 @@ $(document).ready(function(){
|
||||||
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
|
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
|
||||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
||||||
}},
|
}},
|
||||||
{targets: 3, createdCell: function (td, cellData) {
|
|
||||||
var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData;
|
|
||||||
$(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');
|
|
||||||
}},
|
|
||||||
{targets: 4, createdCell: function (td, cellData, rowData) {
|
{targets: 4, createdCell: function (td, cellData, rowData) {
|
||||||
var update_btn = '<a href="{% url "assets:asset-group-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
var update_btn = '<a href="{% url "assets:asset-group-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_group_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_group_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
|
|
|
@ -176,7 +176,7 @@ function initTable() {
|
||||||
{data: "get_type_display" }, {data: "is_connective" }, {data: "id"}],
|
{data: "get_type_display" }, {data: "is_connective" }, {data: "id"}],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
<th class="text-center">{% trans 'Name' %}</th>
|
<th class="text-center">{% trans 'Name' %}</th>
|
||||||
<th class="text-center">{% trans 'Value' %}</th>
|
<th class="text-center">{% trans 'Value' %}</th>
|
||||||
<th class="text-center">{% trans 'Asset' %}</th>
|
<th class="text-center">{% trans 'Asset' %}</th>
|
||||||
<th class="text-center">{% trans 'Admin user' %}</th>
|
|
||||||
<th class="text-center">{% trans 'System user' %}</th>
|
|
||||||
<th class="text-center">{% trans 'Action' %}</th>
|
<th class="text-center">{% trans 'Action' %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -36,7 +34,7 @@ function initTable() {
|
||||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{targets: 6, createdCell: function (td, cellData, rowData) {
|
{targets: 4, createdCell: function (td, cellData, rowData) {
|
||||||
var update_btn = '<a href="{% url "assets:cluster-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
var update_btn = '<a href="{% url "assets:cluster-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_cluster_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_cluster_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
$(td).html(update_btn + del_btn)
|
$(td).html(update_btn + del_btn)
|
||||||
|
@ -44,8 +42,7 @@ function initTable() {
|
||||||
ajax_url: '{% url "api-assets:label-list" %}?sort=name',
|
ajax_url: '{% url "api-assets:label-list" %}?sort=name',
|
||||||
columns: [
|
columns: [
|
||||||
{data: "id"}, {data: "name" }, {data: "value" },
|
{data: "id"}, {data: "name" }, {data: "value" },
|
||||||
{data: "asset_count" }, {data: "admin_user_count" },
|
{data: "asset_count" }, {data: "id"}
|
||||||
{data: "system_user_count" }, {data: "id"}
|
|
||||||
],
|
],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
|
|
|
@ -121,7 +121,7 @@ function initAssetsTable() {
|
||||||
columns: [{data: "hostname" }, {data: "ip" }, {data: "port" }, {data: "hostname" }],
|
columns: [{data: "hostname" }, {data: "ip" }, {data: "port" }, {data: "hostname" }],
|
||||||
op_html: $('#actions').html()
|
op_html: $('#actions').html()
|
||||||
};
|
};
|
||||||
jumpserver.initDataTable(options);
|
jumpserver.initServerSideDataTable(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
|
@ -60,11 +60,6 @@ class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView)
|
||||||
success_url = reverse_lazy('assets:cluster-list')
|
success_url = reverse_lazy('assets:cluster-list')
|
||||||
success_message = update_success_msg
|
success_message = update_success_msg
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
cluster = form.save(commit=False)
|
|
||||||
cluster.save()
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
'app': _('assets'),
|
'app': _('assets'),
|
||||||
|
|
|
@ -63,8 +63,6 @@ class LDAPTestingAPI(APIView):
|
||||||
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
|
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
|
||||||
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
|
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
|
||||||
|
|
||||||
print(serializer.validated_data)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
attr_map = json.loads(attr_map)
|
attr_map = json.loads(attr_map)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
|
@ -77,9 +75,6 @@ class LDAPTestingAPI(APIView):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Response({"error": str(e)}, status=401)
|
return Response({"error": str(e)}, status=401)
|
||||||
|
|
||||||
print(search_ou)
|
|
||||||
print(search_filter % ({"user": "*"}))
|
|
||||||
print(attr_map.values())
|
|
||||||
ok = conn.search(search_ou, search_filter % ({"user": "*"}),
|
ok = conn.search(search_ou, search_filter % ({"user": "*"}),
|
||||||
attributes=list(attr_map.values()))
|
attributes=list(attr_map.values()))
|
||||||
if not ok:
|
if not ok:
|
||||||
|
|
|
@ -5,6 +5,7 @@ import json
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
|
||||||
class DictField(forms.Field):
|
class DictField(forms.Field):
|
||||||
|
@ -18,16 +19,16 @@ class DictField(forms.Field):
|
||||||
# we don't need to handle that explicitly.
|
# we don't need to handle that explicitly.
|
||||||
if isinstance(value, six.string_types):
|
if isinstance(value, six.string_types):
|
||||||
try:
|
try:
|
||||||
print(value)
|
|
||||||
value = json.loads(value)
|
value = json.loads(value)
|
||||||
return value
|
return value
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
pass
|
return ValidationError(_("Not a valid json"))
|
||||||
value = {}
|
else:
|
||||||
return value
|
return ValidationError(_("Not a string type"))
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
print(value)
|
if isinstance(value, ValidationError):
|
||||||
|
raise value
|
||||||
if not value and self.required:
|
if not value and self.required:
|
||||||
raise ValidationError(self.error_messages['required'], code='required')
|
raise ValidationError(self.error_messages['required'], code='required')
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,9 @@ import json
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.utils.html import escape
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from .models import Setting
|
from .models import Setting
|
||||||
from .fields import DictField
|
from .fields import DictField
|
||||||
|
@ -24,34 +26,38 @@ def to_form_value(value):
|
||||||
data = value
|
data = value
|
||||||
return data
|
return data
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
return ''
|
return ""
|
||||||
|
|
||||||
|
|
||||||
class BaseForm(forms.Form):
|
class BaseForm(forms.Form):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
settings = Setting.objects.all()
|
db_settings = Setting.objects.all()
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
db_value = getattr(settings, name).value
|
db_value = getattr(db_settings, name).value
|
||||||
if db_value:
|
django_value = getattr(settings, name) if hasattr(settings, name) else None
|
||||||
|
if db_value is False or db_value:
|
||||||
field.initial = to_form_value(db_value)
|
field.initial = to_form_value(db_value)
|
||||||
|
elif django_value is False or django_value:
|
||||||
|
field.initial = to_form_value(to_model_value(django_value))
|
||||||
|
|
||||||
def save(self):
|
def save(self, category="default"):
|
||||||
if not self.is_bound:
|
if not self.is_bound:
|
||||||
raise ValueError("Form is not bound")
|
raise ValueError("Form is not bound")
|
||||||
|
|
||||||
settings = Setting.objects.all()
|
db_settings = Setting.objects.all()
|
||||||
if self.is_valid():
|
if self.is_valid():
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
for name, value in self.cleaned_data.items():
|
for name, value in self.cleaned_data.items():
|
||||||
field = self.fields[name]
|
field = self.fields[name]
|
||||||
if isinstance(field.widget, forms.PasswordInput) and not value:
|
if isinstance(field.widget, forms.PasswordInput) and not value:
|
||||||
continue
|
continue
|
||||||
if value == to_form_value(getattr(settings, name).value):
|
if value == to_form_value(getattr(db_settings, name).value):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
defaults = {
|
defaults = {
|
||||||
'name': name,
|
'name': name,
|
||||||
|
'category': category,
|
||||||
'value': to_model_value(value)
|
'value': to_model_value(value)
|
||||||
}
|
}
|
||||||
Setting.objects.update_or_create(defaults=defaults, name=name)
|
Setting.objects.update_or_create(defaults=defaults, name=name)
|
||||||
|
@ -72,9 +78,6 @@ class BasicSettingForm(BaseForm):
|
||||||
max_length=1024, label=_("Email Subject Prefix"),
|
max_length=1024, label=_("Email Subject Prefix"),
|
||||||
initial="[Jumpserver] "
|
initial="[Jumpserver] "
|
||||||
)
|
)
|
||||||
AUTH_LDAP = forms.BooleanField(
|
|
||||||
label=_("Enable LDAP Auth"), initial=False, required=False
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class EmailSettingForm(BaseForm):
|
class EmailSettingForm(BaseForm):
|
||||||
|
@ -129,3 +132,28 @@ class LDAPSettingForm(BaseForm):
|
||||||
AUTH_LDAP_START_TLS = forms.BooleanField(
|
AUTH_LDAP_START_TLS = forms.BooleanField(
|
||||||
label=_("Use SSL"), initial=False, required=False
|
label=_("Use SSL"), initial=False, required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TerminalSettingForm(BaseForm):
|
||||||
|
SORT_BY_CHOICES = (
|
||||||
|
('hostname', _('Hostname')),
|
||||||
|
('ip', _('IP')),
|
||||||
|
)
|
||||||
|
TERMINAL_ASSET_LIST_SORT_BY = forms.ChoiceField(
|
||||||
|
choices=SORT_BY_CHOICES, initial='hostname', label=_("List sort by")
|
||||||
|
)
|
||||||
|
TERMINAL_HEARTBEAT_INTERVAL = forms.IntegerField(
|
||||||
|
initial=5, label=_("Heartbeat interval"), help_text=_("Units: seconds")
|
||||||
|
)
|
||||||
|
TERMINAL_PASSWORD_AUTH = forms.BooleanField(
|
||||||
|
initial=True, required=False, label=_("Password auth")
|
||||||
|
)
|
||||||
|
TERMINAL_PUBLIC_KEY_AUTH = forms.BooleanField(
|
||||||
|
initial=True, required=False, label=_("Public key auth")
|
||||||
|
)
|
||||||
|
TERMINAL_COMMAND_STORAGE = DictField(
|
||||||
|
label=_("Command storage"), help_text=_(
|
||||||
|
"Set terminal storage setting, `default` is the using as default,"
|
||||||
|
"You can set other storage and some terminal using"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import json
|
||||||
|
|
||||||
import ldap
|
import ldap
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.utils import ProgrammingError, OperationalError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django_auth_ldap.config import LDAPSearch
|
from django_auth_ldap.config import LDAPSearch
|
||||||
|
@ -24,6 +25,7 @@ class SettingManager(models.Manager):
|
||||||
class Setting(models.Model):
|
class Setting(models.Model):
|
||||||
name = models.CharField(max_length=128, unique=True, verbose_name=_("Name"))
|
name = models.CharField(max_length=128, unique=True, verbose_name=_("Name"))
|
||||||
value = models.TextField(verbose_name=_("Value"))
|
value = models.TextField(verbose_name=_("Value"))
|
||||||
|
category = models.CharField(max_length=128, default="default")
|
||||||
enabled = models.BooleanField(verbose_name=_("Enabled"), default=True)
|
enabled = models.BooleanField(verbose_name=_("Enabled"), default=True)
|
||||||
comment = models.TextField(verbose_name=_("Comment"))
|
comment = models.TextField(verbose_name=_("Comment"))
|
||||||
|
|
||||||
|
@ -33,17 +35,28 @@ class Setting(models.Model):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value_(self):
|
def cleaned_value(self):
|
||||||
try:
|
try:
|
||||||
return json.loads(self.value)
|
return json.loads(self.value)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@cleaned_value.setter
|
||||||
|
def cleaned_value(self, item):
|
||||||
|
try:
|
||||||
|
v = json.dumps(item)
|
||||||
|
self.value = v
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise ValueError("Json dump error: {}".format(str(e)))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def refresh_all_settings(cls):
|
def refresh_all_settings(cls):
|
||||||
settings_list = cls.objects.all()
|
try:
|
||||||
for setting in settings_list:
|
settings_list = cls.objects.all()
|
||||||
setting.refresh_setting()
|
for setting in settings_list:
|
||||||
|
setting.refresh_setting()
|
||||||
|
except (ProgrammingError, OperationalError):
|
||||||
|
pass
|
||||||
|
|
||||||
def refresh_setting(self):
|
def refresh_setting(self):
|
||||||
try:
|
try:
|
||||||
|
@ -53,9 +66,9 @@ class Setting(models.Model):
|
||||||
setattr(settings, self.name, value)
|
setattr(settings, self.name, value)
|
||||||
|
|
||||||
if self.name == "AUTH_LDAP":
|
if self.name == "AUTH_LDAP":
|
||||||
if self.value_ and settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS:
|
if self.cleaned_value and settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS:
|
||||||
settings.AUTHENTICATION_BACKENDS.insert(0, settings.AUTH_LDAP_BACKEND)
|
settings.AUTHENTICATION_BACKENDS.insert(0, settings.AUTH_LDAP_BACKEND)
|
||||||
elif not self.value_ and settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
|
elif not self.cleaned_value and settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
|
||||||
settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND)
|
settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND)
|
||||||
|
|
||||||
if self.name == "AUTH_LDAP_SEARCH_FILTER":
|
if self.name == "AUTH_LDAP_SEARCH_FILTER":
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
<li class="active">
|
<li class="active">
|
||||||
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load common_tags %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
||||||
|
</li>
|
||||||
|
<li class="active">
|
||||||
|
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="col-sm-12" style="padding-left:0">
|
||||||
|
<div class="ibox-content" style="border-width: 0;padding-top: 40px;">
|
||||||
|
<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 setting" %}</h3>
|
||||||
|
{% for field in form %}
|
||||||
|
{% if not field.field|is_bool_field %}
|
||||||
|
{% bootstrap_field field layout="horizontal" %}
|
||||||
|
{% else %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="col-sm-1">
|
||||||
|
{{ field }}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<span class="help-block" >{{ field.help_text }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<h3>{% trans "Command storage" %}</h3>
|
||||||
|
<table class="table table-hover " id="task-history-list-table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans 'Name' %}</th>
|
||||||
|
<th>{% trans 'Type' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for name, setting in command_storage.items %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ name }}</td>
|
||||||
|
<td>{{ setting.TYPE }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{# <button class="btn btn-default btn-circle btn-add-command-storage" data-toggle="modal" data-target="#add_command_storage_model" tabindex="0" type="button"><i class="fa fa-plus"></i></button>#}
|
||||||
|
{# <div class="hr-line-dashed"></div>#}
|
||||||
|
{# <h3>{% trans "Replay storage" %}</h3>#}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
|
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
|
||||||
|
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
})
|
||||||
|
.on("click", ".btn-test", function () {
|
||||||
|
var data = {};
|
||||||
|
var form = $("form").serializeArray();
|
||||||
|
$.each(form, function (i, field) {
|
||||||
|
data[field.name] = field.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
var the_url = "{% url 'api-common:ldap-testing' %}";
|
||||||
|
|
||||||
|
function error(message) {
|
||||||
|
toastr.error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
function success(message) {
|
||||||
|
toastr.success(message.msg)
|
||||||
|
}
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
method: "POST",
|
||||||
|
flash_message: false,
|
||||||
|
success: success,
|
||||||
|
error: error
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.on('click', '', function () {
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -10,4 +10,5 @@ urlpatterns = [
|
||||||
url(r'^$', views.BasicSettingView.as_view(), name='basic-setting'),
|
url(r'^$', views.BasicSettingView.as_view(), name='basic-setting'),
|
||||||
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
|
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
|
||||||
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
|
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
|
||||||
|
url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
from django.views.generic import View, TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm
|
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
|
||||||
|
TerminalSettingForm
|
||||||
|
from .models import Setting
|
||||||
from .mixins import AdminUserRequiredMixin
|
from .mixins import AdminUserRequiredMixin
|
||||||
from .signals import ldap_auth_enable
|
from .signals import ldap_auth_enable
|
||||||
|
|
||||||
|
@ -25,8 +28,6 @@ class BasicSettingView(AdminUserRequiredMixin, TemplateView):
|
||||||
form = self.form_class(request.POST)
|
form = self.form_class(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
if "AUTH_LDAP" in form.cleaned_data:
|
|
||||||
ldap_auth_enable.send(form.cleaned_data["AUTH_LDAP"])
|
|
||||||
msg = _("Update setting successfully, please restart program")
|
msg = _("Update setting successfully, please restart program")
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
return redirect('settings:basic-setting')
|
return redirect('settings:basic-setting')
|
||||||
|
@ -79,6 +80,8 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
|
||||||
form = self.form_class(request.POST)
|
form = self.form_class(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
|
if "AUTH_LDAP" in form.cleaned_data:
|
||||||
|
ldap_auth_enable.send(form.cleaned_data["AUTH_LDAP"])
|
||||||
msg = _("Update setting successfully, please restart program")
|
msg = _("Update setting successfully, please restart program")
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
return redirect('settings:ldap-setting')
|
return redirect('settings:ldap-setting')
|
||||||
|
@ -86,3 +89,31 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
|
||||||
context = self.get_context_data()
|
context = self.get_context_data()
|
||||||
context.update({"form": form})
|
context.update({"form": form})
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
|
|
||||||
|
class TerminalSettingView(AdminUserRequiredMixin, TemplateView):
|
||||||
|
form_class = TerminalSettingForm
|
||||||
|
template_name = "common/terminal_setting.html"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
command_storage = settings.TERMINAL_COMMAND_STORAGE
|
||||||
|
context = {
|
||||||
|
'app': _('Settings'),
|
||||||
|
'action': _('Terminal setting'),
|
||||||
|
'form': self.form_class(),
|
||||||
|
'command_storage': command_storage,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
form = self.form_class(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
msg = _("Update setting successfully, please restart program")
|
||||||
|
messages.success(request, msg)
|
||||||
|
return redirect('settings:terminal-setting')
|
||||||
|
else:
|
||||||
|
context = self.get_context_data()
|
||||||
|
context.update({"form": form})
|
||||||
|
return render(request, self.template_name, context)
|
||||||
|
|
|
@ -274,7 +274,7 @@ EMAIL_HOST_USER = CONFIG.EMAIL_HOST_USER
|
||||||
EMAIL_HOST_PASSWORD = CONFIG.EMAIL_HOST_PASSWORD
|
EMAIL_HOST_PASSWORD = CONFIG.EMAIL_HOST_PASSWORD
|
||||||
EMAIL_USE_SSL = CONFIG.EMAIL_USE_SSL
|
EMAIL_USE_SSL = CONFIG.EMAIL_USE_SSL
|
||||||
EMAIL_USE_TLS = CONFIG.EMAIL_USE_TLS
|
EMAIL_USE_TLS = CONFIG.EMAIL_USE_TLS
|
||||||
EMAIL_SUBJECT_PREFIX = CONFIG.EMAIL_SUBJECT_PREFIX
|
EMAIL_SUBJECT_PREFIX = CONFIG.EMAIL_SUBJECT_PREFIX or ''
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
# Use Django's standard `django.contrib.auth` permissions,
|
# Use Django's standard `django.contrib.auth` permissions,
|
||||||
|
@ -298,7 +298,7 @@ REST_FRAMEWORK = {
|
||||||
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S %z',
|
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S %z',
|
||||||
'DATETIME_INPUT_FORMATS': ['%Y-%m-%d %H:%M:%S %z'],
|
'DATETIME_INPUT_FORMATS': ['%Y-%m-%d %H:%M:%S %z'],
|
||||||
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
|
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
|
||||||
'PAGE_SIZE': 15
|
# 'PAGE_SIZE': 15
|
||||||
}
|
}
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = [
|
AUTHENTICATION_BACKENDS = [
|
||||||
|
@ -373,7 +373,20 @@ CAPTCHA_FOREGROUND_COLOR = '#001100'
|
||||||
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)
|
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)
|
||||||
CAPTCHA_TEST_MODE = CONFIG.CAPTCHA_TEST_MODE
|
CAPTCHA_TEST_MODE = CONFIG.CAPTCHA_TEST_MODE
|
||||||
|
|
||||||
COMMAND_STORAGE_BACKEND = 'terminal.backends.command.db'
|
COMMAND_STORAGE = {
|
||||||
|
'ENGINE': 'terminal.backends.command.db',
|
||||||
|
}
|
||||||
|
|
||||||
|
TERMINAL_COMMAND_STORAGE = {
|
||||||
|
"default": {
|
||||||
|
"TYPE": "server",
|
||||||
|
},
|
||||||
|
# 'ali-es': {
|
||||||
|
# 'TYPE': 'elasticsearch',
|
||||||
|
# 'HOSTS': ['http://elastic:changeme@localhost:9200'],
|
||||||
|
# },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
|
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
|
||||||
BOOTSTRAP3 = {
|
BOOTSTRAP3 = {
|
||||||
|
|
Binary file not shown.
|
@ -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: 2018-01-12 18:51+0800\n"
|
"POT-Creation-Date: 2018-01-23 11:56+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"
|
||||||
|
@ -18,7 +18,7 @@ msgstr ""
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
#: assets/forms.py:23 assets/forms.py:53 assets/forms.py:99 perms/forms.py:37
|
#: assets/forms.py:23 assets/forms.py:53 assets/forms.py:99 perms/forms.py:37
|
||||||
#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:246
|
#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:245
|
||||||
msgid "Select asset groups"
|
msgid "Select asset groups"
|
||||||
msgstr "选择资产组"
|
msgstr "选择资产组"
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ msgstr "默认使用管理用户"
|
||||||
|
|
||||||
#: assets/forms.py:76 assets/forms.py:81 assets/forms.py:127
|
#: assets/forms.py:76 assets/forms.py:81 assets/forms.py:127
|
||||||
#: assets/templates/assets/asset_group_detail.html:75 perms/forms.py:34
|
#: assets/templates/assets/asset_group_detail.html:75 perms/forms.py:34
|
||||||
#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:243
|
#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:242
|
||||||
msgid "Select assets"
|
msgid "Select assets"
|
||||||
msgstr "选择资产"
|
msgstr "选择资产"
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ msgstr "端口"
|
||||||
#: assets/templates/assets/system_user_list.html:26 perms/models.py:17
|
#: assets/templates/assets/system_user_list.html:26 perms/models.py:17
|
||||||
#: perms/templates/perms/asset_permission_create_update.html:40
|
#: perms/templates/perms/asset_permission_create_update.html:40
|
||||||
#: perms/templates/perms/asset_permission_list.html:28 templates/_nav.html:22
|
#: perms/templates/perms/asset_permission_list.html:28 templates/_nav.html:22
|
||||||
#: terminal/backends/command/models.py:11 terminal/models.py:93
|
#: terminal/backends/command/models.py:11 terminal/models.py:116
|
||||||
#: terminal/templates/terminal/command_list.html:40
|
#: terminal/templates/terminal/command_list.html:40
|
||||||
#: terminal/templates/terminal/command_list.html:73
|
#: terminal/templates/terminal/command_list.html:73
|
||||||
#: terminal/templates/terminal/session_list.html:41
|
#: terminal/templates/terminal/session_list.html:41
|
||||||
|
@ -77,7 +77,7 @@ msgid "Asset"
|
||||||
msgstr "资产"
|
msgstr "资产"
|
||||||
|
|
||||||
#: assets/forms.py:161 perms/forms.py:40
|
#: assets/forms.py:161 perms/forms.py:40
|
||||||
#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:249
|
#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:248
|
||||||
msgid "Select system users"
|
msgid "Select system users"
|
||||||
msgstr "选择系统用户"
|
msgstr "选择系统用户"
|
||||||
|
|
||||||
|
@ -99,14 +99,15 @@ msgstr "选择的系统用户将会在该集群资产上创建"
|
||||||
#: assets/templates/assets/cluster_detail.html:57
|
#: assets/templates/assets/cluster_detail.html:57
|
||||||
#: assets/templates/assets/cluster_list.html:19
|
#: assets/templates/assets/cluster_list.html:19
|
||||||
#: assets/templates/assets/system_user_detail.html:58
|
#: assets/templates/assets/system_user_detail.html:58
|
||||||
#: assets/templates/assets/system_user_list.html:24 common/models.py:25
|
#: assets/templates/assets/system_user_list.html:24 common/models.py:26
|
||||||
#: ops/models.py:31 ops/templates/ops/task_detail.html:56
|
#: common/templates/common/terminal_setting.html:62 ops/models.py:31
|
||||||
#: ops/templates/ops/task_list.html:34 perms/models.py:14
|
#: ops/templates/ops/task_detail.html:56 ops/templates/ops/task_list.html:34
|
||||||
|
#: perms/models.py:14
|
||||||
#: perms/templates/perms/asset_permission_create_update.html:33
|
#: perms/templates/perms/asset_permission_create_update.html:33
|
||||||
#: perms/templates/perms/asset_permission_detail.html:62
|
#: perms/templates/perms/asset_permission_detail.html:62
|
||||||
#: perms/templates/perms/asset_permission_list.html:25
|
#: perms/templates/perms/asset_permission_list.html:25
|
||||||
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:14
|
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:15
|
||||||
#: terminal/models.py:118 terminal/templates/terminal/terminal_detail.html:43
|
#: terminal/models.py:141 terminal/templates/terminal/terminal_detail.html:43
|
||||||
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
|
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
|
||||||
#: users/models/user.py:35 users/templates/users/_select_user_modal.html:13
|
#: users/models/user.py:35 users/templates/users/_select_user_modal.html:13
|
||||||
#: users/templates/users/user_detail.html:62
|
#: users/templates/users/user_detail.html:62
|
||||||
|
@ -126,10 +127,10 @@ msgstr "集群级别管理用户"
|
||||||
|
|
||||||
#: assets/forms.py:200
|
#: assets/forms.py:200
|
||||||
msgid "Password or private key password"
|
msgid "Password or private key password"
|
||||||
msgstr "密码或秘钥不合法"
|
msgstr "密码或秘钥密码"
|
||||||
|
|
||||||
#: assets/forms.py:201 assets/forms.py:262 assets/models/user.py:30
|
#: assets/forms.py:201 assets/forms.py:262 assets/models/user.py:30
|
||||||
#: common/forms.py:110 users/forms.py:16 users/forms.py:24
|
#: common/forms.py:113 users/forms.py:16 users/forms.py:24
|
||||||
#: users/templates/users/login.html:56
|
#: users/templates/users/login.html:56
|
||||||
#: users/templates/users/reset_password.html:52
|
#: users/templates/users/reset_password.html:52
|
||||||
#: users/templates/users/user_create.html:11
|
#: users/templates/users/user_create.html:11
|
||||||
|
@ -239,7 +240,7 @@ msgstr "测试环境"
|
||||||
#: assets/templates/assets/asset_list.html:31
|
#: assets/templates/assets/asset_list.html:31
|
||||||
#: assets/templates/assets/cluster_assets.html:52
|
#: assets/templates/assets/cluster_assets.html:52
|
||||||
#: assets/templates/assets/system_user_asset.html:53
|
#: assets/templates/assets/system_user_asset.html:53
|
||||||
#: assets/templates/assets/user_asset_list.html:20
|
#: assets/templates/assets/user_asset_list.html:20 common/forms.py:140
|
||||||
#: perms/templates/perms/asset_permission_asset.html:55
|
#: perms/templates/perms/asset_permission_asset.html:55
|
||||||
#: users/templates/users/login_log_list.html:52
|
#: users/templates/users/login_log_list.html:52
|
||||||
#: users/templates/users/user_granted_asset.html:49
|
#: users/templates/users/user_granted_asset.html:49
|
||||||
|
@ -253,7 +254,7 @@ msgstr "IP"
|
||||||
#: assets/templates/assets/asset_list.html:30
|
#: assets/templates/assets/asset_list.html:30
|
||||||
#: assets/templates/assets/cluster_assets.html:51
|
#: assets/templates/assets/cluster_assets.html:51
|
||||||
#: assets/templates/assets/system_user_asset.html:52
|
#: assets/templates/assets/system_user_asset.html:52
|
||||||
#: assets/templates/assets/user_asset_list.html:19
|
#: assets/templates/assets/user_asset_list.html:19 common/forms.py:139
|
||||||
#: perms/templates/perms/asset_permission_asset.html:54
|
#: perms/templates/perms/asset_permission_asset.html:54
|
||||||
#: users/templates/users/user_granted_asset.html:48
|
#: users/templates/users/user_granted_asset.html:48
|
||||||
#: users/templates/users/user_group_granted_asset.html:49
|
#: users/templates/users/user_group_granted_asset.html:49
|
||||||
|
@ -400,9 +401,9 @@ msgstr "创建日期"
|
||||||
#: assets/templates/assets/asset_group_list.html:17
|
#: assets/templates/assets/asset_group_list.html:17
|
||||||
#: assets/templates/assets/cluster_detail.html:97
|
#: assets/templates/assets/cluster_detail.html:97
|
||||||
#: assets/templates/assets/system_user_detail.html:100
|
#: assets/templates/assets/system_user_detail.html:100
|
||||||
#: assets/templates/assets/system_user_list.html:30 common/models.py:28
|
#: assets/templates/assets/system_user_list.html:30 common/models.py:30
|
||||||
#: ops/models.py:37 perms/models.py:24
|
#: ops/models.py:37 perms/models.py:24
|
||||||
#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:22
|
#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:25
|
||||||
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
|
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
|
||||||
#: users/models/user.py:47 users/templates/users/user_detail.html:110
|
#: users/models/user.py:47 users/templates/users/user_detail.html:110
|
||||||
#: users/templates/users/user_group_detail.html:67
|
#: users/templates/users/user_group_detail.html:67
|
||||||
|
@ -494,7 +495,7 @@ msgstr "Shell"
|
||||||
#: assets/models/user.py:269 perms/models.py:19
|
#: assets/models/user.py:269 perms/models.py:19
|
||||||
#: perms/templates/perms/asset_permission_detail.html:136
|
#: perms/templates/perms/asset_permission_detail.html:136
|
||||||
#: perms/templates/perms/asset_permission_list.html:30 templates/_nav.html:26
|
#: perms/templates/perms/asset_permission_list.html:30 templates/_nav.html:26
|
||||||
#: terminal/backends/command/models.py:12 terminal/models.py:94
|
#: terminal/backends/command/models.py:12 terminal/models.py:117
|
||||||
#: terminal/templates/terminal/command_list.html:48
|
#: terminal/templates/terminal/command_list.html:48
|
||||||
#: terminal/templates/terminal/command_list.html:74
|
#: terminal/templates/terminal/command_list.html:74
|
||||||
#: terminal/templates/terminal/session_list.html:49
|
#: terminal/templates/terminal/session_list.html:49
|
||||||
|
@ -576,9 +577,9 @@ msgstr "仅修改你需要更新的字段"
|
||||||
#: assets/views/admin_user.py:106 assets/views/asset.py:48
|
#: assets/views/admin_user.py:106 assets/views/asset.py:48
|
||||||
#: assets/views/asset.py:61 assets/views/asset.py:84 assets/views/asset.py:144
|
#: assets/views/asset.py:61 assets/views/asset.py:84 assets/views/asset.py:144
|
||||||
#: assets/views/asset.py:161 assets/views/asset.py:185
|
#: assets/views/asset.py:161 assets/views/asset.py:185
|
||||||
#: assets/views/cluster.py:26 assets/views/cluster.py:85
|
#: assets/views/cluster.py:26 assets/views/cluster.py:80
|
||||||
#: assets/views/cluster.py:102 assets/views/group.py:34
|
#: assets/views/cluster.py:97 assets/views/group.py:34 assets/views/group.py:52
|
||||||
#: assets/views/group.py:52 assets/views/group.py:69 assets/views/group.py:87
|
#: assets/views/group.py:69 assets/views/group.py:87
|
||||||
#: assets/views/system_user.py:28 assets/views/system_user.py:44
|
#: assets/views/system_user.py:28 assets/views/system_user.py:44
|
||||||
#: assets/views/system_user.py:60 assets/views/system_user.py:75
|
#: assets/views/system_user.py:60 assets/views/system_user.py:75
|
||||||
#: templates/_nav.html:19
|
#: templates/_nav.html:19
|
||||||
|
@ -650,7 +651,7 @@ msgstr "自动生成秘钥"
|
||||||
#: assets/templates/assets/asset_update.html:47
|
#: assets/templates/assets/asset_update.html:47
|
||||||
#: assets/templates/assets/cluster_create_update.html:46
|
#: assets/templates/assets/cluster_create_update.html:46
|
||||||
#: perms/templates/perms/asset_permission_create_update.html:45
|
#: perms/templates/perms/asset_permission_create_update.html:45
|
||||||
#: terminal/templates/terminal/terminal_update.html:40
|
#: terminal/templates/terminal/terminal_update.html:41
|
||||||
msgid "Other"
|
msgid "Other"
|
||||||
msgstr "其它"
|
msgstr "其它"
|
||||||
|
|
||||||
|
@ -661,12 +662,13 @@ msgstr "其它"
|
||||||
#: assets/templates/assets/asset_group_create.html:16
|
#: assets/templates/assets/asset_group_create.html:16
|
||||||
#: assets/templates/assets/asset_update.html:55
|
#: assets/templates/assets/asset_update.html:55
|
||||||
#: assets/templates/assets/cluster_create_update.html:54
|
#: assets/templates/assets/cluster_create_update.html:54
|
||||||
#: common/templates/common/basic_setting.html:56
|
#: common/templates/common/basic_setting.html:58
|
||||||
#: common/templates/common/email_setting.html:56
|
#: common/templates/common/email_setting.html:59
|
||||||
#: common/templates/common/ldap_setting.html:56
|
#: common/templates/common/ldap_setting.html:59
|
||||||
|
#: common/templates/common/terminal_setting.html:82
|
||||||
#: perms/templates/perms/asset_permission_create_update.html:67
|
#: perms/templates/perms/asset_permission_create_update.html:67
|
||||||
#: terminal/templates/terminal/terminal_update.html:45
|
#: terminal/templates/terminal/terminal_update.html:46
|
||||||
#: users/templates/users/_user.html:49
|
#: users/templates/users/_user.html:43
|
||||||
#: users/templates/users/user_bulk_update.html:23
|
#: users/templates/users/user_bulk_update.html:23
|
||||||
#: users/templates/users/user_password_update.html:58
|
#: users/templates/users/user_password_update.html:58
|
||||||
#: users/templates/users/user_profile.html:139
|
#: users/templates/users/user_profile.html:139
|
||||||
|
@ -681,15 +683,16 @@ msgstr "重置"
|
||||||
#: assets/templates/assets/asset_bulk_update.html:24
|
#: assets/templates/assets/asset_bulk_update.html:24
|
||||||
#: assets/templates/assets/asset_create.html:41
|
#: assets/templates/assets/asset_create.html:41
|
||||||
#: assets/templates/assets/asset_group_create.html:17
|
#: assets/templates/assets/asset_group_create.html:17
|
||||||
#: assets/templates/assets/asset_list.html:55
|
#: assets/templates/assets/asset_list.html:53
|
||||||
#: assets/templates/assets/asset_update.html:56
|
#: assets/templates/assets/asset_update.html:56
|
||||||
#: assets/templates/assets/cluster_create_update.html:55
|
#: assets/templates/assets/cluster_create_update.html:55
|
||||||
#: common/templates/common/basic_setting.html:57
|
#: common/templates/common/basic_setting.html:59
|
||||||
#: common/templates/common/email_setting.html:57
|
#: common/templates/common/email_setting.html:60
|
||||||
#: common/templates/common/ldap_setting.html:57
|
#: common/templates/common/ldap_setting.html:60
|
||||||
|
#: common/templates/common/terminal_setting.html:83
|
||||||
#: perms/templates/perms/asset_permission_create_update.html:68
|
#: perms/templates/perms/asset_permission_create_update.html:68
|
||||||
#: terminal/templates/terminal/terminal_update.html:46
|
#: terminal/templates/terminal/terminal_update.html:47
|
||||||
#: users/templates/users/_user.html:50
|
#: users/templates/users/_user.html:44
|
||||||
#: users/templates/users/first_login.html:62
|
#: users/templates/users/first_login.html:62
|
||||||
#: users/templates/users/forgot_password.html:44
|
#: users/templates/users/forgot_password.html:44
|
||||||
#: users/templates/users/user_bulk_update.html:24
|
#: users/templates/users/user_bulk_update.html:24
|
||||||
|
@ -726,8 +729,8 @@ msgstr "资产列表"
|
||||||
#: assets/templates/assets/asset_detail.html:24
|
#: assets/templates/assets/asset_detail.html:24
|
||||||
#: assets/templates/assets/asset_group_detail.html:18
|
#: assets/templates/assets/asset_group_detail.html:18
|
||||||
#: assets/templates/assets/asset_group_detail.html:177
|
#: assets/templates/assets/asset_group_detail.html:177
|
||||||
#: assets/templates/assets/asset_group_list.html:42
|
#: assets/templates/assets/asset_group_list.html:38
|
||||||
#: assets/templates/assets/asset_list.html:95
|
#: assets/templates/assets/asset_list.html:98
|
||||||
#: assets/templates/assets/cluster_assets.html:170
|
#: assets/templates/assets/cluster_assets.html:170
|
||||||
#: assets/templates/assets/cluster_detail.html:25
|
#: assets/templates/assets/cluster_detail.html:25
|
||||||
#: assets/templates/assets/cluster_list.html:43
|
#: assets/templates/assets/cluster_list.html:43
|
||||||
|
@ -750,8 +753,8 @@ msgstr "更新"
|
||||||
#: assets/templates/assets/admin_user_list.html:84
|
#: assets/templates/assets/admin_user_list.html:84
|
||||||
#: assets/templates/assets/asset_detail.html:28
|
#: assets/templates/assets/asset_detail.html:28
|
||||||
#: assets/templates/assets/asset_group_detail.html:22
|
#: assets/templates/assets/asset_group_detail.html:22
|
||||||
#: assets/templates/assets/asset_group_list.html:43
|
#: assets/templates/assets/asset_group_list.html:39
|
||||||
#: assets/templates/assets/asset_list.html:96
|
#: assets/templates/assets/asset_list.html:99
|
||||||
#: assets/templates/assets/cluster_detail.html:29
|
#: assets/templates/assets/cluster_detail.html:29
|
||||||
#: assets/templates/assets/cluster_list.html:44
|
#: assets/templates/assets/cluster_list.html:44
|
||||||
#: assets/templates/assets/system_user_detail.html:30
|
#: assets/templates/assets/system_user_detail.html:30
|
||||||
|
@ -776,9 +779,9 @@ msgstr "资产列表"
|
||||||
|
|
||||||
#: assets/templates/assets/admin_user_assets.html:62
|
#: assets/templates/assets/admin_user_assets.html:62
|
||||||
#: assets/templates/assets/asset_group_detail.html:53
|
#: assets/templates/assets/asset_group_detail.html:53
|
||||||
#: assets/templates/assets/asset_list.html:34
|
|
||||||
#: assets/templates/assets/cluster_assets.html:54
|
#: assets/templates/assets/cluster_assets.html:54
|
||||||
#: assets/templates/assets/user_asset_list.html:22
|
#: assets/templates/assets/user_asset_list.html:22
|
||||||
|
#: common/templates/common/terminal_setting.html:63
|
||||||
#: users/templates/users/login_log_list.html:50
|
#: users/templates/users/login_log_list.html:50
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "类型"
|
msgstr "类型"
|
||||||
|
@ -786,7 +789,7 @@ msgstr "类型"
|
||||||
#: assets/templates/assets/admin_user_assets.html:63
|
#: assets/templates/assets/admin_user_assets.html:63
|
||||||
#: assets/templates/assets/admin_user_list.html:25
|
#: assets/templates/assets/admin_user_list.html:25
|
||||||
#: assets/templates/assets/asset_detail.html:376
|
#: assets/templates/assets/asset_detail.html:376
|
||||||
#: assets/templates/assets/asset_list.html:38
|
#: assets/templates/assets/asset_list.html:36
|
||||||
#: assets/templates/assets/system_user_asset.html:55
|
#: assets/templates/assets/system_user_asset.html:55
|
||||||
#: assets/templates/assets/system_user_list.html:27
|
#: assets/templates/assets/system_user_list.html:27
|
||||||
msgid "Reachable"
|
msgid "Reachable"
|
||||||
|
@ -827,8 +830,8 @@ msgstr "使用集群管理用户"
|
||||||
|
|
||||||
#: assets/templates/assets/admin_user_detail.html:101
|
#: assets/templates/assets/admin_user_detail.html:101
|
||||||
#: assets/templates/assets/asset_detail.html:230
|
#: assets/templates/assets/asset_detail.html:230
|
||||||
#: assets/templates/assets/asset_group_list.html:85
|
#: assets/templates/assets/asset_group_list.html:81
|
||||||
#: assets/templates/assets/asset_list.html:214
|
#: assets/templates/assets/asset_list.html:220
|
||||||
#: assets/templates/assets/cluster_assets.html:104
|
#: assets/templates/assets/cluster_assets.html:104
|
||||||
#: assets/templates/assets/cluster_list.html:89
|
#: assets/templates/assets/cluster_list.html:89
|
||||||
#: assets/templates/assets/system_user_detail.html:164
|
#: assets/templates/assets/system_user_detail.html:164
|
||||||
|
@ -859,7 +862,7 @@ msgstr "比例"
|
||||||
#: assets/templates/assets/admin_user_list.html:29
|
#: assets/templates/assets/admin_user_list.html:29
|
||||||
#: assets/templates/assets/asset_group_detail.html:55
|
#: assets/templates/assets/asset_group_detail.html:55
|
||||||
#: assets/templates/assets/asset_group_list.html:18
|
#: assets/templates/assets/asset_group_list.html:18
|
||||||
#: assets/templates/assets/asset_list.html:39
|
#: assets/templates/assets/asset_list.html:37
|
||||||
#: assets/templates/assets/cluster_assets.html:56
|
#: assets/templates/assets/cluster_assets.html:56
|
||||||
#: assets/templates/assets/cluster_list.html:23
|
#: assets/templates/assets/cluster_list.html:23
|
||||||
#: assets/templates/assets/system_user_list.html:31
|
#: assets/templates/assets/system_user_list.html:31
|
||||||
|
@ -879,7 +882,7 @@ msgid "Group"
|
||||||
msgstr "组"
|
msgstr "组"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:186
|
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:186
|
||||||
#: assets/views/cluster.py:103
|
#: assets/views/cluster.py:98
|
||||||
msgid "Asset detail"
|
msgid "Asset detail"
|
||||||
msgstr "资产详情"
|
msgstr "资产详情"
|
||||||
|
|
||||||
|
@ -909,7 +912,7 @@ msgid "Quick modify"
|
||||||
msgstr "快速修改"
|
msgstr "快速修改"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_detail.html:175
|
#: assets/templates/assets/asset_detail.html:175
|
||||||
#: assets/templates/assets/asset_list.html:37
|
#: assets/templates/assets/asset_list.html:35
|
||||||
#: assets/templates/assets/user_asset_list.html:25 perms/models.py:20
|
#: assets/templates/assets/user_asset_list.html:25 perms/models.py:20
|
||||||
#: perms/templates/perms/asset_permission_create_update.html:47
|
#: perms/templates/perms/asset_permission_create_update.html:47
|
||||||
#: perms/templates/perms/asset_permission_detail.html:116
|
#: perms/templates/perms/asset_permission_detail.html:116
|
||||||
|
@ -970,8 +973,8 @@ msgstr "移除"
|
||||||
msgid "Create asset group"
|
msgid "Create asset group"
|
||||||
msgstr "创建资产组"
|
msgstr "创建资产组"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_group_list.html:80
|
#: assets/templates/assets/asset_group_list.html:76
|
||||||
#: assets/templates/assets/asset_list.html:209
|
#: assets/templates/assets/asset_list.html:215
|
||||||
#: assets/templates/assets/cluster_list.html:84
|
#: assets/templates/assets/cluster_list.html:84
|
||||||
#: assets/templates/assets/system_user_list.html:129
|
#: assets/templates/assets/system_user_list.html:129
|
||||||
#: users/templates/users/user_detail.html:333
|
#: users/templates/users/user_detail.html:333
|
||||||
|
@ -981,29 +984,29 @@ msgstr "创建资产组"
|
||||||
msgid "Are you sure?"
|
msgid "Are you sure?"
|
||||||
msgstr "你确认吗?"
|
msgstr "你确认吗?"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_group_list.html:81
|
#: assets/templates/assets/asset_group_list.html:77
|
||||||
#: users/templates/users/user_group_list.html:78
|
#: users/templates/users/user_group_list.html:78
|
||||||
msgid "This will delete the selected groups !!!"
|
msgid "This will delete the selected groups !!!"
|
||||||
msgstr "删除选择组"
|
msgstr "删除选择组"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_group_list.html:89
|
#: assets/templates/assets/asset_group_list.html:85
|
||||||
msgid "Group deleted"
|
msgid "Group deleted"
|
||||||
msgstr "组已被删除"
|
msgstr "组已被删除"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_group_list.html:90
|
#: assets/templates/assets/asset_group_list.html:86
|
||||||
#: assets/templates/assets/asset_group_list.html:95
|
#: assets/templates/assets/asset_group_list.html:91
|
||||||
msgid "Group Delete"
|
msgid "Group Delete"
|
||||||
msgstr "删除"
|
msgstr "删除"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_group_list.html:94
|
#: assets/templates/assets/asset_group_list.html:90
|
||||||
msgid "Group deleting failed."
|
msgid "Group deleting failed."
|
||||||
msgstr "删除失败"
|
msgstr "删除失败"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_group_list.html:157
|
#: assets/templates/assets/asset_group_list.html:153
|
||||||
msgid "The selected asset groups has been updated successfully."
|
msgid "The selected asset groups has been updated successfully."
|
||||||
msgstr "更新成功"
|
msgstr "更新成功"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_group_list.html:158
|
#: assets/templates/assets/asset_group_list.html:154
|
||||||
msgid "AssetGroup Updated"
|
msgid "AssetGroup Updated"
|
||||||
msgstr "资产组更新"
|
msgstr "资产组更新"
|
||||||
|
|
||||||
|
@ -1021,52 +1024,47 @@ msgstr "导出"
|
||||||
msgid "Create asset"
|
msgid "Create asset"
|
||||||
msgstr "创建资产"
|
msgstr "创建资产"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_list.html:35
|
#: assets/templates/assets/asset_list.html:34
|
||||||
#: assets/templates/assets/user_asset_list.html:23
|
|
||||||
msgid "Env"
|
|
||||||
msgstr "环境"
|
|
||||||
|
|
||||||
#: assets/templates/assets/asset_list.html:36
|
|
||||||
#: assets/templates/assets/user_asset_list.html:24
|
#: assets/templates/assets/user_asset_list.html:24
|
||||||
msgid "Hardware"
|
msgid "Hardware"
|
||||||
msgstr "硬件"
|
msgstr "硬件"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_list.html:48
|
#: assets/templates/assets/asset_list.html:46
|
||||||
#: users/templates/users/user_list.html:37
|
#: users/templates/users/user_list.html:37
|
||||||
msgid "Delete selected"
|
msgid "Delete selected"
|
||||||
msgstr "批量删除"
|
msgstr "批量删除"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_list.html:49
|
#: assets/templates/assets/asset_list.html:47
|
||||||
#: users/templates/users/user_list.html:38
|
#: users/templates/users/user_list.html:38
|
||||||
msgid "Update selected"
|
msgid "Update selected"
|
||||||
msgstr "批量更新"
|
msgstr "批量更新"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_list.html:50
|
#: assets/templates/assets/asset_list.html:48
|
||||||
#: users/templates/users/user_list.html:39
|
#: users/templates/users/user_list.html:39
|
||||||
msgid "Deactive selected"
|
msgid "Deactive selected"
|
||||||
msgstr "禁用所选"
|
msgstr "禁用所选"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_list.html:51
|
#: assets/templates/assets/asset_list.html:49
|
||||||
#: users/templates/users/user_list.html:40
|
#: users/templates/users/user_list.html:40
|
||||||
msgid "Active selected"
|
msgid "Active selected"
|
||||||
msgstr "激活所选"
|
msgstr "激活所选"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_list.html:210
|
#: assets/templates/assets/asset_list.html:216
|
||||||
msgid "This will delete the selected assets !!!"
|
msgid "This will delete the selected assets !!!"
|
||||||
msgstr "删除选择资产"
|
msgstr "删除选择资产"
|
||||||
|
|
||||||
# msgid "Deleted!"
|
# msgid "Deleted!"
|
||||||
# msgstr "删除"
|
# msgstr "删除"
|
||||||
#: assets/templates/assets/asset_list.html:218
|
#: assets/templates/assets/asset_list.html:224
|
||||||
msgid "Asset Deleted."
|
msgid "Asset Deleted."
|
||||||
msgstr "已被删除"
|
msgstr "已被删除"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_list.html:219
|
#: assets/templates/assets/asset_list.html:225
|
||||||
#: assets/templates/assets/asset_list.html:224
|
#: assets/templates/assets/asset_list.html:230
|
||||||
msgid "Asset Delete"
|
msgid "Asset Delete"
|
||||||
msgstr "删除"
|
msgstr "删除"
|
||||||
|
|
||||||
#: assets/templates/assets/asset_list.html:223
|
#: assets/templates/assets/asset_list.html:229
|
||||||
msgid "Asset Deleting failed."
|
msgid "Asset Deleting failed."
|
||||||
msgstr "删除失败"
|
msgstr "删除失败"
|
||||||
|
|
||||||
|
@ -1202,6 +1200,10 @@ msgstr "删除系统用户"
|
||||||
msgid "System Users Deleting failed."
|
msgid "System Users Deleting failed."
|
||||||
msgstr "系统用户删除失败"
|
msgstr "系统用户删除失败"
|
||||||
|
|
||||||
|
#: assets/templates/assets/user_asset_list.html:23
|
||||||
|
msgid "Env"
|
||||||
|
msgstr "环境"
|
||||||
|
|
||||||
#: assets/templates/assets/user_asset_list.html:26
|
#: assets/templates/assets/user_asset_list.html:26
|
||||||
msgid "Connective"
|
msgid "Connective"
|
||||||
msgstr "连接性"
|
msgstr "连接性"
|
||||||
|
@ -1238,16 +1240,16 @@ msgstr "已经存在"
|
||||||
msgid "Cluster list"
|
msgid "Cluster list"
|
||||||
msgstr "集群列表"
|
msgstr "集群列表"
|
||||||
|
|
||||||
#: assets/views/cluster.py:42 assets/views/cluster.py:70
|
#: assets/views/cluster.py:42 assets/views/cluster.py:65
|
||||||
#: assets/views/system_user.py:96
|
#: assets/views/system_user.py:96
|
||||||
msgid "assets"
|
msgid "assets"
|
||||||
msgstr "资产管理"
|
msgstr "资产管理"
|
||||||
|
|
||||||
#: assets/views/cluster.py:71
|
#: assets/views/cluster.py:66
|
||||||
msgid "Update Cluster"
|
msgid "Update Cluster"
|
||||||
msgstr "更新Cluster"
|
msgstr "更新Cluster"
|
||||||
|
|
||||||
#: assets/views/cluster.py:86
|
#: assets/views/cluster.py:81
|
||||||
msgid "Cluster detail"
|
msgid "Cluster detail"
|
||||||
msgstr "集群详情"
|
msgstr "集群详情"
|
||||||
|
|
||||||
|
@ -1293,82 +1295,119 @@ msgstr "<b>%(name)s</b> 创建成功"
|
||||||
msgid "<b>%(name)s</b> was updated successfully"
|
msgid "<b>%(name)s</b> was updated successfully"
|
||||||
msgstr "<b>%(name)s</b> 更新成功"
|
msgstr "<b>%(name)s</b> 更新成功"
|
||||||
|
|
||||||
#: common/forms.py:64
|
#: common/fields.py:25
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Not a valid ssh public key"
|
||||||
|
msgid "Not a valid json"
|
||||||
|
msgstr "ssh密钥不合法"
|
||||||
|
|
||||||
|
#: common/fields.py:27
|
||||||
|
msgid "Not a string type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: common/forms.py:70
|
||||||
msgid "Current SITE URL"
|
msgid "Current SITE URL"
|
||||||
msgstr "当前站点URL"
|
msgstr "当前站点URL"
|
||||||
|
|
||||||
#: common/forms.py:68
|
#: common/forms.py:74
|
||||||
msgid "User Guide URL"
|
msgid "User Guide URL"
|
||||||
msgstr "用户向导URL"
|
msgstr "用户向导URL"
|
||||||
|
|
||||||
#: common/forms.py:69
|
#: common/forms.py:75
|
||||||
msgid "User first login update profile done redirect to it"
|
msgid "User first login update profile done redirect to it"
|
||||||
msgstr "用户第一次登录,修改profile后重定向到地址"
|
msgstr "用户第一次登录,修改profile后重定向到地址"
|
||||||
|
|
||||||
#: common/forms.py:72
|
#: common/forms.py:78
|
||||||
msgid "Email Subject Prefix"
|
msgid "Email Subject Prefix"
|
||||||
msgstr "Email主题前缀"
|
msgstr "Email主题前缀"
|
||||||
|
|
||||||
#: common/forms.py:76
|
#: common/forms.py:85
|
||||||
msgid "Enable LDAP Auth"
|
|
||||||
msgstr "二次验证"
|
|
||||||
|
|
||||||
#: common/forms.py:82
|
|
||||||
msgid "SMTP host"
|
msgid "SMTP host"
|
||||||
msgstr "SMTP主机"
|
msgstr "SMTP主机"
|
||||||
|
|
||||||
#: common/forms.py:84
|
#: common/forms.py:87
|
||||||
msgid "SMTP port"
|
msgid "SMTP port"
|
||||||
msgstr "SMTP端口"
|
msgstr "SMTP端口"
|
||||||
|
|
||||||
#: common/forms.py:86
|
#: common/forms.py:89
|
||||||
msgid "SMTP user"
|
msgid "SMTP user"
|
||||||
msgstr "SMTP账号"
|
msgstr "SMTP账号"
|
||||||
|
|
||||||
#: common/forms.py:89
|
#: common/forms.py:92
|
||||||
msgid "SMTP password"
|
msgid "SMTP password"
|
||||||
msgstr "SMTP密码"
|
msgstr "SMTP密码"
|
||||||
|
|
||||||
#: common/forms.py:90
|
#: common/forms.py:93
|
||||||
msgid "Some provider use token except password"
|
msgid "Some provider use token except password"
|
||||||
msgstr "一些邮件提供商需要输入的是Token"
|
msgstr "一些邮件提供商需要输入的是Token"
|
||||||
|
|
||||||
#: common/forms.py:93 common/forms.py:130
|
#: common/forms.py:96 common/forms.py:133
|
||||||
msgid "Use SSL"
|
msgid "Use SSL"
|
||||||
msgstr "使用SSL"
|
msgstr "使用SSL"
|
||||||
|
|
||||||
#: common/forms.py:94
|
#: common/forms.py:97
|
||||||
msgid "If SMTP port is 465, may be select"
|
msgid "If SMTP port is 465, may be select"
|
||||||
msgstr "如果SMTP端口是465,通常需要启用SSL"
|
msgstr "如果SMTP端口是465,通常需要启用SSL"
|
||||||
|
|
||||||
#: common/forms.py:97
|
#: common/forms.py:100
|
||||||
msgid "Use TLS"
|
msgid "Use TLS"
|
||||||
msgstr "使用TLS"
|
msgstr "使用TLS"
|
||||||
|
|
||||||
#: common/forms.py:98
|
#: common/forms.py:101
|
||||||
msgid "If SMTP port is 587, may be select"
|
msgid "If SMTP port is 587, may be select"
|
||||||
msgstr "如果SMTP端口是587,通常需要启用TLS"
|
msgstr "如果SMTP端口是587,通常需要启用TLS"
|
||||||
|
|
||||||
#: common/forms.py:104
|
#: common/forms.py:107
|
||||||
msgid "LDAP server"
|
msgid "LDAP server"
|
||||||
msgstr "LDAP地址"
|
msgstr "LDAP地址"
|
||||||
|
|
||||||
#: common/forms.py:107
|
#: common/forms.py:110
|
||||||
msgid "Bind DN"
|
msgid "Bind DN"
|
||||||
msgstr "绑定DN"
|
msgstr "绑定DN"
|
||||||
|
|
||||||
#: common/forms.py:114
|
#: common/forms.py:117
|
||||||
msgid "User OU"
|
msgid "User OU"
|
||||||
msgstr "用户OU"
|
msgstr "用户OU"
|
||||||
|
|
||||||
#: common/forms.py:117
|
#: common/forms.py:120
|
||||||
msgid "User search filter"
|
msgid "User search filter"
|
||||||
msgstr "用户过滤器"
|
msgstr "用户过滤器"
|
||||||
|
|
||||||
#: common/forms.py:120
|
#: common/forms.py:123
|
||||||
msgid "User attr map"
|
msgid "User attr map"
|
||||||
msgstr "LDAP属性映射"
|
msgstr "LDAP属性映射"
|
||||||
|
|
||||||
|
#: common/forms.py:143
|
||||||
|
msgid "List sort by"
|
||||||
|
msgstr "资产列表排序"
|
||||||
|
|
||||||
|
#: common/forms.py:146
|
||||||
|
msgid "Heartbeat interval"
|
||||||
|
msgstr "心跳间隔"
|
||||||
|
|
||||||
|
#: common/forms.py:146 ops/models.py:32
|
||||||
|
msgid "Units: seconds"
|
||||||
|
msgstr "单位: 秒"
|
||||||
|
|
||||||
|
#: common/forms.py:149
|
||||||
|
msgid "Password auth"
|
||||||
|
msgstr "密码认证"
|
||||||
|
|
||||||
|
#: common/forms.py:152
|
||||||
|
msgid "Public key auth"
|
||||||
|
msgstr "秘钥认证"
|
||||||
|
|
||||||
|
#: common/forms.py:155 common/templates/common/terminal_setting.html:58
|
||||||
|
#: terminal/forms.py:21 terminal/models.py:19
|
||||||
|
msgid "Command storage"
|
||||||
|
msgstr "命令存储"
|
||||||
|
|
||||||
|
#: common/forms.py:156
|
||||||
|
msgid ""
|
||||||
|
"Set terminal storage setting, `default` is the using as default,You can set "
|
||||||
|
"other storage and some terminal using"
|
||||||
|
msgstr "设置终端命令存储,default是默认用的存储方式"
|
||||||
|
|
||||||
#: common/mixins.py:29
|
#: common/mixins.py:29
|
||||||
msgid "is discard"
|
msgid "is discard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -1377,55 +1416,61 @@ msgstr ""
|
||||||
msgid "discard time"
|
msgid "discard time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: common/models.py:26
|
#: common/models.py:27
|
||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "值"
|
msgstr "值"
|
||||||
|
|
||||||
#: common/models.py:27
|
#: common/models.py:29
|
||||||
msgid "Enabled"
|
msgid "Enabled"
|
||||||
msgstr "启用"
|
msgstr "启用"
|
||||||
|
|
||||||
#: common/templates/common/basic_setting.html:15
|
#: common/templates/common/basic_setting.html:15
|
||||||
#: common/templates/common/email_setting.html:15
|
#: common/templates/common/email_setting.html:15
|
||||||
#: common/templates/common/ldap_setting.html:15 common/views.py:18
|
#: common/templates/common/ldap_setting.html:15
|
||||||
|
#: common/templates/common/terminal_setting.html:15
|
||||||
|
#: common/templates/common/terminal_setting.html:38 common/views.py:21
|
||||||
msgid "Basic setting"
|
msgid "Basic setting"
|
||||||
msgstr "基本设置"
|
msgstr "基本设置"
|
||||||
|
|
||||||
#: common/templates/common/basic_setting.html:18
|
#: common/templates/common/basic_setting.html:18
|
||||||
#: common/templates/common/email_setting.html:18
|
#: common/templates/common/email_setting.html:18
|
||||||
#: common/templates/common/ldap_setting.html:18 common/views.py:45
|
#: common/templates/common/ldap_setting.html:18
|
||||||
|
#: common/templates/common/terminal_setting.html:18 common/views.py:47
|
||||||
msgid "Email setting"
|
msgid "Email setting"
|
||||||
msgstr "邮件设置"
|
msgstr "邮件设置"
|
||||||
|
|
||||||
#: common/templates/common/basic_setting.html:21
|
#: common/templates/common/basic_setting.html:21
|
||||||
#: common/templates/common/email_setting.html:21
|
#: common/templates/common/email_setting.html:21
|
||||||
#: common/templates/common/ldap_setting.html:21 common/views.py:70
|
#: common/templates/common/ldap_setting.html:21
|
||||||
|
#: common/templates/common/terminal_setting.html:21 common/views.py:73
|
||||||
msgid "LDAP setting"
|
msgid "LDAP setting"
|
||||||
msgstr "LDAP设置"
|
msgstr "LDAP设置"
|
||||||
|
|
||||||
#: common/templates/common/basic_setting.html:55
|
#: common/templates/common/basic_setting.html:24
|
||||||
#: common/templates/common/email_setting.html:55
|
#: common/templates/common/email_setting.html:24
|
||||||
#: common/templates/common/ldap_setting.html:55
|
#: common/templates/common/ldap_setting.html:24
|
||||||
|
#: common/templates/common/terminal_setting.html:24 common/views.py:102
|
||||||
|
msgid "Terminal setting"
|
||||||
|
msgstr "终端设置"
|
||||||
|
|
||||||
|
#: common/templates/common/email_setting.html:58
|
||||||
|
#: common/templates/common/ldap_setting.html:58
|
||||||
msgid "Test connection"
|
msgid "Test connection"
|
||||||
msgstr "测试连接"
|
msgstr "测试连接"
|
||||||
|
|
||||||
#: common/views.py:17 common/views.py:44 common/views.py:69
|
#: common/views.py:20 common/views.py:46 common/views.py:72 common/views.py:101
|
||||||
#: templates/_nav.html:69
|
#: templates/_nav.html:69
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr "系统设置"
|
msgstr "系统设置"
|
||||||
|
|
||||||
#: common/views.py:30 common/views.py:55 common/views.py:80
|
#: common/views.py:31 common/views.py:57 common/views.py:85 common/views.py:113
|
||||||
msgid "Update setting successfully"
|
msgid "Update setting successfully, please restart program"
|
||||||
msgstr "更新设置成功"
|
msgstr "更新设置成功, 请手动重启程序"
|
||||||
|
|
||||||
#: ops/models.py:32
|
#: ops/models.py:32
|
||||||
msgid "Interval"
|
msgid "Interval"
|
||||||
msgstr "间隔"
|
msgstr "间隔"
|
||||||
|
|
||||||
#: ops/models.py:32
|
|
||||||
msgid "Units: seconds"
|
|
||||||
msgstr "单位: 秒"
|
|
||||||
|
|
||||||
#: ops/models.py:33
|
#: ops/models.py:33
|
||||||
msgid "Crontab"
|
msgid "Crontab"
|
||||||
msgstr "Crontab"
|
msgstr "Crontab"
|
||||||
|
@ -1569,7 +1614,7 @@ msgstr "执行历史"
|
||||||
|
|
||||||
#: ops/templates/ops/adhoc_history.html:52
|
#: ops/templates/ops/adhoc_history.html:52
|
||||||
#: ops/templates/ops/adhoc_history_detail.html:58
|
#: ops/templates/ops/adhoc_history_detail.html:58
|
||||||
#: ops/templates/ops/task_history.html:55 terminal/models.py:101
|
#: ops/templates/ops/task_history.html:55 terminal/models.py:124
|
||||||
#: terminal/templates/terminal/session_list.html:77
|
#: terminal/templates/terminal/session_list.html:77
|
||||||
msgid "Date start"
|
msgid "Date start"
|
||||||
msgstr "开始日期"
|
msgstr "开始日期"
|
||||||
|
@ -1686,18 +1731,18 @@ msgid "Task run history"
|
||||||
msgstr "执行历史"
|
msgstr "执行历史"
|
||||||
|
|
||||||
#: perms/forms.py:16 users/forms.py:147 users/forms.py:152 users/forms.py:164
|
#: perms/forms.py:16 users/forms.py:147 users/forms.py:152 users/forms.py:164
|
||||||
#: users/forms.py:195
|
#: users/forms.py:194
|
||||||
msgid "Select users"
|
msgid "Select users"
|
||||||
msgstr "选择用户"
|
msgstr "选择用户"
|
||||||
|
|
||||||
#: perms/forms.py:18 perms/models.py:15
|
#: perms/forms.py:18 perms/models.py:15
|
||||||
#: perms/templates/perms/asset_permission_create_update.html:36
|
#: perms/templates/perms/asset_permission_create_update.html:36
|
||||||
#: perms/templates/perms/asset_permission_list.html:26 templates/_nav.html:12
|
#: perms/templates/perms/asset_permission_list.html:26 templates/_nav.html:12
|
||||||
#: terminal/backends/command/models.py:10 terminal/models.py:92
|
#: terminal/backends/command/models.py:10 terminal/models.py:115
|
||||||
#: terminal/templates/terminal/command_list.html:32
|
#: terminal/templates/terminal/command_list.html:32
|
||||||
#: terminal/templates/terminal/command_list.html:72
|
#: terminal/templates/terminal/command_list.html:72
|
||||||
#: terminal/templates/terminal/session_list.html:33
|
#: terminal/templates/terminal/session_list.html:33
|
||||||
#: terminal/templates/terminal/session_list.html:71 users/forms.py:191
|
#: terminal/templates/terminal/session_list.html:71 users/forms.py:190
|
||||||
#: users/models/user.py:30 users/templates/users/user_group_detail.html:78
|
#: users/models/user.py:30 users/templates/users/user_group_detail.html:78
|
||||||
#: users/views/user.py:337
|
#: users/views/user.py:337
|
||||||
msgid "User"
|
msgid "User"
|
||||||
|
@ -1842,16 +1887,12 @@ msgstr "资产授权包含用户"
|
||||||
msgid "Asset permission asset list"
|
msgid "Asset permission asset list"
|
||||||
msgstr "资产组授权包含资产"
|
msgstr "资产组授权包含资产"
|
||||||
|
|
||||||
#: templates/_header_bar.html:14
|
|
||||||
msgid "Welcome to use Jumpserver system"
|
|
||||||
msgstr "欢迎使用Jumpserver开源跳板机系统"
|
|
||||||
|
|
||||||
#: templates/_header_bar.html:18
|
#: templates/_header_bar.html:18
|
||||||
msgid "Help"
|
msgid "Help"
|
||||||
msgstr "帮助"
|
msgstr "帮助"
|
||||||
|
|
||||||
#: templates/_header_bar.html:33 templates/_nav_user.html:9
|
#: templates/_header_bar.html:32 templates/_nav_user.html:9
|
||||||
#: users/templates/users/_user.html:42
|
#: users/templates/users/_user.html:36
|
||||||
#: users/templates/users/user_password_update.html:37
|
#: users/templates/users/user_password_update.html:37
|
||||||
#: users/templates/users/user_profile.html:17
|
#: users/templates/users/user_profile.html:17
|
||||||
#: users/templates/users/user_profile_update.html:37
|
#: users/templates/users/user_profile_update.html:37
|
||||||
|
@ -1860,24 +1901,24 @@ msgstr "帮助"
|
||||||
msgid "Profile"
|
msgid "Profile"
|
||||||
msgstr "个人信息"
|
msgstr "个人信息"
|
||||||
|
|
||||||
#: templates/_header_bar.html:37
|
#: templates/_header_bar.html:36
|
||||||
msgid "Admin page"
|
msgid "Admin page"
|
||||||
msgstr "管理页面"
|
msgstr "管理页面"
|
||||||
|
|
||||||
#: templates/_header_bar.html:39
|
#: templates/_header_bar.html:38
|
||||||
msgid "User page"
|
msgid "User page"
|
||||||
msgstr "用户页面"
|
msgstr "用户页面"
|
||||||
|
|
||||||
#: templates/_header_bar.html:42
|
#: templates/_header_bar.html:41
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "注销登录"
|
msgstr "注销登录"
|
||||||
|
|
||||||
#: templates/_header_bar.html:46 users/templates/users/login.html:42
|
#: templates/_header_bar.html:45 users/templates/users/login.html:42
|
||||||
#: users/templates/users/login.html:61
|
#: users/templates/users/login.html:61
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "登录"
|
msgstr "登录"
|
||||||
|
|
||||||
#: templates/_header_bar.html:59 templates/_nav.html:4
|
#: templates/_header_bar.html:58 templates/_nav.html:4
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr "仪表盘"
|
msgstr "仪表盘"
|
||||||
|
|
||||||
|
@ -1912,8 +1953,8 @@ msgid "Close"
|
||||||
msgstr "关闭"
|
msgstr "关闭"
|
||||||
|
|
||||||
#: templates/_nav.html:9 users/views/group.py:28 users/views/group.py:44
|
#: templates/_nav.html:9 users/views/group.py:28 users/views/group.py:44
|
||||||
#: users/views/group.py:62 users/views/group.py:79 users/views/login.py:194
|
#: users/views/group.py:62 users/views/group.py:79 users/views/login.py:197
|
||||||
#: users/views/login.py:243 users/views/user.py:57 users/views/user.py:72
|
#: users/views/login.py:246 users/views/user.py:57 users/views/user.py:72
|
||||||
#: users/views/user.py:91 users/views/user.py:147 users/views/user.py:304
|
#: users/views/user.py:91 users/views/user.py:147 users/views/user.py:304
|
||||||
#: users/views/user.py:318 users/views/user.py:355 users/views/user.py:377
|
#: users/views/user.py:318 users/views/user.py:355 users/views/user.py:377
|
||||||
msgid "Users"
|
msgid "Users"
|
||||||
|
@ -1952,7 +1993,7 @@ msgstr "在线会话"
|
||||||
msgid "Session offline"
|
msgid "Session offline"
|
||||||
msgstr "离线会话"
|
msgstr "离线会话"
|
||||||
|
|
||||||
#: templates/_nav.html:53 terminal/models.py:99
|
#: templates/_nav.html:53 terminal/models.py:122
|
||||||
#: terminal/templates/terminal/command_list.html:55
|
#: terminal/templates/terminal/command_list.html:55
|
||||||
#: terminal/templates/terminal/command_list.html:71
|
#: terminal/templates/terminal/command_list.html:71
|
||||||
#: terminal/templates/terminal/session_detail.html:48
|
#: terminal/templates/terminal/session_detail.html:48
|
||||||
|
@ -1993,64 +2034,68 @@ msgstr "输出"
|
||||||
msgid "Session"
|
msgid "Session"
|
||||||
msgstr "会话"
|
msgstr "会话"
|
||||||
|
|
||||||
#: terminal/forms.py:15
|
#: terminal/forms.py:27
|
||||||
msgid "Coco ssh listen port"
|
msgid "Coco ssh listen port"
|
||||||
msgstr "SSH 监听端口"
|
msgstr "SSH 监听端口"
|
||||||
|
|
||||||
#: terminal/forms.py:16
|
#: terminal/forms.py:28
|
||||||
msgid "Coco http/ws listen port"
|
msgid "Coco http/ws listen port"
|
||||||
msgstr "Http/Websocket 监听端口"
|
msgstr "Http/Websocket 监听端口"
|
||||||
|
|
||||||
#: terminal/models.py:15
|
#: terminal/models.py:16
|
||||||
msgid "Remote Address"
|
msgid "Remote Address"
|
||||||
msgstr "远端地址"
|
msgstr "远端地址"
|
||||||
|
|
||||||
#: terminal/models.py:16
|
#: terminal/models.py:17
|
||||||
msgid "SSH Port"
|
msgid "SSH Port"
|
||||||
msgstr "SSH端口"
|
msgstr "SSH端口"
|
||||||
|
|
||||||
#: terminal/models.py:17
|
#: terminal/models.py:18
|
||||||
msgid "HTTP Port"
|
msgid "HTTP Port"
|
||||||
msgstr "HTTP端口"
|
msgstr "HTTP端口"
|
||||||
|
|
||||||
#: terminal/models.py:68
|
#: terminal/models.py:20
|
||||||
|
msgid "Replay storage"
|
||||||
|
msgstr "录像存储"
|
||||||
|
|
||||||
|
#: terminal/models.py:91
|
||||||
msgid "Session Online"
|
msgid "Session Online"
|
||||||
msgstr "在线会话"
|
msgstr "在线会话"
|
||||||
|
|
||||||
#: terminal/models.py:69
|
#: terminal/models.py:92
|
||||||
msgid "CPU Usage"
|
msgid "CPU Usage"
|
||||||
msgstr "CPU使用"
|
msgstr "CPU使用"
|
||||||
|
|
||||||
#: terminal/models.py:70
|
#: terminal/models.py:93
|
||||||
msgid "Memory Used"
|
msgid "Memory Used"
|
||||||
msgstr "内存使用"
|
msgstr "内存使用"
|
||||||
|
|
||||||
#: terminal/models.py:71
|
#: terminal/models.py:94
|
||||||
msgid "Connections"
|
msgid "Connections"
|
||||||
msgstr "连接数"
|
msgstr "连接数"
|
||||||
|
|
||||||
#: terminal/models.py:72
|
#: terminal/models.py:95
|
||||||
msgid "Threads"
|
msgid "Threads"
|
||||||
msgstr "线程数"
|
msgstr "线程数"
|
||||||
|
|
||||||
#: terminal/models.py:73
|
#: terminal/models.py:96
|
||||||
msgid "Boot Time"
|
msgid "Boot Time"
|
||||||
msgstr "运行时间"
|
msgstr "运行时间"
|
||||||
|
|
||||||
#: terminal/models.py:96 terminal/templates/terminal/session_list.html:74
|
#: terminal/models.py:119 terminal/templates/terminal/session_list.html:74
|
||||||
#: terminal/templates/terminal/terminal_detail.html:47
|
#: terminal/templates/terminal/terminal_detail.html:47
|
||||||
msgid "Remote addr"
|
msgid "Remote addr"
|
||||||
msgstr "远端地址"
|
msgstr "远端地址"
|
||||||
|
|
||||||
#: terminal/models.py:98 terminal/templates/terminal/session_list.html:100
|
#: terminal/models.py:121 terminal/templates/terminal/session_list.html:100
|
||||||
msgid "Replay"
|
msgid "Replay"
|
||||||
msgstr "回放"
|
msgstr "回放"
|
||||||
|
|
||||||
#: terminal/models.py:102
|
#: terminal/models.py:125
|
||||||
msgid "Date end"
|
msgid "Date end"
|
||||||
msgstr "结束日期"
|
msgstr "结束日期"
|
||||||
|
|
||||||
#: terminal/models.py:119
|
#: terminal/models.py:142
|
||||||
msgid "Args"
|
msgid "Args"
|
||||||
msgstr "参数"
|
msgstr "参数"
|
||||||
|
|
||||||
|
@ -2404,7 +2449,7 @@ msgstr "忘记密码"
|
||||||
|
|
||||||
#: users/templates/users/forgot_password.html:33
|
#: users/templates/users/forgot_password.html:33
|
||||||
msgid "Input your email, that will send a mail to your"
|
msgid "Input your email, that will send a mail to your"
|
||||||
msgstr "输入您的邮箱, 将会发一封重置短信邮件到您的邮箱中"
|
msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中"
|
||||||
|
|
||||||
#: users/templates/users/login.html:47
|
#: users/templates/users/login.html:47
|
||||||
msgid "Captcha invalid"
|
msgid "Captcha invalid"
|
||||||
|
@ -2473,10 +2518,8 @@ msgid "An e-mail has been sent to the user\\'s mailbox."
|
||||||
msgstr "已发送邮件到用户邮箱"
|
msgstr "已发送邮件到用户邮箱"
|
||||||
|
|
||||||
#: users/templates/users/user_detail.html:334
|
#: users/templates/users/user_detail.html:334
|
||||||
msgid ""
|
msgid "This will reset the user password and send a reset mail"
|
||||||
"This will reset the user's password. A password-reset email will be sent to "
|
msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱"
|
||||||
"the user\\'s mailbox."
|
|
||||||
msgstr "重设密码邮件将会发送到用户邮箱"
|
|
||||||
|
|
||||||
#: users/templates/users/user_detail.html:348
|
#: users/templates/users/user_detail.html:348
|
||||||
msgid ""
|
msgid ""
|
||||||
|
@ -2490,8 +2533,8 @@ msgid "Reset SSH public key"
|
||||||
msgstr "重置SSH密钥"
|
msgstr "重置SSH密钥"
|
||||||
|
|
||||||
#: users/templates/users/user_detail.html:359
|
#: users/templates/users/user_detail.html:359
|
||||||
msgid "This will reset the user\\"
|
msgid "This will reset the user public key and send a reset mail"
|
||||||
msgstr "重置"
|
msgstr "将会失效用户当前秘钥,并发送重置邮件到用户邮箱"
|
||||||
|
|
||||||
#: users/templates/users/user_detail.html:376
|
#: users/templates/users/user_detail.html:376
|
||||||
#: users/templates/users/user_profile.html:170
|
#: users/templates/users/user_profile.html:170
|
||||||
|
@ -2740,48 +2783,48 @@ msgstr "编辑用户组"
|
||||||
msgid "Please enable cookies and try again."
|
msgid "Please enable cookies and try again."
|
||||||
msgstr "设置你的浏览器支持cookie"
|
msgstr "设置你的浏览器支持cookie"
|
||||||
|
|
||||||
#: users/views/login.py:84
|
#: users/views/login.py:87
|
||||||
msgid "Logout success"
|
msgid "Logout success"
|
||||||
msgstr "退出登录成功"
|
msgstr "退出登录成功"
|
||||||
|
|
||||||
#: users/views/login.py:85
|
#: users/views/login.py:88
|
||||||
msgid "Logout success, return login page"
|
msgid "Logout success, return login page"
|
||||||
msgstr "退出登录成功,返回到登录页面"
|
msgstr "退出登录成功,返回到登录页面"
|
||||||
|
|
||||||
#: users/views/login.py:101
|
#: users/views/login.py:104
|
||||||
msgid "Email address invalid, please input again"
|
msgid "Email address invalid, please input again"
|
||||||
msgstr "邮箱地址错误,重新输入"
|
msgstr "邮箱地址错误,重新输入"
|
||||||
|
|
||||||
#: users/views/login.py:114
|
#: users/views/login.py:117
|
||||||
msgid "Send reset password message"
|
msgid "Send reset password message"
|
||||||
msgstr "发送重置密码邮件"
|
msgstr "发送重置密码邮件"
|
||||||
|
|
||||||
#: users/views/login.py:115
|
#: users/views/login.py:118
|
||||||
msgid "Send reset password mail success, login your mail box and follow it "
|
msgid "Send reset password mail success, login your mail box and follow it "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
|
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
|
||||||
|
|
||||||
#: users/views/login.py:129
|
#: users/views/login.py:132
|
||||||
msgid "Reset password success"
|
msgid "Reset password success"
|
||||||
msgstr "重置密码成功"
|
msgstr "重置密码成功"
|
||||||
|
|
||||||
#: users/views/login.py:130
|
#: users/views/login.py:133
|
||||||
msgid "Reset password success, return to login page"
|
msgid "Reset password success, return to login page"
|
||||||
msgstr "重置密码成功,返回到登录页面"
|
msgstr "重置密码成功,返回到登录页面"
|
||||||
|
|
||||||
#: users/views/login.py:147 users/views/login.py:160
|
#: users/views/login.py:150 users/views/login.py:163
|
||||||
msgid "Token invalid or expired"
|
msgid "Token invalid or expired"
|
||||||
msgstr "Token错误或失效"
|
msgstr "Token错误或失效"
|
||||||
|
|
||||||
#: users/views/login.py:156
|
#: users/views/login.py:159
|
||||||
msgid "Password not same"
|
msgid "Password not same"
|
||||||
msgstr "密码不一致"
|
msgstr "密码不一致"
|
||||||
|
|
||||||
#: users/views/login.py:194
|
#: users/views/login.py:197
|
||||||
msgid "First login"
|
msgid "First login"
|
||||||
msgstr "首次登陆"
|
msgstr "首次登陆"
|
||||||
|
|
||||||
#: users/views/login.py:244
|
#: users/views/login.py:247
|
||||||
msgid "Login log list"
|
msgid "Login log list"
|
||||||
msgstr "登录日志"
|
msgstr "登录日志"
|
||||||
|
|
||||||
|
@ -2813,5 +2856,17 @@ msgstr "密码更新"
|
||||||
msgid "Public key update"
|
msgid "Public key update"
|
||||||
msgstr "秘钥更新"
|
msgstr "秘钥更新"
|
||||||
|
|
||||||
|
#~ msgid "Add command storage"
|
||||||
|
#~ msgstr "添加命令存储"
|
||||||
|
|
||||||
|
#~ msgid "Welcome to use Jumpserver system"
|
||||||
|
#~ msgstr "欢迎使用Jumpserver开源跳板机系统"
|
||||||
|
|
||||||
|
#~ msgid "This will reset the user\\"
|
||||||
|
#~ msgstr "重置"
|
||||||
|
|
||||||
|
#~ msgid "Enable LDAP Auth"
|
||||||
|
#~ msgstr "LDAP认证"
|
||||||
|
|
||||||
#~ msgid "Connect"
|
#~ msgid "Connect"
|
||||||
#~ msgstr "连接"
|
#~ msgstr "连接"
|
||||||
|
|
|
@ -165,7 +165,7 @@ class AdHoc(models.Model):
|
||||||
if item and isinstance(item, list):
|
if item and isinstance(item, list):
|
||||||
self._tasks = json.dumps(item)
|
self._tasks = json.dumps(item)
|
||||||
else:
|
else:
|
||||||
raise SyntaxError('Tasks should be a list')
|
raise SyntaxError('Tasks should be a list: {}'.format(item))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hosts(self):
|
def hosts(self):
|
||||||
|
|
|
@ -16,6 +16,8 @@ def update_or_create_ansible_task(
|
||||||
run_as_admin=False, run_as="", become_info=None,
|
run_as_admin=False, run_as="", become_info=None,
|
||||||
created_by=None,
|
created_by=None,
|
||||||
):
|
):
|
||||||
|
if not hosts or not tasks or not task_name:
|
||||||
|
return
|
||||||
|
|
||||||
defaults = {
|
defaults = {
|
||||||
'name': task_name,
|
'name': task_name,
|
||||||
|
@ -41,7 +43,7 @@ def update_or_create_ansible_task(
|
||||||
new_adhoc.become = become_info
|
new_adhoc.become = become_info
|
||||||
|
|
||||||
if not adhoc or adhoc != new_adhoc:
|
if not adhoc or adhoc != new_adhoc:
|
||||||
logger.debug("Task create new adhoc: {}".format(task_name))
|
print("Task create new adhoc: {}".format(task_name))
|
||||||
new_adhoc.save()
|
new_adhoc.save()
|
||||||
task.latest_adhoc = new_adhoc
|
task.latest_adhoc = new_adhoc
|
||||||
created = True
|
created = True
|
||||||
|
|
|
@ -303,7 +303,7 @@ div.dataTables_wrapper div.dataTables_filter {
|
||||||
|
|
||||||
.profile-element div:first-child {
|
.profile-element div:first-child {
|
||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
width: 70px;
|
/*width: 70px;*/
|
||||||
float: left;
|
float: left;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -4,6 +4,6 @@
|
||||||
<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">
|
<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Copyright</strong> 北京堆栈科技有限公司 © 2014-2017
|
<strong>Copyright</strong> 北京堆栈科技有限公司 © 2014-2018
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -10,9 +10,9 @@
|
||||||
<!--</form>-->
|
<!--</form>-->
|
||||||
</div>
|
</div>
|
||||||
<ul class="nav navbar-top-links navbar-right">
|
<ul class="nav navbar-top-links navbar-right">
|
||||||
<li>
|
{# <li>#}
|
||||||
<span class="m-r-sm text-muted welcome-message">{% trans 'Welcome to use Jumpserver system' %}</span>
|
{# <span class="m-r-sm text-muted welcome-message">{% trans 'Welcome to use Jumpserver system' %}</span>#}
|
||||||
</li>
|
{# </li>#}
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a class="dropdown-toggle count-info" data-toggle="dropdown" href="#">
|
<a class="dropdown-toggle count-info" data-toggle="dropdown" href="#">
|
||||||
<span class="m-r-sm text-muted welcome-message">{% trans 'Help' %}</span>
|
<span class="m-r-sm text-muted welcome-message">{% trans 'Help' %}</span>
|
||||||
|
@ -22,11 +22,10 @@
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<a data-toggle="dropdown" class="dropdown-toggle" href="#">
|
<a data-toggle="dropdown" class="dropdown-toggle" href="#">
|
||||||
<span class="m-r-sm text-muted welcome-message">
|
<span class="m-r-sm text-muted welcome-message">
|
||||||
<img alt="image" class="img-circle" width="40" height="40" src="{{ request.user.avatar_url }}"/>
|
<img alt="image" class="img-circle" width="30" height="30" src="{{ request.user.avatar_url }}"/>
|
||||||
<strong class="font-bold"> {{ request.user.name }}
|
<span style="font-size: 13px;font-weight: 400"> {{ request.user.name }}
|
||||||
<span style="color: #8095a8"></span>
|
|
||||||
<b class="caret"></b>
|
<b class="caret"></b>
|
||||||
</strong>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu animated fadeInRight m-t-xs">
|
<ul class="dropdown-menu animated fadeInRight m-t-xs">
|
||||||
|
|
|
@ -2,11 +2,8 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<li class="nav-header">
|
<li class="nav-header">
|
||||||
<div class="dropdown profile-element">
|
<div class="dropdown profile-element">
|
||||||
<div>
|
<div href="http://www.jumpserver.org" target="_blank">
|
||||||
<img alt="image" height="40" src="/static/img/logo.png"/>
|
<img alt="image" height="55" src="/static/img/logo-text.png" style="margin-left: 10px"/>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href="http://www.jumpserver.org" target="_blank">Jumpserver</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
Copyright Jumpserver.org
|
Copyright Jumpserver.org
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 text-right">
|
<div class="col-md-6 text-right">
|
||||||
<small>2014-2017</small>
|
<small>2014-2018</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -164,7 +164,7 @@
|
||||||
{% for login in last_login_ten %}
|
{% for login in last_login_ten %}
|
||||||
<div class="feed-element">
|
<div class="feed-element">
|
||||||
<a href="#" class="pull-left">
|
<a href="#" class="pull-left">
|
||||||
<img alt="image" class="img-circle" src="/static/img/root.png">
|
<img alt="image" class="img-circle" src="{% static 'img/avatar/user.png' %}">
|
||||||
</a>
|
</a>
|
||||||
<div class="media-body ">
|
<div class="media-body ">
|
||||||
{% ifequal login.is_finished 0 %}
|
{% ifequal login.is_finished 0 %}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import copy
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
@ -21,7 +20,8 @@ from .serializers import TerminalSerializer, StatusSerializer, \
|
||||||
SessionSerializer, TaskSerializer, ReplaySerializer
|
SessionSerializer, TaskSerializer, ReplaySerializer
|
||||||
from .hands import IsSuperUserOrAppUser, IsAppUser, \
|
from .hands import IsSuperUserOrAppUser, IsAppUser, \
|
||||||
IsSuperUserOrAppUserOrUserReadonly
|
IsSuperUserOrAppUserOrUserReadonly
|
||||||
from .backends import get_command_store, SessionCommandSerializer
|
from .backends import get_command_store, get_multi_command_store, \
|
||||||
|
SessionCommandSerializer
|
||||||
|
|
||||||
logger = logging.getLogger(__file__)
|
logger = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
@ -141,7 +141,9 @@ class StatusViewSet(viewsets.ModelViewSet):
|
||||||
session = serializer.save()
|
session = serializer.save()
|
||||||
return session
|
return session
|
||||||
else:
|
else:
|
||||||
msg = "session data is not valid {}".format(serializer.errors)
|
msg = "session data is not valid {}: {}".format(
|
||||||
|
serializer.errors, str(serializer.data)
|
||||||
|
)
|
||||||
logger.error(msg)
|
logger.error(msg)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -195,6 +197,7 @@ class CommandViewSet(viewsets.ViewSet):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
command_store = get_command_store()
|
command_store = get_command_store()
|
||||||
|
multi_command_storage = get_multi_command_store()
|
||||||
serializer_class = SessionCommandSerializer
|
serializer_class = SessionCommandSerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsSuperUserOrAppUser,)
|
||||||
|
|
||||||
|
@ -215,7 +218,7 @@ class CommandViewSet(viewsets.ViewSet):
|
||||||
return Response({"msg": msg}, status=401)
|
return Response({"msg": msg}, status=401)
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
queryset = list(self.command_store.all())
|
queryset = self.multi_command_storage.filter()
|
||||||
serializer = self.serializer_class(queryset, many=True)
|
serializer = self.serializer_class(queryset, many=True)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@ -258,3 +261,13 @@ class SessionReplayViewSet(viewsets.ViewSet):
|
||||||
return redirect(url)
|
return redirect(url)
|
||||||
else:
|
else:
|
||||||
return HttpResponseNotFound()
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
|
||||||
|
class TerminalConfig(APIView):
|
||||||
|
permission_classes = (IsAppUser,)
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
user = request.user
|
||||||
|
terminal = user.terminal
|
||||||
|
configs = terminal.config
|
||||||
|
return Response(configs, status=200)
|
||||||
|
|
|
@ -2,9 +2,39 @@ from importlib import import_module
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from .command.serializers import SessionCommandSerializer
|
from .command.serializers import SessionCommandSerializer
|
||||||
|
|
||||||
|
TYPE_ENGINE_MAPPING = {
|
||||||
|
'elasticsearch': 'terminal.backends.command.es',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_command_store():
|
def get_command_store():
|
||||||
command_engine = import_module(settings.COMMAND_STORAGE_BACKEND)
|
params = settings.COMMAND_STORAGE
|
||||||
command_store = command_engine.CommandStore()
|
engine_class = import_module(params['ENGINE'])
|
||||||
return command_store
|
storage = engine_class.CommandStore(params)
|
||||||
|
return storage
|
||||||
|
|
||||||
|
|
||||||
|
def get_terminal_command_store():
|
||||||
|
storage_list = {}
|
||||||
|
for name, params in settings.TERMINAL_COMMAND_STORAGE.items():
|
||||||
|
tp = params['TYPE']
|
||||||
|
if tp == 'server':
|
||||||
|
storage = get_command_store()
|
||||||
|
else:
|
||||||
|
if not TYPE_ENGINE_MAPPING.get(tp):
|
||||||
|
raise AssertionError("Command storage type should in {}".format(
|
||||||
|
', '.join(TYPE_ENGINE_MAPPING.keys()))
|
||||||
|
)
|
||||||
|
engine_class = import_module(TYPE_ENGINE_MAPPING[tp])
|
||||||
|
storage = engine_class.CommandStore(params)
|
||||||
|
storage_list[name] = storage
|
||||||
|
return storage_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_multi_command_store():
|
||||||
|
from .command.multi import CommandStore
|
||||||
|
storage_list = get_terminal_command_store().values()
|
||||||
|
storage = CommandStore(storage_list)
|
||||||
|
return storage
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,3 +19,9 @@ class CommandBase(object):
|
||||||
input=None, session=None):
|
input=None, session=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def count(self, date_from=None, date_to=None,
|
||||||
|
user=None, asset=None, system_user=None,
|
||||||
|
input=None, session=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ from .base import CommandBase
|
||||||
|
|
||||||
class CommandStore(CommandBase):
|
class CommandStore(CommandBase):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, params):
|
||||||
from terminal.models import Command
|
from terminal.models import Command
|
||||||
self.model = Command
|
self.model = Command
|
||||||
|
|
||||||
|
@ -37,9 +37,11 @@ class CommandStore(CommandBase):
|
||||||
))
|
))
|
||||||
return self.model.objects.bulk_create(_commands)
|
return self.model.objects.bulk_create(_commands)
|
||||||
|
|
||||||
def filter(self, date_from=None, date_to=None,
|
@staticmethod
|
||||||
user=None, asset=None, system_user=None,
|
def make_filter_kwargs(
|
||||||
input=None, session=None):
|
date_from=None, date_to=None,
|
||||||
|
user=None, asset=None, system_user=None,
|
||||||
|
input=None, session=None):
|
||||||
filter_kwargs = {}
|
filter_kwargs = {}
|
||||||
date_from_default = timezone.now() - datetime.timedelta(days=7)
|
date_from_default = timezone.now() - datetime.timedelta(days=7)
|
||||||
date_to_default = timezone.now()
|
date_to_default = timezone.now()
|
||||||
|
@ -60,10 +62,28 @@ class CommandStore(CommandBase):
|
||||||
if session:
|
if session:
|
||||||
filter_kwargs['session'] = session
|
filter_kwargs['session'] = session
|
||||||
|
|
||||||
|
return filter_kwargs
|
||||||
|
|
||||||
|
def filter(self, date_from=None, date_to=None,
|
||||||
|
user=None, asset=None, system_user=None,
|
||||||
|
input=None, session=None):
|
||||||
|
filter_kwargs = self.make_filter_kwargs(
|
||||||
|
date_from=date_from, date_to=date_to, user=user,
|
||||||
|
asset=asset, system_user=system_user, input=input,
|
||||||
|
session=session,
|
||||||
|
)
|
||||||
queryset = self.model.objects.filter(**filter_kwargs)
|
queryset = self.model.objects.filter(**filter_kwargs)
|
||||||
return queryset
|
return [command.to_dict() for command in queryset]
|
||||||
|
|
||||||
|
def count(self, date_from=None, date_to=None,
|
||||||
|
user=None, asset=None, system_user=None,
|
||||||
|
input=None, session=None):
|
||||||
|
filter_kwargs = self.make_filter_kwargs(
|
||||||
|
date_from=date_from, date_to=date_to, user=user,
|
||||||
|
asset=asset, system_user=system_user, input=input,
|
||||||
|
session=session,
|
||||||
|
)
|
||||||
|
count = self.model.objects.filter(**filter_kwargs).count()
|
||||||
|
return count
|
||||||
|
|
||||||
def all(self):
|
|
||||||
"""返回所有数据"""
|
|
||||||
return self.model.objects.iterator()
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from jms_es_sdk import ESStore
|
||||||
|
from .base import CommandBase
|
||||||
|
|
||||||
|
|
||||||
|
class CommandStore(CommandBase, ESStore):
|
||||||
|
def __init__(self, params):
|
||||||
|
hosts = params.get('HOSTS', ['http://localhost'])
|
||||||
|
ESStore.__init__(self, hosts=hosts)
|
||||||
|
|
||||||
|
def save(self, command):
|
||||||
|
return ESStore.save(self, command)
|
||||||
|
|
||||||
|
def bulk_save(self, commands):
|
||||||
|
return ESStore.bulk_save(self, commands)
|
||||||
|
|
||||||
|
def filter(self, date_from=None, date_to=None,
|
||||||
|
user=None, asset=None, system_user=None,
|
||||||
|
input=None, session=None):
|
||||||
|
|
||||||
|
data = ESStore.filter(
|
||||||
|
self, date_from=date_from, date_to=date_to,
|
||||||
|
user=user, asset=asset, system_user=system_user,
|
||||||
|
input=input, session=session
|
||||||
|
)
|
||||||
|
return [item["_source"] for item in data["hits"] if item]
|
||||||
|
|
||||||
|
def count(self, date_from=None, date_to=None,
|
||||||
|
user=None, asset=None, system_user=None,
|
||||||
|
input=None, session=None):
|
||||||
|
amount = ESStore.count(
|
||||||
|
self, date_from=date_from, date_to=date_to,
|
||||||
|
user=user, asset=asset, system_user=system_user,
|
||||||
|
input=input, session=session
|
||||||
|
)
|
||||||
|
return amount
|
|
@ -18,5 +18,26 @@ class AbstractSessionCommand(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, d):
|
||||||
|
self = cls()
|
||||||
|
for k, v in d.items():
|
||||||
|
setattr(self, k, v)
|
||||||
|
return self
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_multi_dict(cls, l):
|
||||||
|
commands = []
|
||||||
|
for d in l:
|
||||||
|
command = cls.from_dict(d)
|
||||||
|
commands.append(command)
|
||||||
|
return commands
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
d = {}
|
||||||
|
for field in self._meta.fields:
|
||||||
|
d[field.name] = getattr(self, field.name)
|
||||||
|
return d
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.input
|
return self.input
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from .base import CommandBase
|
||||||
|
|
||||||
|
|
||||||
|
class CommandStore(CommandBase):
|
||||||
|
def __init__(self, storage_list):
|
||||||
|
self.storage_list = storage_list
|
||||||
|
|
||||||
|
def filter(self, **kwargs):
|
||||||
|
queryset = []
|
||||||
|
for storage in self.storage_list:
|
||||||
|
queryset.extend(storage.filter(**kwargs))
|
||||||
|
return sorted(queryset, key=lambda command: command["timestamp"], reverse=True)
|
||||||
|
|
||||||
|
def count(self, **kwargs):
|
||||||
|
amount = 0
|
||||||
|
for storage in self.storage_list:
|
||||||
|
amount += storage.count(**kwargs)
|
||||||
|
return amount
|
||||||
|
|
||||||
|
def save(self, command):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def bulk_save(self, commands):
|
||||||
|
pass
|
|
@ -2,15 +2,27 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from .models import Terminal
|
from .models import Terminal
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_command_storage():
|
||||||
|
# storage_choices = []
|
||||||
|
from common.models import Setting
|
||||||
|
Setting.refresh_all_settings()
|
||||||
|
for k, v in settings.TERMINAL_COMMAND_STORAGE.items():
|
||||||
|
yield (k, k)
|
||||||
|
|
||||||
|
|
||||||
class TerminalForm(forms.ModelForm):
|
class TerminalForm(forms.ModelForm):
|
||||||
|
command_storage = forms.ChoiceField(choices=get_all_command_storage(),
|
||||||
|
label=_("Command storage"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Terminal
|
model = Terminal
|
||||||
fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment']
|
fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment', 'command_storage']
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'ssh_port': _("Coco ssh listen port"),
|
'ssh_port': _("Coco ssh listen port"),
|
||||||
'http_port': _("Coco http/ws listen port"),
|
'http_port': _("Coco http/ws listen port"),
|
||||||
|
|
|
@ -4,6 +4,7 @@ import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from .backends.command.models import AbstractSessionCommand
|
from .backends.command.models import AbstractSessionCommand
|
||||||
|
@ -15,6 +16,8 @@ class Terminal(models.Model):
|
||||||
remote_addr = models.CharField(max_length=128, verbose_name=_('Remote Address'))
|
remote_addr = models.CharField(max_length=128, verbose_name=_('Remote Address'))
|
||||||
ssh_port = models.IntegerField(verbose_name=_('SSH Port'), default=2222)
|
ssh_port = models.IntegerField(verbose_name=_('SSH Port'), default=2222)
|
||||||
http_port = models.IntegerField(verbose_name=_('HTTP Port'), default=5000)
|
http_port = models.IntegerField(verbose_name=_('HTTP Port'), default=5000)
|
||||||
|
command_storage = models.CharField(max_length=128, verbose_name=_("Command storage"), default='default')
|
||||||
|
replay_storage = models.CharField(max_length=128, verbose_name=_("Replay storage"), default='default')
|
||||||
user = models.OneToOneField(User, related_name='terminal', verbose_name='Application User', null=True, on_delete=models.CASCADE)
|
user = models.OneToOneField(User, related_name='terminal', verbose_name='Application User', null=True, on_delete=models.CASCADE)
|
||||||
is_accepted = models.BooleanField(default=False, verbose_name='Is Accepted')
|
is_accepted = models.BooleanField(default=False, verbose_name='Is Accepted')
|
||||||
is_deleted = models.BooleanField(default=False)
|
is_deleted = models.BooleanField(default=False)
|
||||||
|
@ -33,6 +36,26 @@ class Terminal(models.Model):
|
||||||
self.user.is_active = active
|
self.user.is_active = active
|
||||||
self.user.save()
|
self.user.save()
|
||||||
|
|
||||||
|
def get_common_storage(self):
|
||||||
|
storage_all = settings.TERMINAL_COMMAND_STORAGE
|
||||||
|
if self.command_storage in storage_all:
|
||||||
|
storage = storage_all.get(self.command_storage)
|
||||||
|
else:
|
||||||
|
storage = storage_all.get('default')
|
||||||
|
return {"TERMINAL_COMMAND_STORAGE": storage}
|
||||||
|
|
||||||
|
def get_replay_storage(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def config(self):
|
||||||
|
configs = {}
|
||||||
|
for k in dir(settings):
|
||||||
|
if k.startswith('TERMINAL'):
|
||||||
|
configs[k] = getattr(settings, k)
|
||||||
|
configs.update(self.get_common_storage())
|
||||||
|
return configs
|
||||||
|
|
||||||
def create_app_user(self):
|
def create_app_user(self):
|
||||||
random = uuid.uuid4().hex[:6]
|
random = uuid.uuid4().hex[:6]
|
||||||
user, access_key = User.create_app_user(name="{}-{}".format(self.name, random), comment=self.comment)
|
user, access_key = User.create_app_user(name="{}-{}".format(self.name, random), comment=self.comment)
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.utils import timezone
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from .models import Terminal, Status, Session, Task
|
from .models import Terminal, Status, Session, Task
|
||||||
from .backends import get_command_store
|
from .backends import get_multi_command_store
|
||||||
|
|
||||||
|
|
||||||
class TerminalSerializer(serializers.ModelSerializer):
|
class TerminalSerializer(serializers.ModelSerializer):
|
||||||
|
@ -43,14 +43,14 @@ class TerminalSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class SessionSerializer(serializers.ModelSerializer):
|
class SessionSerializer(serializers.ModelSerializer):
|
||||||
command_amount = serializers.SerializerMethodField()
|
command_amount = serializers.SerializerMethodField()
|
||||||
command_store = get_command_store()
|
command_store = get_multi_command_store()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Session
|
model = Session
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
def get_command_amount(self, obj):
|
def get_command_amount(self, obj):
|
||||||
return len(self.command_store.filter(session=obj.session))
|
return self.command_store.count(session=str(obj.id))
|
||||||
|
|
||||||
|
|
||||||
class StatusSerializer(serializers.ModelSerializer):
|
class StatusSerializer(serializers.ModelSerializer):
|
||||||
|
|
|
@ -13,10 +13,6 @@ RUNNING = False
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
|
||||||
@register_as_period_task(interval=3600)
|
|
||||||
@after_app_ready_start
|
|
||||||
@after_app_shutdown_clean
|
|
||||||
def set_session_info_cache():
|
def set_session_info_cache():
|
||||||
logger.debug("")
|
logger.debug("")
|
||||||
from .utils import get_session_asset_list, get_session_user_list, \
|
from .utils import get_session_asset_list, get_session_user_list, \
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
{% bootstrap_field form.remote_addr layout="horizontal" %}
|
{% bootstrap_field form.remote_addr layout="horizontal" %}
|
||||||
{% bootstrap_field form.ssh_port layout="horizontal" %}
|
{% bootstrap_field form.ssh_port layout="horizontal" %}
|
||||||
{% bootstrap_field form.http_port layout="horizontal" %}
|
{% bootstrap_field form.http_port layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.command_storage layout="horizontal" %}
|
||||||
{% bootstrap_field form.comment layout="horizontal" %}
|
{% bootstrap_field form.comment layout="horizontal" %}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
{% bootstrap_field form.remote_addr layout="horizontal" %}
|
{% bootstrap_field form.remote_addr layout="horizontal" %}
|
||||||
{% bootstrap_field form.ssh_port layout="horizontal" %}
|
{% bootstrap_field form.ssh_port layout="horizontal" %}
|
||||||
{% bootstrap_field form.http_port layout="horizontal" %}
|
{% bootstrap_field form.http_port layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.command_storage layout="horizontal" %}
|
||||||
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<h3>{% trans 'Other' %}</h3>
|
<h3>{% trans 'Other' %}</h3>
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from ..backends import get_command_store
|
from ..backends import get_multi_command_store
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
command_store = get_command_store()
|
command_store = get_multi_command_store()
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def get_session_command_amount(session_id):
|
def get_session_command_amount(session_id):
|
||||||
return len(command_store.filter(session=str(session_id)))
|
return command_store.count(session=session_id)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,8 @@ urlpatterns = [
|
||||||
url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$',
|
url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$',
|
||||||
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
||||||
name='session-replay'),
|
name='session-replay'),
|
||||||
url(r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key')
|
url(r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key'),
|
||||||
|
url(r'^v1/terminal/config', api.TerminalConfig.as_view(), name='terminal-config'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += router.urls
|
urlpatterns += router.urls
|
||||||
|
|
|
@ -9,10 +9,10 @@ from django.utils.translation import ugettext as _
|
||||||
from common.mixins import DatetimeSearchMixin
|
from common.mixins import DatetimeSearchMixin
|
||||||
from ..models import Command
|
from ..models import Command
|
||||||
from .. import utils
|
from .. import utils
|
||||||
from ..backends import get_command_store
|
from ..backends import get_multi_command_store
|
||||||
|
|
||||||
__all__ = ['CommandListView']
|
__all__ = ['CommandListView']
|
||||||
command_store = get_command_store()
|
common_storage = get_multi_command_store()
|
||||||
|
|
||||||
|
|
||||||
class CommandListView(DatetimeSearchMixin, ListView):
|
class CommandListView(DatetimeSearchMixin, ListView):
|
||||||
|
@ -39,7 +39,7 @@ class CommandListView(DatetimeSearchMixin, ListView):
|
||||||
filter_kwargs['system_user'] = self.system_user
|
filter_kwargs['system_user'] = self.system_user
|
||||||
if self.command:
|
if self.command:
|
||||||
filter_kwargs['input'] = self.command
|
filter_kwargs['input'] = self.command
|
||||||
queryset = command_store.filter(**filter_kwargs)
|
queryset = common_storage.filter(**filter_kwargs)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|
|
@ -10,7 +10,7 @@ from django.conf import settings
|
||||||
from users.utils import AdminUserRequiredMixin
|
from users.utils import AdminUserRequiredMixin
|
||||||
from common.mixins import DatetimeSearchMixin
|
from common.mixins import DatetimeSearchMixin
|
||||||
from ..models import Session, Command, Terminal
|
from ..models import Session, Command, Terminal
|
||||||
from ..backends import get_command_store
|
from ..backends import get_multi_command_store
|
||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ __all__ = [
|
||||||
'SessionDetailView',
|
'SessionDetailView',
|
||||||
]
|
]
|
||||||
|
|
||||||
command_store = get_command_store()
|
command_store = get_multi_command_store()
|
||||||
|
|
||||||
|
|
||||||
class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
|
class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
|
||||||
from .serializers import UserSerializer, UserGroupSerializer, \
|
from .serializers import UserSerializer, UserGroupSerializer, \
|
||||||
UserGroupUpdateMemeberSerializer, UserPKUpdateSerializer, \
|
UserGroupUpdateMemeberSerializer, UserPKUpdateSerializer, \
|
||||||
UserUpdateGroupSerializer
|
UserUpdateGroupSerializer, ChangeUserPasswordSerializer
|
||||||
from .tasks import write_login_log_async
|
from .tasks import write_login_log_async
|
||||||
from .models import User, UserGroup
|
from .models import User, UserGroup
|
||||||
from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly
|
from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly
|
||||||
|
@ -24,10 +24,21 @@ class UserViewSet(CustomFilterMixin, BulkModelViewSet):
|
||||||
queryset = User.objects.exclude(role="App")
|
queryset = User.objects.exclude(role="App")
|
||||||
# queryset = User.objects.all().exclude(role="App").order_by("date_joined")
|
# queryset = User.objects.all().exclude(role="App").order_by("date_joined")
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsSuperUser, IsAuthenticated)
|
||||||
filter_fields = ('username', 'email', 'name', 'id')
|
filter_fields = ('username', 'email', 'name', 'id')
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeUserPasswordApi(generics.RetrieveUpdateAPIView):
|
||||||
|
permission_classes = (IsSuperUser,)
|
||||||
|
queryset = User.objects.all()
|
||||||
|
serializer_class = ChangeUserPasswordSerializer
|
||||||
|
|
||||||
|
def perform_update(self, serializer):
|
||||||
|
user = self.get_object()
|
||||||
|
user.password_raw = serializer.validated_data["password"]
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
|
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = UserUpdateGroupSerializer
|
serializer_class = UserUpdateGroupSerializer
|
||||||
|
@ -37,6 +48,7 @@ class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
|
||||||
class UserResetPasswordApi(generics.UpdateAPIView):
|
class UserResetPasswordApi(generics.UpdateAPIView):
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
# Note: we are not updating the user object here.
|
# Note: we are not updating the user object here.
|
||||||
|
@ -128,7 +140,11 @@ class UserAuthApi(APIView):
|
||||||
user_agent = request.data.get('HTTP_USER_AGENT', '')
|
user_agent = request.data.get('HTTP_USER_AGENT', '')
|
||||||
|
|
||||||
if not login_ip:
|
if not login_ip:
|
||||||
login_ip = request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get("REMOTE_ADDR")
|
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
|
||||||
|
if x_forwarded_for:
|
||||||
|
login_ip = x_forwarded_for[0]
|
||||||
|
else:
|
||||||
|
login_ip = request.META.get("REMOTE_ADDR")
|
||||||
|
|
||||||
user, msg = check_user_valid(
|
user, msg = check_user_valid(
|
||||||
username=username, password=password,
|
username=username, password=password,
|
||||||
|
|
|
@ -172,7 +172,6 @@ class UserBulkUpdateForm(forms.ModelForm):
|
||||||
if self.data.get(field) is not None:
|
if self.data.get(field) is not None:
|
||||||
changed_fields.append(field)
|
changed_fields.append(field)
|
||||||
|
|
||||||
print(changed_fields)
|
|
||||||
cleaned_data = {k: v for k, v in self.cleaned_data.items()
|
cleaned_data = {k: v for k, v in self.cleaned_data.items()
|
||||||
if k in changed_fields}
|
if k in changed_fields}
|
||||||
users = cleaned_data.pop('users', '')
|
users = cleaned_data.pop('users', '')
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
Other module of this app shouldn't connect with other app.
|
Other module of this app shouldn't connect with other app.
|
||||||
|
|
||||||
:copyright: (c) 2014-2017 by Jumpserver Team.
|
:copyright: (c) 2014-2018 by Jumpserver Team.
|
||||||
:license: GPL v2, see LICENSE for more details.
|
:license: GPL v2, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -71,3 +71,9 @@ class UserGroupUpdateMemeberSerializer(serializers.ModelSerializer):
|
||||||
model = UserGroup
|
model = UserGroup
|
||||||
fields = ['id', 'users']
|
fields = ['id', 'users']
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeUserPasswordSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['password']
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
|
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h3>{% trans 'Account' %}</h3>
|
<h3>{% trans 'Account' %}</h3>
|
||||||
{% bootstrap_field form.username layout="horizontal" %}
|
|
||||||
{% bootstrap_field form.name layout="horizontal" %}
|
{% bootstrap_field form.name layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.username layout="horizontal" %}
|
||||||
{% bootstrap_field form.email layout="horizontal" %}
|
{% bootstrap_field form.email layout="horizontal" %}
|
||||||
{% bootstrap_field form.groups layout="horizontal" %}
|
{% bootstrap_field form.groups layout="horizontal" %}
|
||||||
|
|
||||||
|
@ -32,12 +32,6 @@
|
||||||
<span class="help-block ">{{ form.date_expired.errors }}</span>
|
<span class="help-block ">{{ form.date_expired.errors }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{# <div class="form-group">#}
|
|
||||||
{# <label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>#}
|
|
||||||
{# <div class="col-sm-8">#}
|
|
||||||
{# {{ form.enable_otp }}#}
|
|
||||||
{# </div>#}
|
|
||||||
{# </div>#}
|
|
||||||
<div class="hr-line-dashed"></div>
|
<div class="hr-line-dashed"></div>
|
||||||
<h3>{% trans 'Profile' %}</h3>
|
<h3>{% trans 'Profile' %}</h3>
|
||||||
{% bootstrap_field form.phone layout="horizontal" %}
|
{% bootstrap_field form.phone layout="horizontal" %}
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
Copyright Jumpserver.org
|
Copyright Jumpserver.org
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 text-right">
|
<div class="col-md-6 text-right">
|
||||||
<small>© 2014-2017</small>
|
<small>© 2014-2018</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
Copyright Jumpserver.org
|
Copyright Jumpserver.org
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 text-right">
|
<div class="col-md-6 text-right">
|
||||||
<small>© 2014-2017</small>
|
<small>© 2014-2018</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
Copyright Jumpserver.org
|
Copyright Jumpserver.org
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 text-right">
|
<div class="col-md-6 text-right">
|
||||||
<small>© 2014-2017</small>
|
<small>© 2014-2018</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,8 +24,9 @@
|
||||||
<li class="pull-right">
|
<li class="pull-right">
|
||||||
<a class="btn btn-outline btn-default" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
<a class="btn btn-outline btn-default" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="pull-right">
|
<li class="pull-right">
|
||||||
<a class="btn btn-outline btn-danger btn-delete-user">
|
<a class="btn btn-outline {% if request.user != user_object and user_object.username != "admin" %} btn-danger btn-delete-user {% else %} disabled {% endif %}">
|
||||||
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
|
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -128,7 +129,7 @@
|
||||||
<td><span class="pull-right">
|
<td><span class="pull-right">
|
||||||
<div class="switch">
|
<div class="switch">
|
||||||
<div class="onoffswitch">
|
<div class="onoffswitch">
|
||||||
<input type="checkbox" {% if user_object.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
|
<input type="checkbox" {% if user_object.is_active %} checked {% endif %} {% if request.user == user_object %} disabled {% endif %} class="onoffswitch-checkbox" id="is_active">
|
||||||
<label class="onoffswitch-label" for="is_active">
|
<label class="onoffswitch-label" for="is_active">
|
||||||
<span class="onoffswitch-inner"></span>
|
<span class="onoffswitch-inner"></span>
|
||||||
<span class="onoffswitch-switch"></span>
|
<span class="onoffswitch-switch"></span>
|
||||||
|
@ -156,7 +157,7 @@
|
||||||
<td>{% trans 'Send reset password mail' %}:</td>
|
<td>{% trans 'Send reset password mail' %}:</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="pull-right">
|
<span class="pull-right">
|
||||||
<button type="button" class="btn btn-primary btn-xs" id="btn-reset-password" style="width: 54px">{% trans 'Send' %}</button>
|
<button type="button" class="btn btn-primary btn-xs" {% if request.user == user_object %} disabled="disabled" {% endif %} id="btn-reset-password" style="width: 54px">{% trans 'Send' %}</button>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -164,7 +165,7 @@
|
||||||
<td>{% trans 'Send reset ssh key mail' %}:</td>
|
<td>{% trans 'Send reset ssh key mail' %}:</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="pull-right">
|
<span class="pull-right">
|
||||||
<button type="button" class="btn btn-primary btn-xs" id="btn-reset-pk" style="width: 54px;">{% trans 'Send' %}</button>
|
<button type="button" class="btn btn-primary btn-xs" {% if request.user == user_object %} disabled="disabled" {% endif %} id="btn-reset-pk" style="width: 54px;">{% trans 'Send' %}</button>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -331,7 +332,7 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
swal({
|
swal({
|
||||||
title: "{% trans 'Are you sure?' %}",
|
title: "{% trans 'Are you sure?' %}",
|
||||||
text: "{% trans "This will reset the user's password. A password-reset email will be sent to the user\'s mailbox." %}",
|
text: "{% trans "This will reset the user password and send a reset mail"%}",
|
||||||
type: "warning",
|
type: "warning",
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonColor: "#DD6B55",
|
confirmButtonColor: "#DD6B55",
|
||||||
|
@ -356,7 +357,7 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
swal({
|
swal({
|
||||||
title: "{% trans 'Are you sure?' %}",
|
title: "{% trans 'Are you sure?' %}",
|
||||||
text: "{% trans 'This will reset the user\'s public key.' %}",
|
text: "{% trans 'This will reset the user public key and send a reset mail' %}",
|
||||||
type: "warning",
|
type: "warning",
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
confirmButtonColor: "#DD6B55",
|
confirmButtonColor: "#DD6B55",
|
||||||
|
|
|
@ -76,7 +76,7 @@ function initTable() {
|
||||||
var update_btn = '<a href="{% url "users:user-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('00000000-0000-0000-0000-000000000000', cellData);
|
var update_btn = '<a href="{% url "users:user-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('00000000-0000-0000-0000-000000000000', cellData);
|
||||||
|
|
||||||
var del_btn = "";
|
var del_btn = "";
|
||||||
if (rowData.id === 1 || rowData.username === "admin" || rowData.username === "{{ user.username }}") {
|
if (rowData.id === 1 || rowData.username === "admin" || rowData.username === "{{ request.user.username }}") {
|
||||||
del_btn = '<a class="btn btn-xs btn-danger m-l-xs" disabled>{% trans "Delete" %}</a>'
|
del_btn = '<a class="btn btn-xs btn-danger m-l-xs" disabled>{% trans "Delete" %}</a>'
|
||||||
.replace('{{ DEFAULT_PK }}', cellData)
|
.replace('{{ DEFAULT_PK }}', cellData)
|
||||||
.replace('99991938', rowData.name);
|
.replace('99991938', rowData.name);
|
||||||
|
|
|
@ -19,6 +19,8 @@ urlpatterns = [
|
||||||
url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'),
|
url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'),
|
||||||
url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'),
|
url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'),
|
||||||
url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'),
|
url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'),
|
||||||
|
url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/password/$',
|
||||||
|
api.ChangeUserPasswordApi.as_view(), name='change-user-password'),
|
||||||
url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/password/reset/$',
|
url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/password/reset/$',
|
||||||
api.UserResetPasswordApi.as_view(), name='user-reset-password'),
|
api.UserResetPasswordApi.as_view(), name='user-reset-password'),
|
||||||
url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/pubkey/reset/$',
|
url(r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/pubkey/reset/$',
|
||||||
|
|
|
@ -180,8 +180,10 @@ def validate_ip(ip):
|
||||||
|
|
||||||
def write_login_log(username, type='', ip='', user_agent=''):
|
def write_login_log(username, type='', ip='', user_agent=''):
|
||||||
if not (ip and validate_ip(ip)):
|
if not (ip and validate_ip(ip)):
|
||||||
ip = '0.0.0.0'
|
ip = ip[:15]
|
||||||
city = get_ip_city(ip)
|
city = "Unknown"
|
||||||
|
else:
|
||||||
|
city = get_ip_city(ip)
|
||||||
LoginLog.objects.create(
|
LoginLog.objects.create(
|
||||||
username=username, type=type,
|
username=username, type=type,
|
||||||
ip=ip, city=city, user_agent=user_agent
|
ip=ip, city=city, user_agent=user_agent
|
||||||
|
|
|
@ -53,8 +53,11 @@ class UserLoginView(FormView):
|
||||||
if not self.request.session.test_cookie_worked():
|
if not self.request.session.test_cookie_worked():
|
||||||
return HttpResponse(_("Please enable cookies and try again."))
|
return HttpResponse(_("Please enable cookies and try again."))
|
||||||
auth_login(self.request, form.get_user())
|
auth_login(self.request, form.get_user())
|
||||||
login_ip = self.request.META.get('HTTP_X_FORWARDED_FOR') or \
|
x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
|
||||||
self.request.META.get('REMOTE_ADDR', '')
|
if x_forwarded_for and x_forwarded_for[0]:
|
||||||
|
login_ip = x_forwarded_for[0]
|
||||||
|
else:
|
||||||
|
login_ip = self.request.META.get('REMOTE_ADDR', '')
|
||||||
user_agent = self.request.META.get('HTTP_USER_AGENT', '')
|
user_agent = self.request.META.get('HTTP_USER_AGENT', '')
|
||||||
write_login_log_async.delay(
|
write_login_log_async.delay(
|
||||||
self.request.user.username, type='W',
|
self.request.user.username, type='W',
|
||||||
|
|
130
docs/install.md
130
docs/install.md
|
@ -1,129 +1 @@
|
||||||
## Jumpserver v0.4.0 版本安装详细过程
|
More see [安装文档](https://github.com/jumpserver/jumpserver/wiki/v0.5.0-%E5%9F%BA%E4%BA%8E-CentOS7)
|
||||||
|
|
||||||
### 环境
|
|
||||||
|
|
||||||
- 系统: CentOS 6.5 x86\_64 mini
|
|
||||||
- Python: 版本 3.6 大部分功能兼容 2.7
|
|
||||||
- 安装目录
|
|
||||||
- /opt/jumpserver
|
|
||||||
- /opt/coco
|
|
||||||
|
|
||||||
#### 一. 环境准备
|
|
||||||
|
|
||||||
##### 1.1 安装基本工具和库
|
|
||||||
|
|
||||||
$ yum -y install sqlite-devel git epel-release
|
|
||||||
$ yum -y install sshpass python-devel libffi-devel openssl-devel
|
|
||||||
$ yum -y install gcc gcc-c++
|
|
||||||
|
|
||||||
|
|
||||||
##### 1.2 安装Python 3.6 和 虚拟环境
|
|
||||||
略
|
|
||||||
|
|
||||||
|
|
||||||
#### 二. Jumpserver安装
|
|
||||||
|
|
||||||
##### 2.1 下载仓库代码
|
|
||||||
|
|
||||||
$ cd /opt
|
|
||||||
$ git clone https://github.com/jumpserver/jumpserver.git
|
|
||||||
$ cd jumpserver
|
|
||||||
$ git checkout dev
|
|
||||||
|
|
||||||
##### 2.2 安装依赖
|
|
||||||
|
|
||||||
$ cd requirements
|
|
||||||
$ sudo yum -y install `cat rpm_requirements.txt`
|
|
||||||
$ pip install -r requirements.txt -i https://pypi.doubanio.com/simple
|
|
||||||
|
|
||||||
// 解决Mac安装ldap提示 Modules/LDAPObject.c:18:10: fatal error: 'sasl.h' file not found
|
|
||||||
pip install python-ldap \
|
|
||||||
--global-option=build_ext \
|
|
||||||
--global-option="-I$(xcrun --show-sdk-path)/usr/include/sasl"
|
|
||||||
|
|
||||||
##### 2.3 准备配置文件
|
|
||||||
|
|
||||||
$ cd ..
|
|
||||||
$ cp config_example.py config.py
|
|
||||||
$ vim config.py
|
|
||||||
|
|
||||||
// 默认使用的是 DevelpmentConfig 所以应该去修改这部分
|
|
||||||
class DevelopmentConfig(Config):
|
|
||||||
EMAIL_HOST = 'smtp.exmail.qq.com'
|
|
||||||
EMAIL_PORT = 465
|
|
||||||
EMAIL_HOST_USER = 'ask@jumpserver.org'
|
|
||||||
EMAIL_HOST_PASSWORD = 'xxx'
|
|
||||||
EMAIL_USE_SSL = True // 端口是 465 设置 True 否则 False
|
|
||||||
EMAIL_USE_TLS = False // 端口是 587 设置为 True 否则 False
|
|
||||||
SITE_URL = 'http://localhost:8080' // 发送邮件会使用这个地址
|
|
||||||
|
|
||||||
##### 2.4 初始化数据库
|
|
||||||
|
|
||||||
$ cd utils
|
|
||||||
$ sh make_migrations.sh
|
|
||||||
$ sh init_db.sh
|
|
||||||
|
|
||||||
##### 2.5 安装redis server
|
|
||||||
|
|
||||||
$ yum -y install redis
|
|
||||||
$ service redis start
|
|
||||||
|
|
||||||
**2.6 启动**
|
|
||||||
```
|
|
||||||
$ cd ..
|
|
||||||
$ python run_server.py
|
|
||||||
```
|
|
||||||
访问 http://ip:8080
|
|
||||||
账号密码: admin admin
|
|
||||||
|
|
||||||
**2.7 测试使用**
|
|
||||||
- 创建用户
|
|
||||||
会发送邮件,测试是否正常修改密码,登录
|
|
||||||
|
|
||||||
- 创建管理用户
|
|
||||||
创建一个管理用户, 创建资产时需要关联
|
|
||||||
|
|
||||||
- 创建资产
|
|
||||||
创建一个 资产,关联刚创建的管理用户
|
|
||||||
|
|
||||||
- 创建系统用户
|
|
||||||
系统用户是用来登录资产的,授权时需要
|
|
||||||
|
|
||||||
- 创建授权规则
|
|
||||||
关联用户,资产,系统用户 形成授权规则,授权的系统用户会自动推送到资产上
|
|
||||||
|
|
||||||
|
|
||||||
#### 三. 安装 SSH SERVER - COCO
|
|
||||||
**3.1 下载代码库**
|
|
||||||
```
|
|
||||||
$ cd /opt
|
|
||||||
$ git clone https://github.com/jumpserver/coco.git
|
|
||||||
```
|
|
||||||
|
|
||||||
**3.2 安装依赖**
|
|
||||||
```
|
|
||||||
$ cd coco
|
|
||||||
$ pip install -r requirements.txt # -i https://pypi.doubanio.com/simple
|
|
||||||
```
|
|
||||||
|
|
||||||
**3.3 启动**
|
|
||||||
|
|
||||||
```
|
|
||||||
$ python run_server.py
|
|
||||||
```
|
|
||||||
|
|
||||||
说明: Coco启动后会向jumpserver注册,请去 jumpserver页面 - 应用程序 - terminal - coco - Accept 允许, 这时 coco就 运行在 2222端口,可以ssh来连接
|
|
||||||
|
|
||||||
命令行:
|
|
||||||
```
|
|
||||||
ssh admin@YourServerIP -p2222
|
|
||||||
```
|
|
||||||
|
|
||||||
**3.5 测试**
|
|
||||||
- 测试登录 ssh server
|
|
||||||
- 测试跳转
|
|
||||||
- 测试命令记录回
|
|
||||||
|
|
||||||
[1]: https://segmentfault.com/a/1190000000654227
|
|
||||||
[2]: https://github.com/jumpserver/jumpserver.git
|
|
||||||
[3]: https://github.com/jumpserver/coco.git
|
|
||||||
|
|
|
@ -56,6 +56,8 @@ uritemplate==3.0.0
|
||||||
urllib3==1.22
|
urllib3==1.22
|
||||||
vine==1.1.4
|
vine==1.1.4
|
||||||
gunicorn==19.7.1
|
gunicorn==19.7.1
|
||||||
django_celery_beat==1.1.0
|
https://github.com/celery/django-celery-beat/zipball/master#egg=django-celery-beat
|
||||||
|
#django_celery_beat==1.1.0
|
||||||
ephem==3.7.6.0
|
ephem==3.7.6.0
|
||||||
python-gssapi==0.6.4
|
python-gssapi==0.6.4
|
||||||
|
jms-es-sdk
|
||||||
|
|
Loading…
Reference in New Issue