Commit 968b395b authored by ibuler's avatar ibuler

Merge branch 'es' into dev

parents 7f188219 0a931bbf
...@@ -60,11 +60,6 @@ class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView) ...@@ -60,11 +60,6 @@ class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView)
success_url = reverse_lazy('assets:cluster-list') success_url = reverse_lazy('assets:cluster-list')
success_message = update_success_msg success_message = update_success_msg
def form_valid(self, form):
cluster = form.save(commit=False)
cluster.save()
return super().form_valid(form)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('assets'), 'app': _('assets'),
......
...@@ -63,8 +63,6 @@ class LDAPTestingAPI(APIView): ...@@ -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): ...@@ -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:
......
...@@ -18,7 +18,6 @@ class DictField(forms.Field): ...@@ -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:
......
...@@ -4,7 +4,9 @@ import json ...@@ -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): ...@@ -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): ...@@ -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"
)
) )
...@@ -2,6 +2,7 @@ import json ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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":
......
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}add_command_storage_model{% endblock %}
{% block modal_title%}{% trans "Add command storage" %}{% endblock %}
{% block modal_body %}
<form method="post" action="" id="add_command_storage_form">
{% csrf_token %}
<div class="form-group">
<label class="control-label" for="id_assets">{% trans "Template" %}</label>
<a href="{% url 'assets:asset-export' %}" style="display: block">{% trans 'Download' %}</a>
</div>
<div class="form-group">
<label class="control-label" for="id_users">{% trans "Asset csv file" %}</label>
<input id="id_assets" type="file" name="file" />
<span class="help-block red-fonts">
{% trans 'If set id, will use this id update asset existed' %}
</span>
</div>
</form>
{% endblock %}
{% block modal_confirm_id %}btn_asset_import{% endblock %}
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
<li> <li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a> <a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li> </li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
</ul> </ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
......
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
<li> <li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a> <a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li> </li>
<li>
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
</ul> </ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
......
...@@ -20,6 +20,9 @@ ...@@ -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">
......
{% extends 'base.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load common_tags %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li>
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li>
<li class="active">
<a href="{% url 'settings:terminal-setting' %}" class="text-center"><i class="fa fa-hdd-o"></i> {% trans 'Terminal setting' %} </a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-12" style="padding-left:0">
<div class="ibox-content" style="border-width: 0;padding-top: 40px;">
<form action="" method="post" class="form-horizontal">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
<h3>{% trans "Basic setting" %}</h3>
{% for field in form %}
{% if not field.field|is_bool_field %}
{% bootstrap_field field layout="horizontal" %}
{% else %}
<div class="form-group">
<label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-8">
<div class="col-sm-1">
{{ field }}
</div>
<div class="col-sm-9">
<span class="help-block" >{{ field.help_text }}</span>
</div>
</div>
</div>
{% endif %}
{% endfor %}
<div class="hr-line-dashed"></div>
<h3>{% trans "Command storage" %}</h3>
<table class="table table-hover " id="task-history-list-table" >
<thead>
<tr>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Type' %}</th>
</tr>
</thead>
<tbody>
{% for name, setting in command_storage.items %}
<tr>
<td>{{ name }}</td>
<td>{{ setting.TYPE }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{# <button class="btn btn-default btn-circle btn-add-command-storage" data-toggle="modal" data-target="#add_command_storage_model" tabindex="0" type="button"><i class="fa fa-plus"></i></button>#}
<div class="hr-line-dashed"></div>
<h3>{% trans "Replay storage" %}</h3>
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'common/_add_terminal_command_storage_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
})
.on("click", ".btn-test", function () {
var data = {};
var form = $("form").serializeArray();
$.each(form, function (i, field) {
data[field.name] = field.value;
});
var the_url = "{% url 'api-common:ldap-testing' %}";
function error(message) {
toastr.error(message)
}
function success(message) {
toastr.success(message.msg)
}
APIUpdateAttr({
url: the_url,
body: JSON.stringify(data),
method: "POST",
flash_message: false,
success: success,
error: error
});
})
.on('click', '', function () {
})
</script>
{% endblock %}
...@@ -10,4 +10,5 @@ urlpatterns = [ ...@@ -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'),
] ]
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): ...@@ -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)
...@@ -373,7 +373,20 @@ CAPTCHA_FOREGROUND_COLOR = '#001100' ...@@ -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 = {
......
This diff is collapsed.
# -*- 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, \ ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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)
...@@ -2,9 +2,39 @@ from importlib import import_module ...@@ -2,9 +2,39 @@ from importlib import import_module
from django.conf import settings from django.conf import settings
from .command.serializers import SessionCommandSerializer from .command.serializers import SessionCommandSerializer
TYPE_ENGINE_MAPPING = {
'elasticsearch': 'terminal.backends.command.es',
}
def get_command_store(): def get_command_store():
command_engine = import_module(settings.COMMAND_STORAGE_BACKEND) params = settings.COMMAND_STORAGE
command_store = command_engine.CommandStore() engine_class = import_module(params['ENGINE'])
return command_store storage = engine_class.CommandStore(params)
return storage
def get_terminal_command_store():
storage_list = {}
for name, params in settings.TERMINAL_COMMAND_STORAGE.items():
tp = params['TYPE']
if tp == 'server':
storage = get_command_store()
else:
if not TYPE_ENGINE_MAPPING.get(tp):
raise AssertionError("Command storage type should in {}".format(
', '.join(TYPE_ENGINE_MAPPING.keys()))
)
engine_class = import_module(TYPE_ENGINE_MAPPING[tp])
storage = engine_class.CommandStore(params)
storage_list[name] = storage
return storage_list
def get_multi_command_store():
from .command.multi import CommandStore
storage_list = get_terminal_command_store().values()
storage = CommandStore(storage_list)
return storage
...@@ -19,3 +19,9 @@ class CommandBase(object): ...@@ -19,3 +19,9 @@ class CommandBase(object):
input=None, session=None): input=None, session=None):
pass pass
@abc.abstractmethod
def count(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
pass
...@@ -8,7 +8,7 @@ from .base import CommandBase ...@@ -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): ...@@ -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): ...@@ -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()
# -*- coding: utf-8 -*-
#
from jms_es_sdk import ESStore
from .base import CommandBase
class CommandStore(CommandBase, ESStore):
def __init__(self, params):
hosts = params.get('HOSTS', ['http://localhost'])
ESStore.__init__(self, hosts=hosts)
def save(self, command):
return ESStore.save(self, command)
def bulk_save(self, commands):
return ESStore.bulk_save(self, commands)
def filter(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
data = ESStore.filter(
self, date_from=date_from, date_to=date_to,
user=user, asset=asset, system_user=system_user,
input=input, session=session
)
return [item["_source"] for item in data["hits"] if item]
def count(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
amount = ESStore.count(
self, date_from=date_from, date_to=date_to,
user=user, asset=asset, system_user=system_user,
input=input, session=session
)
return amount
...@@ -18,5 +18,26 @@ class AbstractSessionCommand(models.Model): ...@@ -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
# -*- 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
\ No newline at end of file
...@@ -10,7 +10,7 @@ from .models import Terminal ...@@ -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"),
......
...@@ -4,17 +4,28 @@ import uuid ...@@ -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): ...@@ -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)
......
...@@ -5,7 +5,7 @@ from django.utils import timezone ...@@ -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): ...@@ -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):
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
{% bootstrap_field form.remote_addr layout="horizontal" %} {% bootstrap_field form.remote_addr layout="horizontal" %}
{% bootstrap_field form.ssh_port layout="horizontal" %} {% bootstrap_field form.ssh_port layout="horizontal" %}
{% bootstrap_field form.http_port layout="horizontal" %} {% bootstrap_field form.http_port layout="horizontal" %}
{% bootstrap_field form.command_storage layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %} {% bootstrap_field form.comment layout="horizontal" %}
</form> </form>
......
...@@ -35,6 +35,7 @@ ...@@ -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>
......
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from django import template from django import template
from ..backends import get_command_store from ..backends import get_multi_command_store
register = template.Library() register = template.Library()
command_store = get_command_store() command_store = get_multi_command_store()
@register.filter @register.filter
def get_session_command_amount(session_id): def get_session_command_amount(session_id):
return len(command_store.filter(session=str(session_id))) return command_store.count(session=session_id)
...@@ -20,7 +20,8 @@ urlpatterns = [ ...@@ -20,7 +20,8 @@ urlpatterns = [
url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$', url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$',
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}), api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
name='session-replay'), name='session-replay'),
url(r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key') url(r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key'),
url(r'^v1/terminal/config', api.TerminalConfig.as_view(), name='terminal-config'),
] ]
urlpatterns += router.urls urlpatterns += router.urls
...@@ -9,10 +9,10 @@ from django.utils.translation import ugettext as _ ...@@ -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): ...@@ -39,7 +39,7 @@ class CommandListView(DatetimeSearchMixin, ListView):
filter_kwargs['system_user'] = self.system_user filter_kwargs['system_user'] = self.system_user
if self.command: if self.command:
filter_kwargs['input'] = self.command filter_kwargs['input'] = self.command
queryset = command_store.filter(**filter_kwargs) queryset = common_storage.filter(**filter_kwargs)
return queryset return queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
......
...@@ -10,7 +10,7 @@ from django.conf import settings ...@@ -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__ = [ ...@@ -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):
......
...@@ -172,7 +172,6 @@ class UserBulkUpdateForm(forms.ModelForm): ...@@ -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', '')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment