From ba5ab21b371fd05672adcb618d502ee81a92f2d4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 17 Jan 2018 17:17:18 +0800 Subject: [PATCH 1/5] For storage --- .../templates/common/basic_setting.html | 3 + .../templates/common/email_setting.html | 3 + .../common/templates/common/ldap_setting.html | 3 + .../templates/common/storage_setting.html | 102 ++++++++++++++++++ apps/common/urls/view_urls.py | 1 + apps/common/views.py | 28 +++++ apps/locale/zh/LC_MESSAGES/django.po | 2 +- 7 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 apps/common/templates/common/storage_setting.html diff --git a/apps/common/templates/common/basic_setting.html b/apps/common/templates/common/basic_setting.html index fb5039795..9d67d762d 100644 --- a/apps/common/templates/common/basic_setting.html +++ b/apps/common/templates/common/basic_setting.html @@ -20,6 +20,9 @@
  • {% trans 'LDAP setting' %}
  • +
  • + {% trans 'Storage setting' %} +
  • diff --git a/apps/common/templates/common/email_setting.html b/apps/common/templates/common/email_setting.html index 7561849bd..2cd018021 100644 --- a/apps/common/templates/common/email_setting.html +++ b/apps/common/templates/common/email_setting.html @@ -20,6 +20,9 @@
  • {% trans 'LDAP setting' %}
  • +
  • + {% trans 'Storage setting' %} +
  • diff --git a/apps/common/templates/common/ldap_setting.html b/apps/common/templates/common/ldap_setting.html index 26f021569..de4a196c9 100644 --- a/apps/common/templates/common/ldap_setting.html +++ b/apps/common/templates/common/ldap_setting.html @@ -20,6 +20,9 @@
  • {% trans 'LDAP setting' %}
  • +
  • + {% trans 'Storage setting' %} +
  • diff --git a/apps/common/templates/common/storage_setting.html b/apps/common/templates/common/storage_setting.html new file mode 100644 index 000000000..cf2c243f1 --- /dev/null +++ b/apps/common/templates/common/storage_setting.html @@ -0,0 +1,102 @@ +{% extends 'base.html' %} +{% load static %} +{% load bootstrap3 %} +{% load i18n %} +{% load common_tags %} + +{% block content %} +
    +
    +
    +
    + +
    +
    +
    +
    + {% if form.non_field_errors %} +
    + {{ form.non_field_errors }} +
    + {% endif %} + {% csrf_token %} +

    {% trans "Command storage" %}

    + + + + + + + + + + +
    {% trans 'Name' %}{% trans 'Engine' %}{% trans 'Action' %}
    + +
    +

    {% trans "Replay storage" %}

    + +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/common/urls/view_urls.py b/apps/common/urls/view_urls.py index ff8086bde..57594b043 100644 --- a/apps/common/urls/view_urls.py +++ b/apps/common/urls/view_urls.py @@ -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'^storage/$', views.StorageSettingView.as_view(), name='storage-setting'), ] diff --git a/apps/common/views.py b/apps/common/views.py index 4135ca82c..6ab46ead4 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -4,6 +4,7 @@ from django.contrib import messages from django.utils.translation import ugettext as _ from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm +from .models import Setting from .mixins import AdminUserRequiredMixin from .signals import ldap_auth_enable @@ -86,3 +87,30 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView): context = self.get_context_data() context.update({"form": form}) return render(request, self.template_name, context) + + +class StorageSettingView(AdminUserRequiredMixin, TemplateView): + form_class = LDAPSettingForm + template_name = "common/storage_setting.html" + + def get_context_data(self, **kwargs): + context = { + 'app': _('Settings'), + 'action': _('Storage setting'), + 'form': self.form_class(), + 'command_storage': Setting.objects.filter(name__endswith="_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:storage-setting') + else: + context = self.get_context_data() + context.update({"form": form}) + return render(request, self.template_name, context) diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 2c7f39971..55e035bfe 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -1311,7 +1311,7 @@ msgstr "Email主题前缀" #: common/forms.py:76 msgid "Enable LDAP Auth" -msgstr "二次验证" +msgstr "LDAP认证" #: common/forms.py:82 msgid "SMTP host" From 67001dd99f9798a67a4d6b89d7c0adf20d3ca019 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 18 Jan 2018 09:56:13 +0800 Subject: [PATCH 2/5] =?UTF-8?q?[Feature]=20=E6=94=AF=E6=8C=81es=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/settings.py | 3 ++- apps/terminal/backends/command/es.py | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 apps/terminal/backends/command/es.py diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 7a8aa422e..89686cada 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -373,7 +373,8 @@ 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_BACKEND = 'terminal.backends.command.db' +COMMAND_STORAGE_BACKEND = 'terminal.backends.command.es' # Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html BOOTSTRAP3 = { diff --git a/apps/terminal/backends/command/es.py b/apps/terminal/backends/command/es.py new file mode 100644 index 000000000..39c83f09d --- /dev/null +++ b/apps/terminal/backends/command/es.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# + +from jms_es_storage import ESStore +from .base import CommandBase + + +class CommandStore(CommandBase, ESStore): + def __init__(self): + ESStore.__init__(self, hosts=["http://elastic:changeme@localhost:9200"]) + + 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] From b936d54a4883782bcd5f2db779c9441dd5c49193 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 20 Jan 2018 22:22:09 +0800 Subject: [PATCH 3/5] =?UTF-8?q?[Feature]=20=E6=B7=BB=E5=8A=A0es=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/views/cluster.py | 5 --- apps/common/forms.py | 43 ++++++++++++++++--- apps/common/models.py | 15 +++++-- .../_add_terminal_command_storage_modal.html | 21 +++++++++ .../templates/common/basic_setting.html | 2 +- .../templates/common/email_setting.html | 2 +- .../common/templates/common/ldap_setting.html | 2 +- ...age_setting.html => terminal_setting.html} | 38 +++++++++++++--- apps/common/urls/view_urls.py | 2 +- apps/common/views.py | 20 +++++---- apps/jumpserver/settings.py | 18 +++++++- apps/terminal/api.py | 11 ++++- apps/terminal/backends/__init__.py | 30 +++++++++++-- apps/terminal/backends/command/base.py | 6 +++ apps/terminal/backends/command/db.py | 34 ++++++++++++--- apps/terminal/backends/command/models.py | 15 +++++++ apps/terminal/forms.py | 2 +- apps/terminal/models.py | 20 +++++++++ apps/terminal/serializers.py | 4 +- .../terminal/terminal_modal_accept.html | 1 + .../templates/terminal/terminal_update.html | 1 + apps/terminal/templatetags/terminal_tags.py | 10 +++-- apps/terminal/views/command.py | 9 ++-- apps/terminal/views/session.py | 4 +- 24 files changed, 259 insertions(+), 56 deletions(-) create mode 100644 apps/common/templates/common/_add_terminal_command_storage_modal.html rename apps/common/templates/common/{storage_setting.html => terminal_setting.html} (63%) diff --git a/apps/assets/views/cluster.py b/apps/assets/views/cluster.py index 835229fc1..5df58953c 100644 --- a/apps/assets/views/cluster.py +++ b/apps/assets/views/cluster.py @@ -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'), diff --git a/apps/common/forms.py b/apps/common/forms.py index 073bb671f..ab3dadba3 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -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 not None: field.initial = to_form_value(db_value) + elif django_value is not None: + 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) @@ -129,3 +135,28 @@ class LDAPSettingForm(BaseForm): AUTH_LDAP_START_TLS = forms.BooleanField( label=_("Use SSL"), initial=False, required=False ) + + +class TerminalSettingForm(BaseForm): + SORT_BY_CHOICES = ( + ('hostname', _('Hostname')), + ('ip', _('IP')), + ) + TERMINAL_ASSET_LIST_SORT_BY = forms.ChoiceField( + choices=SORT_BY_CHOICES, initial='hostname', label=_("List sort by") + ) + TERMINAL_HEARTBEAT_INTERVAL = forms.IntegerField( + initial=5, label=_("Heartbeat interval"), help_text=_("Units: seconds") + ) + TERMINAL_PASSWORD_AUTH = forms.BooleanField( + initial=True, required=False, label=_("Password auth") + ) + TERMINAL_PUBLIC_KEY_AUTH = forms.BooleanField( + initial=True, required=False, label=_("Public key auth") + ) + TERMINAL_COMMAND_STORAGE = DictField( + label=_("Command storage"), help_text=_( + "Set terminal storage setting, `default` is the using as default," + "You can set other storage and some terminal using" + ) + ) diff --git a/apps/common/models.py b/apps/common/models.py index 091b8b83a..afee1e13d 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -24,6 +24,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,12 +34,20 @@ 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() @@ -53,9 +62,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": diff --git a/apps/common/templates/common/_add_terminal_command_storage_modal.html b/apps/common/templates/common/_add_terminal_command_storage_modal.html new file mode 100644 index 000000000..678152981 --- /dev/null +++ b/apps/common/templates/common/_add_terminal_command_storage_modal.html @@ -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 %} +
    + {% csrf_token %} +
    + + {% trans 'Download' %} +
    +
    + + + + {% trans 'If set id, will use this id update asset existed' %} + +
    +
    +{% endblock %} +{% block modal_confirm_id %}btn_asset_import{% endblock %} diff --git a/apps/common/templates/common/basic_setting.html b/apps/common/templates/common/basic_setting.html index 9d67d762d..496eca977 100644 --- a/apps/common/templates/common/basic_setting.html +++ b/apps/common/templates/common/basic_setting.html @@ -21,7 +21,7 @@ {% trans 'LDAP setting' %}
  • - {% trans 'Storage setting' %} + {% trans 'Terminal setting' %}
  • diff --git a/apps/common/templates/common/email_setting.html b/apps/common/templates/common/email_setting.html index 2cd018021..1fd772db1 100644 --- a/apps/common/templates/common/email_setting.html +++ b/apps/common/templates/common/email_setting.html @@ -21,7 +21,7 @@ {% trans 'LDAP setting' %}
  • - {% trans 'Storage setting' %} + {% trans 'Terminal setting' %}
  • diff --git a/apps/common/templates/common/ldap_setting.html b/apps/common/templates/common/ldap_setting.html index de4a196c9..f0569f873 100644 --- a/apps/common/templates/common/ldap_setting.html +++ b/apps/common/templates/common/ldap_setting.html @@ -21,7 +21,7 @@ {% trans 'LDAP setting' %}
  • - {% trans 'Storage setting' %} + {% trans 'Terminal setting' %}
  • diff --git a/apps/common/templates/common/storage_setting.html b/apps/common/templates/common/terminal_setting.html similarity index 63% rename from apps/common/templates/common/storage_setting.html rename to apps/common/templates/common/terminal_setting.html index cf2c243f1..3d0b7eb6f 100644 --- a/apps/common/templates/common/storage_setting.html +++ b/apps/common/templates/common/terminal_setting.html @@ -21,7 +21,7 @@ {% trans 'LDAP setting' %}
  • - {% trans 'Storage setting' %} + {% trans 'Terminal setting' %}
  • @@ -35,26 +35,50 @@ {% endif %} {% csrf_token %} +

    {% trans "Basic setting" %}

    + {% for field in form %} + {% if not field.field|is_bool_field %} + {% bootstrap_field field layout="horizontal" %} + {% else %} +
    + +
    +
    + {{ field }} +
    +
    + {{ field.help_text }} +
    +
    +
    + {% endif %} + {% endfor %} + +

    {% trans "Command storage" %}

    - - + + {% for name, setting in command_storage.items %} + + + + + {% endfor %}
    {% trans 'Name' %}{% trans 'Engine' %}{% trans 'Action' %}{% trans 'Type' %}
    {{ name }}{{ setting.TYPE }}
    - +{# #}

    {% trans "Replay storage" %}

    -
    @@ -68,6 +92,7 @@
    + {% include 'common/_add_terminal_command_storage_modal.html' %} {% endblock %} {% block custom_foot_js %} {% endblock %} diff --git a/apps/common/urls/view_urls.py b/apps/common/urls/view_urls.py index 57594b043..466f7c49c 100644 --- a/apps/common/urls/view_urls.py +++ b/apps/common/urls/view_urls.py @@ -10,5 +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'^storage/$', views.StorageSettingView.as_view(), name='storage-setting'), + url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'), ] diff --git a/apps/common/views.py b/apps/common/views.py index 6ab46ead4..8e7dc8341 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -1,9 +1,11 @@ -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 @@ -89,27 +91,29 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView): return render(request, self.template_name, context) -class StorageSettingView(AdminUserRequiredMixin, TemplateView): - form_class = LDAPSettingForm - template_name = "common/storage_setting.html" +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': _('Storage setting'), + 'action': _('Terminal setting'), 'form': self.form_class(), - 'command_storage': Setting.objects.filter(name__endswith="_COMMAND_STORAGE") + 'command_storage': command_storage, } kwargs.update(context) return super().get_context_data(**kwargs) def post(self, request): + print(request.POST) 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:storage-setting') + return redirect('settings:terminal-setting') else: context = self.get_context_data() context.update({"form": form}) diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 7a8aa422e..d023b587a 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -373,7 +373,23 @@ 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'], + # }, + # 'ali-hz-es': { + # 'TYPE': 'elasticsearch', + # 'HOSTS': ['http://elastic:changeme@localhost:9200'], + # } +} # Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html BOOTSTRAP3 = { diff --git a/apps/terminal/api.py b/apps/terminal/api.py index 87311d5b2..e587d8928 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -21,7 +21,7 @@ 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_terminal_command_store, SessionCommandSerializer logger = logging.getLogger(__file__) @@ -196,7 +196,7 @@ class CommandViewSet(viewsets.ViewSet): } """ - command_store = get_command_store() + command_store = get_terminal_command_store() serializer_class = SessionCommandSerializer permission_classes = (IsSuperUserOrAppUser,) @@ -260,3 +260,10 @@ class SessionReplayViewSet(viewsets.ViewSet): return redirect(url) else: return HttpResponseNotFound() + + +class LoadConfig(APIView): + permission_classes = (IsAppUser,) + + def get(self, request): + pass diff --git a/apps/terminal/backends/__init__.py b/apps/terminal/backends/__init__.py index 6baaed3c4..8b09f0c55 100644 --- a/apps/terminal/backends/__init__.py +++ b/apps/terminal/backends/__init__.py @@ -2,9 +2,33 @@ from importlib import import_module from django.conf import settings from .command.serializers import SessionCommandSerializer +TYPE_ENGINE_MAPPING = { + 'elasticsearch': 'terminal.backends.command.db', +} + 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 + + diff --git a/apps/terminal/backends/command/base.py b/apps/terminal/backends/command/base.py index 0aa689738..585930a5d 100644 --- a/apps/terminal/backends/command/base.py +++ b/apps/terminal/backends/command/base.py @@ -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 + diff --git a/apps/terminal/backends/command/db.py b/apps/terminal/backends/command/db.py index 27ee19a14..85b99ce80 100644 --- a/apps/terminal/backends/command/db.py +++ b/apps/terminal/backends/command/db.py @@ -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 - def all(self): - """返回所有数据""" - return self.model.objects.iterator() + 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 + diff --git a/apps/terminal/backends/command/models.py b/apps/terminal/backends/command/models.py index 2d1387427..cc0d375e6 100644 --- a/apps/terminal/backends/command/models.py +++ b/apps/terminal/backends/command/models.py @@ -18,5 +18,20 @@ 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 __str__(self): return self.input diff --git a/apps/terminal/forms.py b/apps/terminal/forms.py index 5b6278c02..4253da70a 100644 --- a/apps/terminal/forms.py +++ b/apps/terminal/forms.py @@ -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"), diff --git a/apps/terminal/models.py b/apps/terminal/models.py index f545baa5f..b8524f727 100644 --- a/apps/terminal/models.py +++ b/apps/terminal/models.py @@ -4,17 +4,27 @@ 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 = [] + for k, v in settings.TERMINAL_COMMAND_STORAGE.items(): + storage_choices.append((k, k)) + return storage_choices + + 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 +43,16 @@ class Terminal(models.Model): self.user.is_active = active self.user.save() + def get_common_storage(self): + pass + + def get_replay_storage(self): + pass + + @property + def config(self): + return + 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) diff --git a/apps/terminal/serializers.py b/apps/terminal/serializers.py index 52b4d2b3c..ecea7edfd 100644 --- a/apps/terminal/serializers.py +++ b/apps/terminal/serializers.py @@ -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_terminal_command_store class TerminalSerializer(serializers.ModelSerializer): @@ -43,7 +43,7 @@ class TerminalSerializer(serializers.ModelSerializer): class SessionSerializer(serializers.ModelSerializer): command_amount = serializers.SerializerMethodField() - command_store = get_command_store() + command_store = get_terminal_command_store() class Meta: model = Session diff --git a/apps/terminal/templates/terminal/terminal_modal_accept.html b/apps/terminal/templates/terminal/terminal_modal_accept.html index b4a18b17c..fe70bb342 100644 --- a/apps/terminal/templates/terminal/terminal_modal_accept.html +++ b/apps/terminal/templates/terminal/terminal_modal_accept.html @@ -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" %} diff --git a/apps/terminal/templates/terminal/terminal_update.html b/apps/terminal/templates/terminal/terminal_update.html index 9ebd9d2d8..cbf745608 100644 --- a/apps/terminal/templates/terminal/terminal_update.html +++ b/apps/terminal/templates/terminal/terminal_update.html @@ -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" %}

    {% trans 'Other' %}

    diff --git a/apps/terminal/templatetags/terminal_tags.py b/apps/terminal/templatetags/terminal_tags.py index 22b517880..72822b00a 100644 --- a/apps/terminal/templatetags/terminal_tags.py +++ b/apps/terminal/templatetags/terminal_tags.py @@ -1,13 +1,15 @@ # ~*~ coding: utf-8 ~*~ from django import template -from ..backends import get_command_store +from ..backends import get_terminal_command_store register = template.Library() -command_store = get_command_store() +command_store_dict = get_terminal_command_store() @register.filter def get_session_command_amount(session_id): - return len(command_store.filter(session=str(session_id))) - + amount = 0 + for name, store in command_store_dict.items(): + amount += store.count(session=str(session_id)) + return amount diff --git a/apps/terminal/views/command.py b/apps/terminal/views/command.py index 8b0479d3e..79b2eb2cc 100644 --- a/apps/terminal/views/command.py +++ b/apps/terminal/views/command.py @@ -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_terminal_command_store __all__ = ['CommandListView'] -command_store = get_command_store() +command_store_list = get_terminal_command_store() class CommandListView(DatetimeSearchMixin, ListView): @@ -39,7 +39,10 @@ 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 = [] + for store in command_store_list: + queryset.extend(store.filter(**filter_kwargs)) + queryset = sorted(queryset, key=lambda c: c.timestamp, reverse=True) return queryset def get_context_data(self, **kwargs): diff --git a/apps/terminal/views/session.py b/apps/terminal/views/session.py index 22d04fbaf..63fcf5eb7 100644 --- a/apps/terminal/views/session.py +++ b/apps/terminal/views/session.py @@ -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_terminal_command_store from .. import utils @@ -19,7 +19,7 @@ __all__ = [ 'SessionDetailView', ] -command_store = get_command_store() +command_store = get_terminal_command_store() class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView): From 41f1c3f7f74f2b5c70f081c2e1cdfa33ae717b98 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 21 Jan 2018 15:12:59 +0800 Subject: [PATCH 4/5] [Feature] terminal config load --- apps/terminal/api.py | 7 +++++-- apps/terminal/models.py | 14 ++++++++++++-- apps/terminal/urls/api_urls.py | 3 ++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/apps/terminal/api.py b/apps/terminal/api.py index e587d8928..25408b6b4 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -262,8 +262,11 @@ class SessionReplayViewSet(viewsets.ViewSet): return HttpResponseNotFound() -class LoadConfig(APIView): +class TerminalConfig(APIView): permission_classes = (IsAppUser,) def get(self, request): - pass + user = request.user + terminal = user.terminal + configs = terminal.config + return Response(configs, status=200) diff --git a/apps/terminal/models.py b/apps/terminal/models.py index b8524f727..4c5244bcb 100644 --- a/apps/terminal/models.py +++ b/apps/terminal/models.py @@ -44,14 +44,24 @@ class Terminal(models.Model): self.user.save() def get_common_storage(self): - pass + 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): - return + 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] diff --git a/apps/terminal/urls/api_urls.py b/apps/terminal/urls/api_urls.py index 55d77ee00..a3ebc2129 100644 --- a/apps/terminal/urls/api_urls.py +++ b/apps/terminal/urls/api_urls.py @@ -20,7 +20,8 @@ urlpatterns = [ url(r'^v1/sessions/(?P[0-9a-zA-Z\-]{36})/replay/$', api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}), name='session-replay'), - url(r'^v1/terminal/(?P[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key') + url(r'^v1/terminal/(?P[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 From 0a931bbf776dc39b729922c7c9c92e13b9a33e50 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 22 Jan 2018 11:38:40 +0800 Subject: [PATCH 5/5] [Feature] es support --- apps/common/api.py | 5 - apps/common/fields.py | 1 - apps/common/forms.py | 4 +- apps/common/models.py | 10 +- apps/common/views.py | 1 - apps/jumpserver/settings.py | 4 - apps/locale/zh/LC_MESSAGES/django.mo | Bin 31661 -> 32209 bytes apps/locale/zh/LC_MESSAGES/django.po | 227 ++++++++++++++++----------- apps/users/forms.py | 1 - 9 files changed, 147 insertions(+), 106 deletions(-) diff --git a/apps/common/api.py b/apps/common/api.py index e8680e85e..e75f6147a 100644 --- a/apps/common/api.py +++ b/apps/common/api.py @@ -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: diff --git a/apps/common/fields.py b/apps/common/fields.py index 36a8bdf9a..f19689830 100644 --- a/apps/common/fields.py +++ b/apps/common/fields.py @@ -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: diff --git a/apps/common/forms.py b/apps/common/forms.py index bb708c2f7..3eb8c4989 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -36,9 +36,9 @@ class BaseForm(forms.Form): for name, field in self.fields.items(): db_value = getattr(db_settings, name).value django_value = getattr(settings, name) if hasattr(settings, name) else None - if db_value is not None: + if db_value is False or db_value: field.initial = to_form_value(db_value) - elif django_value is not None: + elif django_value is False or django_value: field.initial = django_value def save(self, category="default"): diff --git a/apps/common/models.py b/apps/common/models.py index afee1e13d..1f634bce2 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -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 @@ -50,9 +51,12 @@ class Setting(models.Model): @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: diff --git a/apps/common/views.py b/apps/common/views.py index 1e8ad503d..dc0d74f92 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -107,7 +107,6 @@ class TerminalSettingView(AdminUserRequiredMixin, TemplateView): return super().get_context_data(**kwargs) def post(self, request): - print(request.POST) form = self.form_class(request.POST) if form.is_valid(): form.save() diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index a164d3355..6b9242d45 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -385,10 +385,6 @@ TERMINAL_COMMAND_STORAGE = { # 'TYPE': 'elasticsearch', # 'HOSTS': ['http://elastic:changeme@localhost:9200'], # }, - # 'ali-hz-es': { - # 'TYPE': 'elasticsearch', - # 'HOSTS': ['http://elastic:changeme@localhost:9200'], - # } } diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 7be8a996a99f2451a1f8854e863216f289eaa052..f6489066200c442d9a9bbce51198493a10222cac 100644 GIT binary patch delta 11945 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 delta 11406 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

    RW};csY=njA*9v`~|IReD;^$EV zj7L4YNvIu|iKTEM7Q)S_e!EcJen0q7Tu{3!q#$X>TgdTI=Fh2hpXgLK< zv^=MO2f7@;`oc;H$h=L}X zhw-=)wSdE@g&ni}l=%zlx+~@#)b;)&+Q^y<{4VOc zhi1TNH!p}5F`E<({Vg&UK=5F&S>bhT1 z1KzMa4=a!djB($kM6)xh|5S6n)aQQ%jZj>VdM4XZ10FKJMO}Ep{MEc+=O0=gFxLGz z7C~KK4J%?DjKhAY8=PUTMBnFsJBbkEjKWkn?-<_it7W|@XBx->%9&3~{6HpUYMGe>xH9!Z{S8`v}KtoVFkcoO^ zFQcxTXD&xww+`#zHq>}GQP&6XS8AR2gj%DNnS|=l05xC>vy+|gi~7zVZS~i!z8asP zzQyt{%^y+Y{e~LvCTby%-18o%$V=|lmqB%`gW8%j)U$1E^>nL0hZ?B2In3%WnNv{X z%(8qD>X*}U^K%?TeiJJ@o%ze*%kF@)&84UT-a$Vs#t;495wFtzoVYNJMocv5i5!EG#?+gROfh-=t{e-<*Mal1v;J~t`a|U zu8^JQPMqzu?-F0reh>5WY<>T7{0B)0q3iGAG}NJgLm5lxXh{6vYdKCWYISLMzzz5c zHbOlUJ&!-hYZ7sU4n1@oJIN>G8lomqf%$#MbNsBt$rHpWVi4zjuQaY8{|WCCQ;BLsMdGkJb4;WiVe|V6)%lrAF^KqQ8F8F;F`^cs;|y_$dQ+T@d+}%D@$nZm&p3V@rShd!Pnthc|BQB1 zOeb{k2MFJxS2fkzAK-A#|4#U`GlR*0wR0WJS2_0@vBz@JnY@yozm78`tBFapi{hum z<6|K00>qbAJC0R2`G$4)jXdgcXZ)1-o7iprb!v#U-$Z>A1+q~7;fZgn|Lp(!oP34i z3hO+9_BG-ld0%`RU$=9uOv+AO+UZz;LC^oEKArXnb+BU-P9gYGand-ase2Ts(VxZ; z;s@F_@Ec+PAI@T)3@TNL+C(tJ>+q=UHl5P67Z5u1?~Kn7Tiw*j!O`S}EPoe2CVzow zVKs4tT*oY;hPCJ8-Nz3{C!g1k{~Y||nDcy_A zgbw}V={({F@rXQ-SVEj8-{{8nQIGa2!jJYc>_jBdetZn0 zQP7fZ)c;4jFpea`i84eTYJcN*gpMd&jsFlY(f){dPtX4g3cUy&M~FF`&@qwNMtdPq z+gEXaY=IAnjQ`ZsJWHIkYmb=+sO_K~gPrhcyi6RTy^2_`Pl%U7Gm;c+g6)V2#9l&2 z2%n0-@E#GW0>?lXXD+UxUo6%qmJ*4Cj&pW>YuYWXt@=~60~qrh?M?cq>DcGugp!Z9 z+G6Z(?Ok^Mj`^`U-yCTk#OLf9l>&(T_OVtzgV;^PDdEuj=ld@v=hfgCN>pZm5%`M@ z)ZA=9tqScHmRBSHifB*hC~x(v*oAyEUa;CNUk}~~j3$~9-}t%nJJED1LZJz9iAW{y zYZrb`uH$tV=PT+JX{X>i+=DIc+$6i^AM-TzDYP3CTZlzg-+)hX?gM@PlSu;X#D0pG zY0t)fR-@^>NW4YNtf3iQi~9CNhZ}@36%8gS?fhwF~mju3g4I_?fiScCo3MV+VTI z)Qk1=9\n" "Language-Team: Jumpserver team\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,85 +1299,108 @@ msgstr "%(name)s 创建成功" msgid "%(name)s was updated successfully" msgstr "%(name)s 更新成功" -#: 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:76 -msgid "Enable LDAP Auth" -msgstr "LDAP认证" - -#: common/forms.py:82 +#: 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" @@ -1379,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 "更新设置成功, 请手动重启程序" @@ -1423,10 +1473,6 @@ msgstr "更新设置成功, 请手动重启程序" msgid "Interval" msgstr "间隔" -#: ops/models.py:32 -msgid "Units: seconds" -msgstr "单位: 秒" - #: ops/models.py:33 msgid "Crontab" msgstr "Crontab" @@ -1570,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 "开始日期" @@ -1687,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" @@ -1953,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 @@ -2002,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 "参数" @@ -2814,5 +2860,8 @@ msgstr "密码更新" msgid "Public key update" msgstr "秘钥更新" +#~ msgid "Enable LDAP Auth" +#~ msgstr "LDAP认证" + #~ msgid "Connect" #~ msgstr "连接" diff --git a/apps/users/forms.py b/apps/users/forms.py index 018c39a18..b239f58ee 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -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', '')