diff --git a/apps/common/api.py b/apps/common/api.py index e09cf972605bc8697a66f59e65f78f4ff1186495..193132b7a9fb90b18a202fd6290f237d7b100cf4 100644 --- a/apps/common/api.py +++ b/apps/common/api.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- # + +import os import json +import jms_storage from rest_framework.views import Response, APIView from ldap3 import Server, Connection @@ -10,6 +13,7 @@ from django.conf import settings from .permissions import IsOrgAdmin from .serializers import MailTestSerializer, LDAPTestSerializer +from .models import Setting class MailTestingAPI(APIView): @@ -85,6 +89,78 @@ class LDAPTestingAPI(APIView): return Response({"error": str(serializer.errors)}, status=401) +class ReplayStorageCreateAPI(APIView): + permission_classes = (IsOrgAdmin,) + + def post(self, request): + storage_data = request.data + + if storage_data.get('TYPE') == 'ceph': + port = storage_data.get('PORT') + if port.isdigit(): + storage_data['PORT'] = int(storage_data.get('PORT')) + + storage_name = storage_data.pop('NAME') + data = {storage_name: storage_data} + + if not self.is_valid(storage_data): + return Response({"error": _("Error: Account invalid")}, status=401) + + Setting.save_storage('TERMINAL_REPLAY_STORAGE', data) + return Response({"msg": _('Create succeed')}, status=200) + + @staticmethod + def is_valid(storage_data): + if storage_data.get('TYPE') == 'server': + return True + storage = jms_storage.get_object_storage(storage_data) + target = 'tests.py' + src = os.path.join(settings.BASE_DIR, 'common', target) + ok, msg = storage.upload(src=src, target=target) + if not ok: + return False + storage.delete(path=target) + return True + + +class ReplayStorageDeleteAPI(APIView): + + def post(self, request): + storage_name = str(request.data.get('name')) + Setting.delete_storage('TERMINAL_REPLAY_STORAGE', storage_name) + return Response({"msg": _('Delete succeed')}, status=200) + + +class CommandStorageCreateAPI(APIView): + permission_classes = (IsOrgAdmin,) + + def post(self, request): + storage_data = request.data + storage_name = storage_data.pop('NAME') + data = {storage_name: storage_data} + if not self.is_valid(storage_data): + return Response({"error": _("Error: Account invalid")}, status=401) + + Setting.save_storage('TERMINAL_COMMAND_STORAGE', data) + return Response({"msg": _('Create succeed')}, status=200) + + @staticmethod + def is_valid(storage_data): + if storage_data.get('TYPE') == 'server': + return True + storage = jms_storage.get_log_storage(storage_data) + return storage.ping() + + +class CommandStorageDeleteAPI(APIView): + permission_classes = (IsOrgAdmin,) + + def post(self, request): + storage_name = str(request.data.get('name')) + Setting.delete_storage('TERMINAL_COMMAND_STORAGE', storage_name) + return Response({"msg": _('Delete succeed')}, status=200) + + class DjangoSettingsAPI(APIView): def get(self, request): if not settings.DEBUG: diff --git a/apps/common/forms.py b/apps/common/forms.py index 610e1a83e856c7a925a7fb11f8792dda35da1993..491ae779a2e47c92119ddbc31d7ff1b313da1cff 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -135,30 +135,34 @@ class TerminalSettingForm(BaseForm): ('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 = FormEncryptDictField( - label=_("Command storage"), help_text=_( - "Set terminal storage setting, `default` is the using as default," - "You can set other storage and some terminal using" - ) + TERMINAL_HEARTBEAT_INTERVAL = forms.IntegerField( + initial=5, label=_("Heartbeat interval"), help_text=_("Units: seconds") ) - TERMINAL_REPLAY_STORAGE = FormEncryptDictField( - label=_("Replay storage"), help_text=_( - "Set replay storage setting, `default` is the using as default," - "You can set other storage and some terminal using" - ) + TERMINAL_ASSET_LIST_SORT_BY = forms.ChoiceField( + choices=SORT_BY_CHOICES, initial='hostname', label=_("List sort by") ) + # TERMINAL_COMMAND_STORAGE = FormEncryptDictField( + # label=_("Command storage"), help_text=_( + # "Set terminal storage setting, `default` is the using as default," + # "You can set other storage and some terminal using" + # ) + # ) + # TERMINAL_REPLAY_STORAGE = FormEncryptDictField( + # label=_("Replay storage"), help_text=_( + # "Set replay storage setting, `default` is the using as default," + # "You can set other storage and some terminal using" + # ) + # ) + + +class TerminalCommandStorage(BaseForm): + pass class SecuritySettingForm(BaseForm): diff --git a/apps/common/models.py b/apps/common/models.py index 61f5512c952f57fc8bbc4b14413453b6cd298a61..812d491a9a81ce871acaa5791a60b6d10ccb2745 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -67,6 +67,30 @@ class Setting(models.Model): except json.JSONDecodeError as e: raise ValueError("Json dump error: {}".format(str(e))) + @classmethod + def save_storage(cls, name, data): + obj = cls.objects.filter(name=name).first() + if not obj: + obj = cls() + obj.name = name + obj.encrypted = True + obj.cleaned_value = data + else: + value = obj.cleaned_value + value.update(data) + obj.cleaned_value = value + obj.save() + return obj + + @classmethod + def delete_storage(cls, name, storage_name): + obj = cls.objects.get(name=name) + value = obj.cleaned_value + value.pop(storage_name, '') + obj.cleaned_value = value + obj.save() + return True + @classmethod def refresh_all_settings(cls): try: diff --git a/apps/common/templates/common/command_storage_create.html b/apps/common/templates/common/command_storage_create.html new file mode 100644 index 0000000000000000000000000000000000000000..0f631161faee41aeb40e399313712a4da738aca9 --- /dev/null +++ b/apps/common/templates/common/command_storage_create.html @@ -0,0 +1,177 @@ +{#{% extends 'base.html' %}#} +{% extends '_base_create_update.html' %} +{% load static %} +{% load bootstrap3 %} +{% load i18n %} +{% load common_tags %} + +{% block content %} +
+
+
+
+
+
{{ action }}
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
* required
+
+
+
+ + + +{# #} + + + + + +
+
+
+ + {% trans 'Submit' %} +
+
+
+
+
+
+
+
+{% endblock %} + +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/common/templates/common/replay_storage_create.html b/apps/common/templates/common/replay_storage_create.html new file mode 100644 index 0000000000000000000000000000000000000000..d717a508db9d97ecc56ed940932b1a375d6ff69e --- /dev/null +++ b/apps/common/templates/common/replay_storage_create.html @@ -0,0 +1,242 @@ +{#{% extends 'base.html' %}#} +{% extends '_base_create_update.html' %} +{% load static %} +{% load bootstrap3 %} +{% load i18n %} +{% load common_tags %} + +{% block content %} +
+
+
+
+
+
{{ action }}
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ +
* required
+{# #} +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + {% trans 'Submit' %} +
+
+
+
+
+
+
+
+{% endblock %} + +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/common/templates/common/terminal_setting.html b/apps/common/templates/common/terminal_setting.html index 320f628b01d58b843858d23f85d9e59b3587928e..f75dea79c84da2de71b76937b4faa789ac004aeb 100644 --- a/apps/common/templates/common/terminal_setting.html +++ b/apps/common/templates/common/terminal_setting.html @@ -63,6 +63,14 @@ {% endif %} {% endfor %} +
+
+ + +
+
+

{% trans "Command storage" %}

@@ -71,6 +79,7 @@ {% trans 'Name' %} {% trans 'Type' %} + {% trans 'Action' %} @@ -78,10 +87,13 @@ {{ name }} {{ setting.TYPE }} + {% trans 'Delete' %} {% endfor %} + {% trans 'Add' %} +

{% trans "Replay storage" %}

@@ -89,6 +101,7 @@ + @@ -96,18 +109,14 @@ + {% endfor %}
{% trans 'Name' %} {% trans 'Type' %}{% trans 'Action' %}
{{ name }} {{ setting.TYPE }}{% trans 'Delete' %}
+ {% trans 'Add' %} +
-
-
- - -
-
@@ -116,40 +125,63 @@ - {% endblock %} {% block custom_foot_js %} - + {% endblock %} diff --git a/apps/common/urls/api_urls.py b/apps/common/urls/api_urls.py index 5b44684ec4cb7b61bef6318ad523386c396e5181..3c86a3a2a8366f08381bddbd88278bc974a9a4c1 100644 --- a/apps/common/urls/api_urls.py +++ b/apps/common/urls/api_urls.py @@ -9,5 +9,9 @@ app_name = 'common' urlpatterns = [ path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'), path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'), + path('terminal/replay-storage/create/', api.ReplayStorageCreateAPI.as_view(), name='replay-storage-create'), + path('terminal/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'), + path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'), + path('terminal/command-storage/delete/', api.CommandStorageDeleteAPI.as_view(), name='command-storage-delete'), # path('django-settings/', api.DjangoSettingsAPI.as_view(), name='django-settings'), ] diff --git a/apps/common/urls/view_urls.py b/apps/common/urls/view_urls.py index e7ccddd064216dd22f0f64502823d49e33986870..8c2b91297a98a25a5ed6039d65d2f16994a9d046 100644 --- a/apps/common/urls/view_urls.py +++ b/apps/common/urls/view_urls.py @@ -11,5 +11,7 @@ urlpatterns = [ url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'), url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'), url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'), + url(r'^terminal/replay-storage/create$', views.ReplayStorageCreateView.as_view(), name='replay-storage-create'), + url(r'^terminal/command-storage/create$', views.CommandStorageCreateView.as_view(), name='command-storage-create'), url(r'^security/$', views.SecuritySettingView.as_view(), name='security-setting'), ] diff --git a/apps/common/utils.py b/apps/common/utils.py index ec55f43a168009e007d400d08b1125e1c1d7e6bb..e8476194f67f413296d0ed931bc0ce1121510736 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -387,6 +387,49 @@ def get_request_ip(request): return login_ip +def get_command_storage_or_create_default_storage(): + from common.models import common_settings, Setting + name = 'TERMINAL_COMMAND_STORAGE' + default = {'default': {'TYPE': 'server'}} + command_storage = common_settings.TERMINAL_COMMAND_STORAGE + if command_storage is None: + obj = Setting() + obj.name = name + obj.encrypted = True + obj.cleaned_value = default + obj.save() + if isinstance(command_storage, dict) and not command_storage: + obj = Setting.objects.get(name=name) + value = obj.cleaned_value + value.update(default) + obj.cleaned_value = value + obj.save() + command_storage = common_settings.TERMINAL_COMMAND_STORAGE + return command_storage + + +def get_replay_storage_or_create_default_storage(): + from common.models import common_settings, Setting + name = 'TERMINAL_REPLAY_STORAGE' + default = {'default': {'TYPE': 'server'}} + replay_storage = common_settings.TERMINAL_REPLAY_STORAGE + if replay_storage is None: + obj = Setting() + obj.name = name + obj.encrypted = True + obj.cleaned_value = default + obj.save() + replay_storage = common_settings.TERMINAL_REPLAY_STORAGE + if isinstance(replay_storage, dict) and not replay_storage: + obj = Setting.objects.get(name=name) + value = obj.cleaned_value + value.update(default) + obj.cleaned_value = value + obj.save() + replay_storage = common_settings.TERMINAL_REPLAY_STORAGE + return replay_storage + + class TeeObj: origin_stdout = sys.stdout diff --git a/apps/common/views.py b/apps/common/views.py index 08c4828f912d936b2c0c9aa4900373d1da003cb3..04d844c04051f65fe690449515e2f8649468dbd5 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -4,10 +4,12 @@ from django.contrib import messages from django.utils.translation import ugettext as _ from django.conf import settings +from common.models import common_settings from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \ TerminalSettingForm, SecuritySettingForm from common.permissions import SuperUserRequiredMixin from .signals import ldap_auth_enable +from . import utils class BasicSettingView(SuperUserRequiredMixin, TemplateView): @@ -95,14 +97,15 @@ class TerminalSettingView(SuperUserRequiredMixin, TemplateView): template_name = "common/terminal_setting.html" def get_context_data(self, **kwargs): - command_storage = settings.TERMINAL_COMMAND_STORAGE - replay_storage = settings.TERMINAL_REPLAY_STORAGE + command_storage = utils.get_command_storage_or_create_default_storage() + replay_storage = utils.get_replay_storage_or_create_default_storage() + context = { 'app': _('Settings'), 'action': _('Terminal setting'), 'form': self.form_class(), 'replay_storage': replay_storage, - 'command_storage': command_storage, + 'command_storage': command_storage } kwargs.update(context) return super().get_context_data(**kwargs) @@ -120,6 +123,30 @@ class TerminalSettingView(SuperUserRequiredMixin, TemplateView): return render(request, self.template_name, context) +class ReplayStorageCreateView(SuperUserRequiredMixin, TemplateView): + template_name = 'common/replay_storage_create.html' + + def get_context_data(self, **kwargs): + context = { + 'app': _('Settings'), + 'action': _('Create replay storage') + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class CommandStorageCreateView(SuperUserRequiredMixin, TemplateView): + template_name = 'common/command_storage_create.html' + + def get_context_data(self, **kwargs): + context = { + 'app': _('Settings'), + 'action': _('Create command storage') + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + class SecuritySettingView(SuperUserRequiredMixin, TemplateView): form_class = SecuritySettingForm template_name = "common/security_setting.html" diff --git a/apps/templates/_base_create_update.html b/apps/templates/_base_create_update.html index ec14da79b0efccc4e5c991808992c8f799b04d43..be3813804a7278c1baa5991f6ee6c707cacc8023 100644 --- a/apps/templates/_base_create_update.html +++ b/apps/templates/_base_create_update.html @@ -31,8 +31,8 @@
{% if form.errors.all %}
- {{ form.errors.all }} -
+ {{ form.errors.all }} +
{% endif %} {% block form %} {% endblock %} diff --git a/apps/terminal/backends/__init__.py b/apps/terminal/backends/__init__.py index 9a1c338f51c0bca39c1e21ec40ff53958be23500..1c454a32d7e22b0b75ab9755d8ae9ad1df9074c7 100644 --- a/apps/terminal/backends/__init__.py +++ b/apps/terminal/backends/__init__.py @@ -2,6 +2,9 @@ from importlib import import_module from django.conf import settings from .command.serializers import SessionCommandSerializer +from common import utils +from common.models import common_settings, Setting + TYPE_ENGINE_MAPPING = { 'elasticsearch': 'terminal.backends.command.es', } @@ -16,7 +19,9 @@ def get_command_storage(): def get_terminal_command_storages(): storage_list = {} - for name, params in settings.TERMINAL_COMMAND_STORAGE.items(): + command_storage = utils.get_command_storage_or_create_default_storage() + + for name, params in command_storage.items(): tp = params['TYPE'] if tp == 'server': storage = get_command_storage() diff --git a/apps/terminal/forms.py b/apps/terminal/forms.py index dbba3cb01c5e29c30a628bd92a15ad1ab0269947..55997a31b00f53bf91f886368ce0a18646505cfa 100644 --- a/apps/terminal/forms.py +++ b/apps/terminal/forms.py @@ -2,36 +2,39 @@ # from django import forms -from django.conf import settings from django.utils.translation import ugettext_lazy as _ from .models import Terminal def get_all_command_storage(): - # storage_choices = [] - from common.models import Setting - Setting.refresh_all_settings() - for k, v in settings.TERMINAL_COMMAND_STORAGE.items(): - yield (k, k) + from common import utils + command_storage = utils.get_command_storage_or_create_default_storage() + command_storage_choice = [] + for k, v in command_storage.items(): + command_storage_choice.append((k, k)) + + return command_storage_choice def get_all_replay_storage(): - # storage_choices = [] - from common.models import Setting - Setting.refresh_all_settings() - for k, v in settings.TERMINAL_REPLAY_STORAGE.items(): - yield (k, k) + from common import utils + replay_storage = utils.get_replay_storage_or_create_default_storage() + replay_storage_choice = [] + for k, v in replay_storage.items(): + replay_storage_choice.append((k, k)) + + return replay_storage_choice class TerminalForm(forms.ModelForm): command_storage = forms.ChoiceField( - choices=get_all_command_storage(), + choices=get_all_command_storage, label=_("Command storage"), help_text=_("Command can store in server db or ES, default to server, more see docs"), ) replay_storage = forms.ChoiceField( - choices=get_all_replay_storage(), + choices=get_all_replay_storage, label=_("Replay storage"), help_text=_("Replay file can store in server disk, AWS S3, Aliyun OSS, default to server, more see docs"), )