Merge branch 'es' into dev

This commit is contained in:
ibuler 2018-01-22 11:51:37 +08:00
commit 968b395bce
32 changed files with 615 additions and 142 deletions

View File

@ -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'),

View File

@ -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:

View File

@ -18,7 +18,6 @@ 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:

View File

@ -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
@ -30,28 +32,32 @@ def to_form_value(value):
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 = 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)
@ -126,6 +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
) )
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"
)
) )

View File

@ -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":

View File

@ -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 %}

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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 %}

View File

@ -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'),
] ]

View File

@ -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
@ -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)

View File

@ -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.

View File

@ -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-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" "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:124
#: 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:23
#: terminal/models.py:118 terminal/templates/terminal/terminal_detail.html:43 #: terminal/models.py:149 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: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/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:33
#: 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:125
#: 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
@ -602,20 +603,24 @@ msgid "Import asset"
msgstr "导入资产" msgstr "导入资产"
#: assets/templates/assets/_asset_import_modal.html:9 #: 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 #: users/templates/users/_user_import_modal.html:10
msgid "Template" msgid "Template"
msgstr "模板" msgstr "模板"
#: assets/templates/assets/_asset_import_modal.html:10 #: 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 #: users/templates/users/_user_import_modal.html:11
msgid "Download" msgid "Download"
msgstr "下载" msgstr "下载"
#: assets/templates/assets/_asset_import_modal.html:13 #: assets/templates/assets/_asset_import_modal.html:13
#: common/templates/common/_add_terminal_command_storage_modal.html:13
msgid "Asset csv file" msgid "Asset csv file"
msgstr "资产csv文件" msgstr "资产csv文件"
#: assets/templates/assets/_asset_import_modal.html:16 #: 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" msgid "If set id, will use this id update asset existed"
msgstr "如果设置了id则会使用该行信息更新该id的资产" msgstr "如果设置了id则会使用该行信息更新该id的资产"
@ -650,7 +655,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,11 +666,12 @@ 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:55 #: 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:49
#: 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
@ -684,11 +690,12 @@ msgstr "重置"
#: assets/templates/assets/asset_list.html:53 #: 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:56 #: 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:50
#: 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
@ -778,6 +785,7 @@ msgstr "资产列表"
#: assets/templates/assets/asset_group_detail.html:53 #: assets/templates/assets/asset_group_detail.html:53
#: 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 "类型"
@ -878,7 +886,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 "资产详情"
@ -1236,16 +1244,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 "集群详情"
@ -1291,81 +1299,108 @@ 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/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:79 #: common/forms.py:85
msgid "SMTP host" msgid "SMTP host"
msgstr "SMTP主机" msgstr "SMTP主机"
#: common/forms.py:81 #: common/forms.py:87
msgid "SMTP port" msgid "SMTP port"
msgstr "SMTP端口" msgstr "SMTP端口"
#: common/forms.py:83 #: common/forms.py:89
msgid "SMTP user" msgid "SMTP user"
msgstr "SMTP账号" msgstr "SMTP账号"
#: common/forms.py:86 #: common/forms.py:92
msgid "SMTP password" msgid "SMTP password"
msgstr "SMTP密码" msgstr "SMTP密码"
#: common/forms.py:87 #: 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:90 common/forms.py:127 #: common/forms.py:96 common/forms.py:133
msgid "Use SSL" msgid "Use SSL"
msgstr "使用SSL" msgstr "使用SSL"
#: common/forms.py:91 #: 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:94 #: common/forms.py:100
msgid "Use TLS" msgid "Use TLS"
msgstr "使用TLS" msgstr "使用TLS"
#: common/forms.py:95 #: 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:101 #: common/forms.py:107
msgid "LDAP server" msgid "LDAP server"
msgstr "LDAP地址" msgstr "LDAP地址"
#: common/forms.py:104 #: common/forms.py:110
msgid "Bind DN" msgid "Bind DN"
msgstr "绑定DN" msgstr "绑定DN"
#: common/forms.py:111 #: common/forms.py:117
msgid "User OU" msgid "User OU"
msgstr "用户OU" msgstr "用户OU"
#: common/forms.py:114 #: common/forms.py:120
msgid "User search filter" msgid "User search filter"
msgstr "用户过滤器" msgstr "用户过滤器"
#: common/forms.py:117 #: common/forms.py:123
msgid "User attr map" msgid "User attr map"
msgstr "LDAP属性映射" msgstr "LDAP属性映射"
#: common/forms.py:130 #: common/forms.py:143
msgid "Enable LDAP Auth" msgid "List sort by"
msgstr "开启LDAP认证" 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 #: common/mixins.py:29
msgid "is discard" msgid "is discard"
@ -1375,43 +1410,62 @@ 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/_add_terminal_command_storage_modal.html:4
msgid "Add command storage"
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:44 #: 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/email_setting.html:55 #: common/templates/common/basic_setting.html:24
#: common/templates/common/ldap_setting.html:55 #: 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" msgid "Test connection"
msgstr "测试连接" 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 #: templates/_nav.html:69
msgid "Settings" msgid "Settings"
msgstr "系统设置" 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" msgid "Update setting successfully, please restart program"
msgstr "更新设置成功, 请手动重启程序" msgstr "更新设置成功, 请手动重启程序"
@ -1419,10 +1473,6 @@ msgstr "更新设置成功, 请手动重启程序"
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"
@ -1566,7 +1616,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:132
#: terminal/templates/terminal/session_list.html:77 #: terminal/templates/terminal/session_list.html:77
msgid "Date start" msgid "Date start"
msgstr "开始日期" msgstr "开始日期"
@ -1683,18 +1733,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:123
#: 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"
@ -1949,7 +1999,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:130
#: 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
@ -1998,56 +2048,56 @@ msgstr "SSH 监听端口"
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:24
msgid "Remote Address" msgid "Remote Address"
msgstr "远端地址" msgstr "远端地址"
#: terminal/models.py:16 #: terminal/models.py:25
msgid "SSH Port" msgid "SSH Port"
msgstr "SSH端口" msgstr "SSH端口"
#: terminal/models.py:17 #: terminal/models.py:26
msgid "HTTP Port" msgid "HTTP Port"
msgstr "HTTP端口" msgstr "HTTP端口"
#: terminal/models.py:68 #: terminal/models.py:99
msgid "Session Online" msgid "Session Online"
msgstr "在线会话" msgstr "在线会话"
#: terminal/models.py:69 #: terminal/models.py:100
msgid "CPU Usage" msgid "CPU Usage"
msgstr "CPU使用" msgstr "CPU使用"
#: terminal/models.py:70 #: terminal/models.py:101
msgid "Memory Used" msgid "Memory Used"
msgstr "内存使用" msgstr "内存使用"
#: terminal/models.py:71 #: terminal/models.py:102
msgid "Connections" msgid "Connections"
msgstr "连接数" msgstr "连接数"
#: terminal/models.py:72 #: terminal/models.py:103
msgid "Threads" msgid "Threads"
msgstr "线程数" msgstr "线程数"
#: terminal/models.py:73 #: terminal/models.py:104
msgid "Boot Time" msgid "Boot Time"
msgstr "运行时间" 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 #: 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:129 terminal/templates/terminal/session_list.html:100
msgid "Replay" msgid "Replay"
msgstr "回放" msgstr "回放"
#: terminal/models.py:102 #: terminal/models.py:133
msgid "Date end" msgid "Date end"
msgstr "结束日期" msgstr "结束日期"
#: terminal/models.py:119 #: terminal/models.py:150
msgid "Args" msgid "Args"
msgstr "参数" msgstr "参数"
@ -2810,5 +2860,8 @@ msgstr "密码更新"
msgid "Public key update" msgid "Public key update"
msgstr "秘钥更新" msgstr "秘钥更新"
#~ msgid "Enable LDAP Auth"
#~ msgstr "LDAP认证"
#~ msgid "Connect" #~ msgid "Connect"
#~ msgstr "连接" #~ msgstr "连接"

View File

@ -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__)
@ -197,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,)
@ -217,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)
@ -260,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)

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -10,7 +10,7 @@ from .models import Terminal
class TerminalForm(forms.ModelForm): class TerminalForm(forms.ModelForm):
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"),

View File

@ -4,17 +4,28 @@ 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
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): class Terminal(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=32, verbose_name=_('Name')) name = models.CharField(max_length=32, verbose_name=_('Name'))
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', 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) 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 +44,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)

View File

@ -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):

View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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):

View File

@ -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', '')