mirror of https://github.com/jumpserver/jumpserver
Merge branch 'es' into dev
commit
968b395bce
|
@ -60,11 +60,6 @@ class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView)
|
|||
success_url = reverse_lazy('assets:cluster-list')
|
||||
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):
|
||||
context = {
|
||||
'app': _('assets'),
|
||||
|
|
|
@ -63,8 +63,6 @@ class LDAPTestingAPI(APIView):
|
|||
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
|
||||
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
|
||||
|
||||
print(serializer.validated_data)
|
||||
|
||||
try:
|
||||
attr_map = json.loads(attr_map)
|
||||
except json.JSONDecodeError:
|
||||
|
@ -77,9 +75,6 @@ class LDAPTestingAPI(APIView):
|
|||
except Exception as e:
|
||||
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": "*"}),
|
||||
attributes=list(attr_map.values()))
|
||||
if not ok:
|
||||
|
|
|
@ -18,7 +18,6 @@ class DictField(forms.Field):
|
|||
# we don't need to handle that explicitly.
|
||||
if isinstance(value, six.string_types):
|
||||
try:
|
||||
print(value)
|
||||
value = json.loads(value)
|
||||
return value
|
||||
except json.JSONDecodeError:
|
||||
|
|
|
@ -4,7 +4,9 @@ import json
|
|||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.html import escape
|
||||
from django.db import transaction
|
||||
from django.conf import settings
|
||||
|
||||
from .models import Setting
|
||||
from .fields import DictField
|
||||
|
@ -30,28 +32,32 @@ def to_form_value(value):
|
|||
class BaseForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
settings = Setting.objects.all()
|
||||
db_settings = Setting.objects.all()
|
||||
for name, field in self.fields.items():
|
||||
db_value = getattr(settings, name).value
|
||||
if db_value:
|
||||
db_value = getattr(db_settings, name).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)
|
||||
elif django_value is False or django_value:
|
||||
field.initial = django_value
|
||||
|
||||
def save(self):
|
||||
def save(self, category="default"):
|
||||
if not self.is_bound:
|
||||
raise ValueError("Form is not bound")
|
||||
|
||||
settings = Setting.objects.all()
|
||||
db_settings = Setting.objects.all()
|
||||
if self.is_valid():
|
||||
with transaction.atomic():
|
||||
for name, value in self.cleaned_data.items():
|
||||
field = self.fields[name]
|
||||
if isinstance(field.widget, forms.PasswordInput) and not value:
|
||||
continue
|
||||
if value == to_form_value(getattr(settings, name).value):
|
||||
if value == to_form_value(getattr(db_settings, name).value):
|
||||
continue
|
||||
|
||||
defaults = {
|
||||
'name': name,
|
||||
'category': category,
|
||||
'value': to_model_value(value)
|
||||
}
|
||||
Setting.objects.update_or_create(defaults=defaults, name=name)
|
||||
|
@ -126,6 +132,28 @@ class LDAPSettingForm(BaseForm):
|
|||
AUTH_LDAP_START_TLS = forms.BooleanField(
|
||||
label=_("Use SSL"), initial=False, required=False
|
||||
)
|
||||
AUTH_LDAP = forms.BooleanField(
|
||||
label=_("Enable LDAP Auth"), 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
|
||||
from django.db import models
|
||||
from django.db.utils import ProgrammingError, OperationalError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django_auth_ldap.config import LDAPSearch
|
||||
|
@ -24,6 +25,7 @@ class SettingManager(models.Manager):
|
|||
class Setting(models.Model):
|
||||
name = models.CharField(max_length=128, unique=True, verbose_name=_("Name"))
|
||||
value = models.TextField(verbose_name=_("Value"))
|
||||
category = models.CharField(max_length=128, default="default")
|
||||
enabled = models.BooleanField(verbose_name=_("Enabled"), default=True)
|
||||
comment = models.TextField(verbose_name=_("Comment"))
|
||||
|
||||
|
@ -33,17 +35,28 @@ class Setting(models.Model):
|
|||
return self.name
|
||||
|
||||
@property
|
||||
def value_(self):
|
||||
def cleaned_value(self):
|
||||
try:
|
||||
return json.loads(self.value)
|
||||
except json.JSONDecodeError:
|
||||
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
|
||||
def refresh_all_settings(cls):
|
||||
settings_list = cls.objects.all()
|
||||
for setting in settings_list:
|
||||
setting.refresh_setting()
|
||||
try:
|
||||
settings_list = cls.objects.all()
|
||||
for setting in settings_list:
|
||||
setting.refresh_setting()
|
||||
except (ProgrammingError, OperationalError):
|
||||
pass
|
||||
|
||||
def refresh_setting(self):
|
||||
try:
|
||||
|
@ -53,9 +66,9 @@ class Setting(models.Model):
|
|||
setattr(settings, self.name, value)
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
if self.name == "AUTH_LDAP_SEARCH_FILTER":
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
{% extends '_modal.html' %}
|
||||
{% load i18n %}
|
||||
{% block modal_id %}add_command_storage_model{% endblock %}
|
||||
{% block modal_title%}{% trans "Add command storage" %}{% endblock %}
|
||||
{% block modal_body %}
|
||||
<form method="post" action="" id="add_command_storage_form">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="id_assets">{% trans "Template" %}</label>
|
||||
<a href="{% url 'assets:asset-export' %}" style="display: block">{% trans 'Download' %}</a>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="id_users">{% trans "Asset csv file" %}</label>
|
||||
<input id="id_assets" type="file" name="file" />
|
||||
<span class="help-block red-fonts">
|
||||
{% trans 'If set id, will use this id update asset existed' %}
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% block modal_confirm_id %}btn_asset_import{% endblock %}
|
|
@ -20,6 +20,9 @@
|
|||
<li>
|
||||
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
||||
</li>
|
||||
<li>
|
||||
<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">
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
<li>
|
||||
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
||||
</li>
|
||||
<li>
|
||||
<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">
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
<li class="active">
|
||||
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
|
||||
</li>
|
||||
<li>
|
||||
<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">
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
{% 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>
|
||||
{% include 'common/_add_terminal_command_storage_modal.html' %}
|
||||
{% 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'^email/$', views.EmailSettingView.as_view(), name='email-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.contrib import messages
|
||||
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 .signals import ldap_auth_enable
|
||||
|
||||
|
@ -86,3 +89,31 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
|
|||
context = self.get_context_data()
|
||||
context.update({"form": form})
|
||||
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)
|
||||
|
|
|
@ -373,7 +373,20 @@ CAPTCHA_FOREGROUND_COLOR = '#001100'
|
|||
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)
|
||||
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
|
||||
BOOTSTRAP3 = {
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Jumpserver 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-01-17 17:26+0800\n"
|
||||
"POT-Creation-Date: 2018-01-22 11:04+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
|
||||
|
@ -18,7 +18,7 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: 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"
|
||||
msgstr "选择资产组"
|
||||
|
||||
|
@ -44,7 +44,7 @@ msgstr "默认使用管理用户"
|
|||
|
||||
#: assets/forms.py:76 assets/forms.py:81 assets/forms.py:127
|
||||
#: 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"
|
||||
msgstr "选择资产"
|
||||
|
||||
|
@ -66,7 +66,7 @@ msgstr "端口"
|
|||
#: 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_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:124
|
||||
#: terminal/templates/terminal/command_list.html:40
|
||||
#: terminal/templates/terminal/command_list.html:73
|
||||
#: terminal/templates/terminal/session_list.html:41
|
||||
|
@ -77,7 +77,7 @@ msgid "Asset"
|
|||
msgstr "资产"
|
||||
|
||||
#: 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"
|
||||
msgstr "选择系统用户"
|
||||
|
||||
|
@ -99,14 +99,15 @@ msgstr "选择的系统用户将会在该集群资产上创建"
|
|||
#: assets/templates/assets/cluster_detail.html:57
|
||||
#: assets/templates/assets/cluster_list.html:19
|
||||
#: assets/templates/assets/system_user_detail.html:58
|
||||
#: assets/templates/assets/system_user_list.html:24 common/models.py:25
|
||||
#: ops/models.py:31 ops/templates/ops/task_detail.html:56
|
||||
#: ops/templates/ops/task_list.html:34 perms/models.py:14
|
||||
#: assets/templates/assets/system_user_list.html:24 common/models.py:26
|
||||
#: common/templates/common/terminal_setting.html:62 ops/models.py:31
|
||||
#: 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_detail.html:62
|
||||
#: perms/templates/perms/asset_permission_list.html:25
|
||||
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:14
|
||||
#: terminal/models.py:118 terminal/templates/terminal/terminal_detail.html:43
|
||||
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:23
|
||||
#: terminal/models.py:149 terminal/templates/terminal/terminal_detail.html:43
|
||||
#: 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/templates/users/user_detail.html:62
|
||||
|
@ -126,10 +127,10 @@ msgstr "集群级别管理用户"
|
|||
|
||||
#: assets/forms.py:200
|
||||
msgid "Password or private key password"
|
||||
msgstr "密码或秘钥不合法"
|
||||
msgstr "密码或秘钥密码"
|
||||
|
||||
#: assets/forms.py:201 assets/forms.py:262 assets/models/user.py:30
|
||||
#: common/forms.py:107 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/reset_password.html:52
|
||||
#: users/templates/users/user_create.html:11
|
||||
|
@ -239,7 +240,7 @@ msgstr "测试环境"
|
|||
#: assets/templates/assets/asset_list.html:31
|
||||
#: assets/templates/assets/cluster_assets.html:52
|
||||
#: 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
|
||||
#: users/templates/users/login_log_list.html:52
|
||||
#: users/templates/users/user_granted_asset.html:49
|
||||
|
@ -253,7 +254,7 @@ msgstr "IP"
|
|||
#: assets/templates/assets/asset_list.html:30
|
||||
#: assets/templates/assets/cluster_assets.html:51
|
||||
#: 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
|
||||
#: users/templates/users/user_granted_asset.html:48
|
||||
#: 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/cluster_detail.html:97
|
||||
#: 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
|
||||
#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:22
|
||||
#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:33
|
||||
#: 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/templates/users/user_group_detail.html:67
|
||||
|
@ -494,7 +495,7 @@ msgstr "Shell"
|
|||
#: assets/models/user.py:269 perms/models.py:19
|
||||
#: perms/templates/perms/asset_permission_detail.html:136
|
||||
#: 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:125
|
||||
#: terminal/templates/terminal/command_list.html:48
|
||||
#: terminal/templates/terminal/command_list.html:74
|
||||
#: 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/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/cluster.py:26 assets/views/cluster.py:85
|
||||
#: assets/views/cluster.py:102 assets/views/group.py:34
|
||||
#: assets/views/group.py:52 assets/views/group.py:69 assets/views/group.py:87
|
||||
#: assets/views/cluster.py:26 assets/views/cluster.py:80
|
||||
#: assets/views/cluster.py:97 assets/views/group.py:34 assets/views/group.py:52
|
||||
#: 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:60 assets/views/system_user.py:75
|
||||
#: templates/_nav.html:19
|
||||
|
@ -602,20 +603,24 @@ msgid "Import asset"
|
|||
msgstr "导入资产"
|
||||
|
||||
#: assets/templates/assets/_asset_import_modal.html:9
|
||||
#: common/templates/common/_add_terminal_command_storage_modal.html:9
|
||||
#: users/templates/users/_user_import_modal.html:10
|
||||
msgid "Template"
|
||||
msgstr "模板"
|
||||
|
||||
#: assets/templates/assets/_asset_import_modal.html:10
|
||||
#: common/templates/common/_add_terminal_command_storage_modal.html:10
|
||||
#: users/templates/users/_user_import_modal.html:11
|
||||
msgid "Download"
|
||||
msgstr "下载"
|
||||
|
||||
#: assets/templates/assets/_asset_import_modal.html:13
|
||||
#: common/templates/common/_add_terminal_command_storage_modal.html:13
|
||||
msgid "Asset csv file"
|
||||
msgstr "资产csv文件"
|
||||
|
||||
#: assets/templates/assets/_asset_import_modal.html:16
|
||||
#: common/templates/common/_add_terminal_command_storage_modal.html:16
|
||||
msgid "If set id, will use this id update asset existed"
|
||||
msgstr "如果设置了id,则会使用该行信息更新该id的资产"
|
||||
|
||||
|
@ -650,7 +655,7 @@ msgstr "自动生成秘钥"
|
|||
#: assets/templates/assets/asset_update.html:47
|
||||
#: assets/templates/assets/cluster_create_update.html:46
|
||||
#: 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"
|
||||
msgstr "其它"
|
||||
|
||||
|
@ -661,11 +666,12 @@ msgstr "其它"
|
|||
#: assets/templates/assets/asset_group_create.html:16
|
||||
#: assets/templates/assets/asset_update.html:55
|
||||
#: assets/templates/assets/cluster_create_update.html:54
|
||||
#: common/templates/common/basic_setting.html:55
|
||||
#: common/templates/common/email_setting.html:56
|
||||
#: common/templates/common/ldap_setting.html:56
|
||||
#: common/templates/common/basic_setting.html:58
|
||||
#: common/templates/common/email_setting.html:59
|
||||
#: common/templates/common/ldap_setting.html:59
|
||||
#: common/templates/common/terminal_setting.html:82
|
||||
#: 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_bulk_update.html:23
|
||||
#: users/templates/users/user_password_update.html:58
|
||||
|
@ -684,11 +690,12 @@ msgstr "重置"
|
|||
#: assets/templates/assets/asset_list.html:53
|
||||
#: assets/templates/assets/asset_update.html:56
|
||||
#: assets/templates/assets/cluster_create_update.html:55
|
||||
#: common/templates/common/basic_setting.html:56
|
||||
#: common/templates/common/email_setting.html:57
|
||||
#: common/templates/common/ldap_setting.html:57
|
||||
#: common/templates/common/basic_setting.html:59
|
||||
#: common/templates/common/email_setting.html:60
|
||||
#: common/templates/common/ldap_setting.html:60
|
||||
#: common/templates/common/terminal_setting.html:83
|
||||
#: 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/first_login.html:62
|
||||
#: users/templates/users/forgot_password.html:44
|
||||
|
@ -778,6 +785,7 @@ msgstr "资产列表"
|
|||
#: assets/templates/assets/asset_group_detail.html:53
|
||||
#: assets/templates/assets/cluster_assets.html:54
|
||||
#: assets/templates/assets/user_asset_list.html:22
|
||||
#: common/templates/common/terminal_setting.html:63
|
||||
#: users/templates/users/login_log_list.html:50
|
||||
msgid "Type"
|
||||
msgstr "类型"
|
||||
|
@ -878,7 +886,7 @@ msgid "Group"
|
|||
msgstr "组"
|
||||
|
||||
#: 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"
|
||||
msgstr "资产详情"
|
||||
|
||||
|
@ -1236,16 +1244,16 @@ msgstr "已经存在"
|
|||
msgid "Cluster list"
|
||||
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
|
||||
msgid "assets"
|
||||
msgstr "资产管理"
|
||||
|
||||
#: assets/views/cluster.py:71
|
||||
#: assets/views/cluster.py:66
|
||||
msgid "Update Cluster"
|
||||
msgstr "更新Cluster"
|
||||
|
||||
#: assets/views/cluster.py:86
|
||||
#: assets/views/cluster.py:81
|
||||
msgid "Cluster detail"
|
||||
msgstr "集群详情"
|
||||
|
||||
|
@ -1291,81 +1299,108 @@ msgstr "<b>%(name)s</b> 创建成功"
|
|||
msgid "<b>%(name)s</b> was updated successfully"
|
||||
msgstr "<b>%(name)s</b> 更新成功"
|
||||
|
||||
#: common/forms.py:64
|
||||
#: common/forms.py:70
|
||||
msgid "Current SITE URL"
|
||||
msgstr "当前站点URL"
|
||||
|
||||
#: common/forms.py:68
|
||||
#: common/forms.py:74
|
||||
msgid "User Guide URL"
|
||||
msgstr "用户向导URL"
|
||||
|
||||
#: common/forms.py:69
|
||||
#: common/forms.py:75
|
||||
msgid "User first login update profile done redirect to it"
|
||||
msgstr "用户第一次登录,修改profile后重定向到地址"
|
||||
|
||||
#: common/forms.py:72
|
||||
#: common/forms.py:78
|
||||
msgid "Email Subject Prefix"
|
||||
msgstr "Email主题前缀"
|
||||
|
||||
#: common/forms.py:79
|
||||
#: common/forms.py:85
|
||||
msgid "SMTP host"
|
||||
msgstr "SMTP主机"
|
||||
|
||||
#: common/forms.py:81
|
||||
#: common/forms.py:87
|
||||
msgid "SMTP port"
|
||||
msgstr "SMTP端口"
|
||||
|
||||
#: common/forms.py:83
|
||||
#: common/forms.py:89
|
||||
msgid "SMTP user"
|
||||
msgstr "SMTP账号"
|
||||
|
||||
#: common/forms.py:86
|
||||
#: common/forms.py:92
|
||||
msgid "SMTP password"
|
||||
msgstr "SMTP密码"
|
||||
|
||||
#: common/forms.py:87
|
||||
#: common/forms.py:93
|
||||
msgid "Some provider use token except password"
|
||||
msgstr "一些邮件提供商需要输入的是Token"
|
||||
|
||||
#: common/forms.py:90 common/forms.py:127
|
||||
#: common/forms.py:96 common/forms.py:133
|
||||
msgid "Use SSL"
|
||||
msgstr "使用SSL"
|
||||
|
||||
#: common/forms.py:91
|
||||
#: common/forms.py:97
|
||||
msgid "If SMTP port is 465, may be select"
|
||||
msgstr "如果SMTP端口是465,通常需要启用SSL"
|
||||
|
||||
#: common/forms.py:94
|
||||
#: common/forms.py:100
|
||||
msgid "Use TLS"
|
||||
msgstr "使用TLS"
|
||||
|
||||
#: common/forms.py:95
|
||||
#: common/forms.py:101
|
||||
msgid "If SMTP port is 587, may be select"
|
||||
msgstr "如果SMTP端口是587,通常需要启用TLS"
|
||||
|
||||
#: common/forms.py:101
|
||||
#: common/forms.py:107
|
||||
msgid "LDAP server"
|
||||
msgstr "LDAP地址"
|
||||
|
||||
#: common/forms.py:104
|
||||
#: common/forms.py:110
|
||||
msgid "Bind DN"
|
||||
msgstr "绑定DN"
|
||||
|
||||
#: common/forms.py:111
|
||||
#: common/forms.py:117
|
||||
msgid "User OU"
|
||||
msgstr "用户OU"
|
||||
|
||||
#: common/forms.py:114
|
||||
#: common/forms.py:120
|
||||
msgid "User search filter"
|
||||
msgstr "用户过滤器"
|
||||
|
||||
#: common/forms.py:117
|
||||
#: common/forms.py:123
|
||||
msgid "User attr map"
|
||||
msgstr "LDAP属性映射"
|
||||
|
||||
#: common/forms.py:130
|
||||
msgid "Enable LDAP Auth"
|
||||
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/models.py:27
|
||||
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
|
||||
msgid "is discard"
|
||||
|
@ -1375,43 +1410,62 @@ msgstr ""
|
|||
msgid "discard time"
|
||||
msgstr ""
|
||||
|
||||
#: common/models.py:26
|
||||
#: common/models.py:27
|
||||
msgid "Value"
|
||||
msgstr "值"
|
||||
|
||||
#: common/models.py:27
|
||||
#: common/models.py:29
|
||||
msgid "Enabled"
|
||||
msgstr "启用"
|
||||
|
||||
#: common/templates/common/_add_terminal_command_storage_modal.html:4
|
||||
msgid "Add command storage"
|
||||
msgstr "添加命令存储"
|
||||
|
||||
#: common/templates/common/basic_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"
|
||||
msgstr "基本设置"
|
||||
|
||||
#: common/templates/common/basic_setting.html:18
|
||||
#: common/templates/common/email_setting.html:18
|
||||
#: common/templates/common/ldap_setting.html:18 common/views.py:44
|
||||
#: common/templates/common/ldap_setting.html:18
|
||||
#: common/templates/common/terminal_setting.html:18 common/views.py:47
|
||||
msgid "Email setting"
|
||||
msgstr "邮件设置"
|
||||
|
||||
#: common/templates/common/basic_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"
|
||||
msgstr "LDAP设置"
|
||||
|
||||
#: common/templates/common/email_setting.html:55
|
||||
#: common/templates/common/ldap_setting.html:55
|
||||
#: common/templates/common/basic_setting.html:24
|
||||
#: common/templates/common/email_setting.html:24
|
||||
#: 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"
|
||||
msgstr "测试连接"
|
||||
|
||||
#: common/views.py:17 common/views.py:43 common/views.py:69
|
||||
#: common/templates/common/terminal_setting.html:77 terminal/models.py:28
|
||||
msgid "Replay storage"
|
||||
msgstr "录像存储"
|
||||
|
||||
#: common/views.py:20 common/views.py:46 common/views.py:72 common/views.py:101
|
||||
#: templates/_nav.html:69
|
||||
msgid "Settings"
|
||||
msgstr "系统设置"
|
||||
|
||||
#: common/views.py:28 common/views.py:54 common/views.py:82
|
||||
#: common/views.py:31 common/views.py:57 common/views.py:85 common/views.py:113
|
||||
msgid "Update setting successfully, please restart program"
|
||||
msgstr "更新设置成功, 请手动重启程序"
|
||||
|
||||
|
@ -1419,10 +1473,6 @@ msgstr "更新设置成功, 请手动重启程序"
|
|||
msgid "Interval"
|
||||
msgstr "间隔"
|
||||
|
||||
#: ops/models.py:32
|
||||
msgid "Units: seconds"
|
||||
msgstr "单位: 秒"
|
||||
|
||||
#: ops/models.py:33
|
||||
msgid "Crontab"
|
||||
msgstr "Crontab"
|
||||
|
@ -1566,7 +1616,7 @@ msgstr "执行历史"
|
|||
|
||||
#: ops/templates/ops/adhoc_history.html:52
|
||||
#: 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:132
|
||||
#: terminal/templates/terminal/session_list.html:77
|
||||
msgid "Date start"
|
||||
msgstr "开始日期"
|
||||
|
@ -1683,18 +1733,18 @@ msgid "Task run history"
|
|||
msgstr "执行历史"
|
||||
|
||||
#: 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"
|
||||
msgstr "选择用户"
|
||||
|
||||
#: perms/forms.py:18 perms/models.py:15
|
||||
#: perms/templates/perms/asset_permission_create_update.html:36
|
||||
#: 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:123
|
||||
#: terminal/templates/terminal/command_list.html:32
|
||||
#: terminal/templates/terminal/command_list.html:72
|
||||
#: 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/views/user.py:337
|
||||
msgid "User"
|
||||
|
@ -1949,7 +1999,7 @@ msgstr "在线会话"
|
|||
msgid "Session offline"
|
||||
msgstr "离线会话"
|
||||
|
||||
#: templates/_nav.html:53 terminal/models.py:99
|
||||
#: templates/_nav.html:53 terminal/models.py:130
|
||||
#: terminal/templates/terminal/command_list.html:55
|
||||
#: terminal/templates/terminal/command_list.html:71
|
||||
#: terminal/templates/terminal/session_detail.html:48
|
||||
|
@ -1998,56 +2048,56 @@ msgstr "SSH 监听端口"
|
|||
msgid "Coco http/ws listen port"
|
||||
msgstr "Http/Websocket 监听端口"
|
||||
|
||||
#: terminal/models.py:15
|
||||
#: terminal/models.py:24
|
||||
msgid "Remote Address"
|
||||
msgstr "远端地址"
|
||||
|
||||
#: terminal/models.py:16
|
||||
#: terminal/models.py:25
|
||||
msgid "SSH Port"
|
||||
msgstr "SSH端口"
|
||||
|
||||
#: terminal/models.py:17
|
||||
#: terminal/models.py:26
|
||||
msgid "HTTP Port"
|
||||
msgstr "HTTP端口"
|
||||
|
||||
#: terminal/models.py:68
|
||||
#: terminal/models.py:99
|
||||
msgid "Session Online"
|
||||
msgstr "在线会话"
|
||||
|
||||
#: terminal/models.py:69
|
||||
#: terminal/models.py:100
|
||||
msgid "CPU Usage"
|
||||
msgstr "CPU使用"
|
||||
|
||||
#: terminal/models.py:70
|
||||
#: terminal/models.py:101
|
||||
msgid "Memory Used"
|
||||
msgstr "内存使用"
|
||||
|
||||
#: terminal/models.py:71
|
||||
#: terminal/models.py:102
|
||||
msgid "Connections"
|
||||
msgstr "连接数"
|
||||
|
||||
#: terminal/models.py:72
|
||||
#: terminal/models.py:103
|
||||
msgid "Threads"
|
||||
msgstr "线程数"
|
||||
|
||||
#: terminal/models.py:73
|
||||
#: terminal/models.py:104
|
||||
msgid "Boot Time"
|
||||
msgstr "运行时间"
|
||||
|
||||
#: terminal/models.py:96 terminal/templates/terminal/session_list.html:74
|
||||
#: terminal/models.py:127 terminal/templates/terminal/session_list.html:74
|
||||
#: terminal/templates/terminal/terminal_detail.html:47
|
||||
msgid "Remote addr"
|
||||
msgstr "远端地址"
|
||||
|
||||
#: terminal/models.py:98 terminal/templates/terminal/session_list.html:100
|
||||
#: terminal/models.py:129 terminal/templates/terminal/session_list.html:100
|
||||
msgid "Replay"
|
||||
msgstr "回放"
|
||||
|
||||
#: terminal/models.py:102
|
||||
#: terminal/models.py:133
|
||||
msgid "Date end"
|
||||
msgstr "结束日期"
|
||||
|
||||
#: terminal/models.py:119
|
||||
#: terminal/models.py:150
|
||||
msgid "Args"
|
||||
msgstr "参数"
|
||||
|
||||
|
@ -2810,5 +2860,8 @@ msgstr "密码更新"
|
|||
msgid "Public key update"
|
||||
msgstr "秘钥更新"
|
||||
|
||||
#~ msgid "Enable LDAP Auth"
|
||||
#~ msgstr "LDAP认证"
|
||||
|
||||
#~ msgid "Connect"
|
||||
#~ msgstr "连接"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from collections import OrderedDict
|
||||
import copy
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
|
@ -21,7 +20,8 @@ from .serializers import TerminalSerializer, StatusSerializer, \
|
|||
SessionSerializer, TaskSerializer, ReplaySerializer
|
||||
from .hands import IsSuperUserOrAppUser, IsAppUser, \
|
||||
IsSuperUserOrAppUserOrUserReadonly
|
||||
from .backends import get_command_store, SessionCommandSerializer
|
||||
from .backends import get_command_store, get_multi_command_store, \
|
||||
SessionCommandSerializer
|
||||
|
||||
logger = logging.getLogger(__file__)
|
||||
|
||||
|
@ -197,6 +197,7 @@ class CommandViewSet(viewsets.ViewSet):
|
|||
|
||||
"""
|
||||
command_store = get_command_store()
|
||||
multi_command_storage = get_multi_command_store()
|
||||
serializer_class = SessionCommandSerializer
|
||||
permission_classes = (IsSuperUserOrAppUser,)
|
||||
|
||||
|
@ -217,7 +218,7 @@ class CommandViewSet(viewsets.ViewSet):
|
|||
return Response({"msg": msg}, status=401)
|
||||
|
||||
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)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
@ -260,3 +261,13 @@ class SessionReplayViewSet(viewsets.ViewSet):
|
|||
return redirect(url)
|
||||
else:
|
||||
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 .command.serializers import SessionCommandSerializer
|
||||
|
||||
TYPE_ENGINE_MAPPING = {
|
||||
'elasticsearch': 'terminal.backends.command.es',
|
||||
}
|
||||
|
||||
|
||||
def get_command_store():
|
||||
command_engine = import_module(settings.COMMAND_STORAGE_BACKEND)
|
||||
command_store = command_engine.CommandStore()
|
||||
return command_store
|
||||
params = settings.COMMAND_STORAGE
|
||||
engine_class = import_module(params['ENGINE'])
|
||||
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):
|
||||
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):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, params):
|
||||
from terminal.models import Command
|
||||
self.model = Command
|
||||
|
||||
|
@ -37,9 +37,11 @@ class CommandStore(CommandBase):
|
|||
))
|
||||
return self.model.objects.bulk_create(_commands)
|
||||
|
||||
def filter(self, date_from=None, date_to=None,
|
||||
user=None, asset=None, system_user=None,
|
||||
input=None, session=None):
|
||||
@staticmethod
|
||||
def make_filter_kwargs(
|
||||
date_from=None, date_to=None,
|
||||
user=None, asset=None, system_user=None,
|
||||
input=None, session=None):
|
||||
filter_kwargs = {}
|
||||
date_from_default = timezone.now() - datetime.timedelta(days=7)
|
||||
date_to_default = timezone.now()
|
||||
|
@ -60,10 +62,28 @@ class CommandStore(CommandBase):
|
|||
if 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)
|
||||
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:
|
||||
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):
|
||||
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"])
|
||||
|
||||
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
|
|
@ -10,7 +10,7 @@ from .models import Terminal
|
|||
class TerminalForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Terminal
|
||||
fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment']
|
||||
fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment', 'command_storage']
|
||||
help_texts = {
|
||||
'ssh_port': _("Coco ssh listen port"),
|
||||
'http_port': _("Coco http/ws listen port"),
|
||||
|
|
|
@ -4,17 +4,28 @@ import uuid
|
|||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from users.models import User
|
||||
from .backends.command.models import AbstractSessionCommand
|
||||
|
||||
|
||||
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 Terminal(models.Model):
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
name = models.CharField(max_length=32, verbose_name=_('Name'))
|
||||
remote_addr = models.CharField(max_length=128, verbose_name=_('Remote Address'))
|
||||
ssh_port = models.IntegerField(verbose_name=_('SSH Port'), default=2222)
|
||||
http_port = models.IntegerField(verbose_name=_('HTTP Port'), default=5000)
|
||||
command_storage = models.CharField(max_length=128, verbose_name=_("Command storage"), default='default', choices=get_all_command_storage())
|
||||
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)
|
||||
is_accepted = models.BooleanField(default=False, verbose_name='Is Accepted')
|
||||
is_deleted = models.BooleanField(default=False)
|
||||
|
@ -33,6 +44,26 @@ class Terminal(models.Model):
|
|||
self.user.is_active = active
|
||||
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):
|
||||
random = uuid.uuid4().hex[:6]
|
||||
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 .models import Terminal, Status, Session, Task
|
||||
from .backends import get_command_store
|
||||
from .backends import get_multi_command_store
|
||||
|
||||
|
||||
class TerminalSerializer(serializers.ModelSerializer):
|
||||
|
@ -43,14 +43,14 @@ class TerminalSerializer(serializers.ModelSerializer):
|
|||
|
||||
class SessionSerializer(serializers.ModelSerializer):
|
||||
command_amount = serializers.SerializerMethodField()
|
||||
command_store = get_command_store()
|
||||
command_store = get_multi_command_store()
|
||||
|
||||
class Meta:
|
||||
model = Session
|
||||
fields = '__all__'
|
||||
|
||||
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):
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
{% bootstrap_field form.remote_addr layout="horizontal" %}
|
||||
{% bootstrap_field form.ssh_port layout="horizontal" %}
|
||||
{% bootstrap_field form.http_port layout="horizontal" %}
|
||||
{% bootstrap_field form.command_storage layout="horizontal" %}
|
||||
{% bootstrap_field form.comment layout="horizontal" %}
|
||||
</form>
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
{% bootstrap_field form.remote_addr layout="horizontal" %}
|
||||
{% bootstrap_field form.ssh_port layout="horizontal" %}
|
||||
{% bootstrap_field form.http_port layout="horizontal" %}
|
||||
{% bootstrap_field form.command_storage layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>{% trans 'Other' %}</h3>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from django import template
|
||||
from ..backends import get_command_store
|
||||
from ..backends import get_multi_command_store
|
||||
|
||||
register = template.Library()
|
||||
command_store = get_command_store()
|
||||
command_store = get_multi_command_store()
|
||||
|
||||
|
||||
@register.filter
|
||||
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/$',
|
||||
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
||||
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
|
||||
|
|
|
@ -9,10 +9,10 @@ from django.utils.translation import ugettext as _
|
|||
from common.mixins import DatetimeSearchMixin
|
||||
from ..models import Command
|
||||
from .. import utils
|
||||
from ..backends import get_command_store
|
||||
from ..backends import get_multi_command_store
|
||||
|
||||
__all__ = ['CommandListView']
|
||||
command_store = get_command_store()
|
||||
common_storage = get_multi_command_store()
|
||||
|
||||
|
||||
class CommandListView(DatetimeSearchMixin, ListView):
|
||||
|
@ -39,7 +39,7 @@ class CommandListView(DatetimeSearchMixin, ListView):
|
|||
filter_kwargs['system_user'] = self.system_user
|
||||
if self.command:
|
||||
filter_kwargs['input'] = self.command
|
||||
queryset = command_store.filter(**filter_kwargs)
|
||||
queryset = common_storage.filter(**filter_kwargs)
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
|
|
@ -10,7 +10,7 @@ from django.conf import settings
|
|||
from users.utils import AdminUserRequiredMixin
|
||||
from common.mixins import DatetimeSearchMixin
|
||||
from ..models import Session, Command, Terminal
|
||||
from ..backends import get_command_store
|
||||
from ..backends import get_multi_command_store
|
||||
from .. import utils
|
||||
|
||||
|
||||
|
@ -19,7 +19,7 @@ __all__ = [
|
|||
'SessionDetailView',
|
||||
]
|
||||
|
||||
command_store = get_command_store()
|
||||
command_store = get_multi_command_store()
|
||||
|
||||
|
||||
class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
|
||||
|
|
|
@ -172,7 +172,6 @@ class UserBulkUpdateForm(forms.ModelForm):
|
|||
if self.data.get(field) is not None:
|
||||
changed_fields.append(field)
|
||||
|
||||
print(changed_fields)
|
||||
cleaned_data = {k: v for k, v in self.cleaned_data.items()
|
||||
if k in changed_fields}
|
||||
users = cleaned_data.pop('users', '')
|
||||
|
|
Loading…
Reference in New Issue