diff --git a/apps/applications/api/__init__.py b/apps/applications/api/__init__.py index e6bc7adb4..a707cfde6 100644 --- a/apps/applications/api/__init__.py +++ b/apps/applications/api/__init__.py @@ -1 +1,2 @@ from .remote_app import * +from .database_app import * diff --git a/apps/applications/api/database_app.py b/apps/applications/api/database_app.py new file mode 100644 index 000000000..8f5f704ef --- /dev/null +++ b/apps/applications/api/database_app.py @@ -0,0 +1,21 @@ +# coding: utf-8 +# + +from orgs.mixins.api import OrgBulkModelViewSet +from orgs.mixins import generics + +from .. import models +from .. import serializers +from ..hands import IsOrgAdmin, IsAppUser + +__all__ = [ + 'DatabaseAppViewSet', +] + + +class DatabaseAppViewSet(OrgBulkModelViewSet): + model = models.DatabaseApp + filter_fields = ('name',) + search_fields = filter_fields + permission_classes = (IsOrgAdmin,) + serializer_class = serializers.DatabaseAppSerializer diff --git a/apps/applications/const.py b/apps/applications/const.py index fe5a26945..af3531c36 100644 --- a/apps/applications/const.py +++ b/apps/applications/const.py @@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _ # RemoteApp + REMOTE_APP_BOOT_PROGRAM_NAME = '||jmservisor' REMOTE_APP_TYPE_CHROME = 'chrome' @@ -50,3 +51,13 @@ REMOTE_APP_TYPE_CHOICES = ( (REMOTE_APP_TYPE_VMWARE_CLIENT, 'vSphere Client'), (REMOTE_APP_TYPE_CUSTOM, _('Custom')), ) + + +# DatabaseApp + + +DATABASE_APP_TYPE_MYSQL = 'mysql' + +DATABASE_APP_TYPE_CHOICES = ( + (DATABASE_APP_TYPE_MYSQL, 'MySQL'), +) diff --git a/apps/applications/forms/__init__.py b/apps/applications/forms/__init__.py index e6bc7adb4..a707cfde6 100644 --- a/apps/applications/forms/__init__.py +++ b/apps/applications/forms/__init__.py @@ -1 +1,2 @@ from .remote_app import * +from .database_app import * diff --git a/apps/applications/forms/database_app.py b/apps/applications/forms/database_app.py new file mode 100644 index 000000000..2b5a4c0cf --- /dev/null +++ b/apps/applications/forms/database_app.py @@ -0,0 +1,26 @@ +# coding: utf-8 +# + + +from django import forms +from django.utils.translation import ugettext_lazy as _ + +from .. import models + +__all__ = ['DatabaseAppMySQLForm'] + + +class BaseDatabaseAppForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['type'].widget.attrs['disabled'] = True + + class Meta: + model = models.DatabaseApp + fields = [ + 'name', 'type', 'host', 'port', 'database', 'comment' + ] + + +class DatabaseAppMySQLForm(BaseDatabaseAppForm): + pass diff --git a/apps/applications/migrations/0004_auto_20191218_1705.py b/apps/applications/migrations/0004_auto_20191218_1705.py new file mode 100644 index 000000000..f22d2e290 --- /dev/null +++ b/apps/applications/migrations/0004_auto_20191218_1705.py @@ -0,0 +1,38 @@ +# Generated by Django 2.1.11 on 2019-12-18 09:05 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('applications', '0003_auto_20191210_1659'), + ] + + operations = [ + migrations.CreateModel( + name='DatabaseApp', + fields=[ + ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), + ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128, verbose_name='Name')), + ('type', models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=128, verbose_name='Type')), + ('host', models.CharField(db_index=True, max_length=128, verbose_name='Host')), + ('port', models.IntegerField(default=3306, verbose_name='Port')), + ('database', models.CharField(blank=True, db_index=True, max_length=128, null=True, verbose_name='Database')), + ('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')), + ], + options={ + 'verbose_name': 'DatabaseApp', + 'ordering': ('name',), + }, + ), + migrations.AlterUniqueTogether( + name='databaseapp', + unique_together={('org_id', 'name')}, + ), + ] diff --git a/apps/applications/models/__init__.py b/apps/applications/models/__init__.py index e6bc7adb4..a707cfde6 100644 --- a/apps/applications/models/__init__.py +++ b/apps/applications/models/__init__.py @@ -1 +1,2 @@ from .remote_app import * +from .database_app import * diff --git a/apps/applications/models/database_app.py b/apps/applications/models/database_app.py new file mode 100644 index 000000000..3317f06a4 --- /dev/null +++ b/apps/applications/models/database_app.py @@ -0,0 +1,42 @@ +# coding: utf-8 +# + +import uuid +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orgs.mixins.models import OrgModelMixin +from common.mixins import CommonModelMixin +from .. import const + + +__all__ = ['DatabaseApp'] + + +class DatabaseApp(CommonModelMixin, OrgModelMixin): + id = models.UUIDField(default=uuid.uuid4, primary_key=True) + name = models.CharField(max_length=128, verbose_name=_('Name')) + type = models.CharField( + default=const.DATABASE_APP_TYPE_MYSQL, + choices=const.DATABASE_APP_TYPE_CHOICES, + max_length=128, verbose_name=_('Type') + ) + host = models.CharField( + max_length=128, verbose_name=_('Host'), db_index=True + ) + port = models.IntegerField(default=3306, verbose_name=_('Port')) + database = models.CharField( + max_length=128, blank=True, null=True, verbose_name=_('Database'), + db_index=True + ) + comment = models.TextField( + max_length=128, default='', blank=True, verbose_name=_('Comment') + ) + + def __str__(self): + return self.name + + class Meta: + unique_together = [('org_id', 'name'), ] + verbose_name = _("DatabaseApp") + ordering = ('name', ) diff --git a/apps/applications/serializers/__init__.py b/apps/applications/serializers/__init__.py index e6bc7adb4..a707cfde6 100644 --- a/apps/applications/serializers/__init__.py +++ b/apps/applications/serializers/__init__.py @@ -1 +1,2 @@ from .remote_app import * +from .database_app import * diff --git a/apps/applications/serializers/database_app.py b/apps/applications/serializers/database_app.py new file mode 100644 index 000000000..43b873193 --- /dev/null +++ b/apps/applications/serializers/database_app.py @@ -0,0 +1,26 @@ +# coding: utf-8 +# + +from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from common.serializers import AdaptedBulkListSerializer + +from .. import models + +__all__ = [ + 'DatabaseAppSerializer', +] + + +class DatabaseAppSerializer(BulkOrgResourceModelSerializer): + + class Meta: + model = models.DatabaseApp + list_serializer_class = AdaptedBulkListSerializer + fields = [ + 'id', 'name', 'type', 'get_type_display', 'host', 'port', + 'database', 'comment', 'created_by', 'date_created', 'date_updated', + ] + read_only_fields = [ + 'created_by', 'date_created', 'date_updated' + 'get_type_display', + ] diff --git a/apps/applications/templates/applications/database_app_create_update.html b/apps/applications/templates/applications/database_app_create_update.html new file mode 100644 index 000000000..84635e2d0 --- /dev/null +++ b/apps/applications/templates/applications/database_app_create_update.html @@ -0,0 +1,55 @@ +{% extends '_base_create_update.html' %} +{% load static %} +{% load bootstrap3 %} +{% load i18n %} + +{% block form %} +
+ {% bootstrap_form form layout="horizontal" %} +
+
+
+ + +
+
+
+{% endblock %} + +{% block custom_foot_js %} + +{% endblock %} \ No newline at end of file diff --git a/apps/applications/templates/applications/database_app_detail.html b/apps/applications/templates/applications/database_app_detail.html new file mode 100644 index 000000000..153bfa98a --- /dev/null +++ b/apps/applications/templates/applications/database_app_detail.html @@ -0,0 +1,103 @@ +{% extends 'base.html' %} +{% load static %} +{% load i18n %} + +{% block content %} +
+
+
+
+ +
+
+
+
+ {{ database_app.name }} +
+ + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{% trans 'Name' %}:{{ database_app.name }}
{% trans 'Type' %}:{{ database_app.get_type_display }}
{% trans 'Host' %}:{{ database_app.host }}
{% trans 'Port' %}:{{ database_app.port }}
{% trans 'Database' %}:{{ database_app.database }}
{% trans 'Date created' %}:{{ database_app.date_created }}
{% trans 'Created by' %}:{{ database_app.created_by }}
{% trans 'Comment' %}:{{ database_app.comment }}
+
+
+
+
+
+
+
+
+{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/applications/templates/applications/database_app_list.html b/apps/applications/templates/applications/database_app_list.html new file mode 100644 index 000000000..ebe14f9b5 --- /dev/null +++ b/apps/applications/templates/applications/database_app_list.html @@ -0,0 +1,89 @@ +{% extends '_base_list.html' %} +{% load i18n static %} +{% block help_message %} +{% endblock %} +{% block table_search %}{% endblock %} +{% block table_container %} +
+ + + +
+ + + + + + + + + + + + + + + +
+ + {% trans 'Name' %}{% trans 'Type' %}{% trans 'Host' %}{% trans 'Port' %}{% trans 'Database' %}{% trans 'Comment' %}{% trans 'Action' %}
+{% endblock %} +{% block content_bottom_left %}{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/applications/templates/applications/remote_app_list.html b/apps/applications/templates/applications/remote_app_list.html index bbbafd3ba..cb406c805 100644 --- a/apps/applications/templates/applications/remote_app_list.html +++ b/apps/applications/templates/applications/remote_app_list.html @@ -69,7 +69,7 @@ function initTable() { {data: "get_type_display", orderable: false}, {data: "asset_info", orderable: false}, {data: "comment"}, - {data: "id", orderable: false} + {data: "id", orderable: false, width: "100px"} ], op_html: $('#actions').html() }; diff --git a/apps/applications/templates/applications/user_database_app_list.html b/apps/applications/templates/applications/user_database_app_list.html new file mode 100644 index 000000000..1edaacd76 --- /dev/null +++ b/apps/applications/templates/applications/user_database_app_list.html @@ -0,0 +1,83 @@ +{% extends 'base.html' %} +{% load i18n static %} + +{% block custom_head_css_js %} + +{% endblock %} + +{% block content %} +
+ + + + + + + + + + + + + + +
+ + {% trans 'Name' %}{% trans 'Type' %}{% trans 'Host' %}{% trans 'Database' %}{% trans 'Comment' %}{% trans 'Action' %}
+
+{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/applications/urls/api_urls.py b/apps/applications/urls/api_urls.py index 6384f5dac..1186bf1a2 100644 --- a/apps/applications/urls/api_urls.py +++ b/apps/applications/urls/api_urls.py @@ -11,10 +11,12 @@ app_name = 'applications' router = BulkRouter() router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app') +router.register(r'database-apps', api.DatabaseAppViewSet, 'database-app') urlpatterns = [ path('remote-apps//connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'), ] + old_version_urlpatterns = [ re_path('(?Premote-app)/.*', capi.redirect_plural_name_api) ] diff --git a/apps/applications/urls/views_urls.py b/apps/applications/urls/views_urls.py index 3ffcffc5c..663b0878d 100644 --- a/apps/applications/urls/views_urls.py +++ b/apps/applications/urls/views_urls.py @@ -11,6 +11,13 @@ urlpatterns = [ path('remote-app//update/', views.RemoteAppUpdateView.as_view(), name='remote-app-update'), path('remote-app//', views.RemoteAppDetailView.as_view(), name='remote-app-detail'), # User RemoteApp view - path('user-remote-app/', views.UserRemoteAppListView.as_view(), name='user-remote-app-list') + path('user-remote-app/', views.UserRemoteAppListView.as_view(), name='user-remote-app-list'), + + path('database-app/', views.DatabaseAppListView.as_view(), name='database-app-list'), + path('database-app/create/', views.DatabaseAppCreateView.as_view(), name='database-app-create'), + path('database-app//update/', views.DatabaseAppUpdateView.as_view(), name='database-app-update'), + path('database-app//', views.DatabaseAppDetailView.as_view(), name='database-app-detail'), + # User DatabaseApp view + path('user-database-app/', views.UserDatabaseAppListView.as_view(), name='user-database-app-list'), ] diff --git a/apps/applications/views/__init__.py b/apps/applications/views/__init__.py index e6bc7adb4..a707cfde6 100644 --- a/apps/applications/views/__init__.py +++ b/apps/applications/views/__init__.py @@ -1 +1,2 @@ from .remote_app import * +from .database_app import * diff --git a/apps/applications/views/database_app.py b/apps/applications/views/database_app.py new file mode 100644 index 000000000..21d2b4f7c --- /dev/null +++ b/apps/applications/views/database_app.py @@ -0,0 +1,115 @@ +# coding: utf-8 +# + +from django.http import Http404 +from django.views.generic import TemplateView +from django.views.generic.edit import CreateView, UpdateView +from django.utils.translation import ugettext_lazy as _ +from django.views.generic.detail import DetailView + +from common.permissions import PermissionsMixin, IsOrgAdmin, IsValidUser + +from .. import models, const, forms + +__all__ = [ + 'DatabaseAppListView', 'DatabaseAppCreateView', 'DatabaseAppUpdateView', + 'DatabaseAppDetailView', 'UserDatabaseAppListView', +] + + +class DatabaseAppListView(PermissionsMixin, TemplateView): + template_name = 'applications/database_app_list.html' + permission_classes = [IsOrgAdmin] + + def get_context_data(self, **kwargs): + context = { + 'app': _("Application"), + 'action': _('DatabaseApp list'), + 'type_choices': const.DATABASE_APP_TYPE_CHOICES + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class BaseDatabaseAppCreateUpdateView: + template_name = 'applications/database_app_create_update.html' + model = models.DatabaseApp + permission_classes = [IsOrgAdmin] + default_type = const.DATABASE_APP_TYPE_MYSQL + form_class = forms.DatabaseAppMySQLForm + form_class_choices = { + const.DATABASE_APP_TYPE_MYSQL: forms.DatabaseAppMySQLForm, + } + + def get_initial(self): + return {'type': self.get_type()} + + def get_type(self): + return self.default_type + + def get_form_class(self): + tp = self.get_type() + form_class = self.form_class_choices.get(tp) + if not form_class: + raise Http404() + return form_class + + +class DatabaseAppCreateView(BaseDatabaseAppCreateUpdateView, CreateView): + + def get_type(self): + tp = self.request.GET.get("type") + if tp: + return tp.lower() + return super().get_type() + + def get_context_data(self, **kwargs): + context = { + 'app': _('Applications'), + 'action': _('Create DatabaseApp'), + 'api_action': 'create' + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class DatabaseAppUpdateView(BaseDatabaseAppCreateUpdateView, UpdateView): + + def get_type(self): + return self.object.type + + def get_context_data(self, **kwargs): + context = { + 'app': _('Applications'), + 'action': _('Create DatabaseApp'), + 'api_action': 'update' + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class DatabaseAppDetailView(PermissionsMixin, DetailView): + template_name = 'applications/database_app_detail.html' + model = models.DatabaseApp + context_object_name = 'database_app' + permission_classes = [IsOrgAdmin] + + def get_context_data(self, **kwargs): + context = { + 'app': _('Applications'), + 'action': _('DatabaseApp detail'), + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class UserDatabaseAppListView(PermissionsMixin, TemplateView): + template_name = 'applications/user_database_app_list.html' + permission_classes = [IsValidUser] + + def get_context_data(self, **kwargs): + context = { + 'action': _('My DatabaseApp'), + } + kwargs.update(context) + return super().get_context_data(**kwargs) diff --git a/apps/assets/migrations/0046_auto_20191218_1705.py b/apps/assets/migrations/0046_auto_20191218_1705.py new file mode 100644 index 000000000..af776eee2 --- /dev/null +++ b/apps/assets/migrations/0046_auto_20191218_1705.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.11 on 2019-12-18 09:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0045_auto_20191206_1607'), + ] + + operations = [ + migrations.AlterField( + model_name='systemuser', + name='protocol', + field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet'), ('vnc', 'vnc'), ('mysql', 'mysql')], default='ssh', max_length=16, verbose_name='Protocol'), + ), + ] diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index dd2504f00..d3e3087ec 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -92,11 +92,13 @@ class SystemUser(AssetUser): PROTOCOL_RDP = 'rdp' PROTOCOL_TELNET = 'telnet' PROTOCOL_VNC = 'vnc' + PROTOCOL_MYSQL = 'mysql' PROTOCOL_CHOICES = ( (PROTOCOL_SSH, 'ssh'), (PROTOCOL_RDP, 'rdp'), (PROTOCOL_TELNET, 'telnet'), (PROTOCOL_VNC, 'vnc'), + (PROTOCOL_MYSQL, 'mysql'), ) LOGIN_AUTO = 'auto' @@ -133,6 +135,18 @@ class SystemUser(AssetUser): else: return False + @property + def is_need_cmd_filter(self): + return self.protocol not in [self.PROTOCOL_RDP, self.PROTOCOL_MYSQL] + + @property + def is_need_test_asset_connective(self): + return self.protocol not in [self.PROTOCOL_MYSQL] + + @property + def can_perm_to_asset(self): + return self.protocol not in [self.PROTOCOL_MYSQL] + @property def cmd_filter_rules(self): from .cmd_filter import CommandFilterRule diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 2eedc15d8..79cc58897 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -103,8 +103,12 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): def validate(self, attrs): username = attrs.get("username", "manual") + auto_gen_key = attrs.pop("auto_generate_key", False) protocol = attrs.get("protocol") - auto_gen_key = attrs.get("auto_generate_key", False) + + if protocol not in [SystemUser.PROTOCOL_RDP, SystemUser.PROTOCOL_SSH]: + return attrs + if auto_gen_key: password = SystemUser.gen_password() attrs["password"] = password @@ -119,7 +123,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): public_key = ssh_pubkey_gen(private_key, password=password, username=username) attrs["public_key"] = public_key - attrs.pop("auto_generate_key", None) return attrs @classmethod diff --git a/apps/assets/templates/assets/_system_user.html b/apps/assets/templates/assets/_system_user.html index 3d1223ea2..c3cba50e7 100644 --- a/apps/assets/templates/assets/_system_user.html +++ b/apps/assets/templates/assets/_system_user.html @@ -95,7 +95,7 @@ function autoLoginModeProtocol() { // 协议+自动登录模式字段控制 $('#auth_title_id').removeClass('hidden'); var protocol = $(protocol_id + " option:selected").text(); - if (protocol === 'rdp') { + if (['rdp'].indexOf(protocol) !== -1) { authFieldsDisplay(); $(auto_generate_key).closest('.form-group').removeClass('hidden'); $(private_key_id).closest('.form-group').addClass('hidden'); @@ -105,7 +105,7 @@ function autoLoginModeProtocol() { $(sudo_id).closest('.form-group').addClass('hidden'); $(shell_id).closest('.form-group').addClass('hidden'); } - else if (protocol === 'vnc') { + else if (['vnc', 'mysql'].indexOf(protocol) !== -1) { $('.auth-fields').removeClass('hidden'); $(auto_generate_key).closest('.form-group').addClass('hidden'); $(private_key_id).closest('.form-group').addClass('hidden'); @@ -141,7 +141,7 @@ function manualLoginModeProtocol() { // 协议+手动登录模式字段控制 $('#auth_title_id').addClass('hidden'); var protocol = $(protocol_id + " option:selected").text(); - if (protocol === 'rdp') { + if (['rdp'].indexOf(protocol) !== -1) { $('.auth-fields').addClass('hidden'); $(auto_generate_key).closest('.form-group').addClass('hidden'); $(password_id).closest('.form-group').addClass('hidden'); @@ -151,7 +151,7 @@ function manualLoginModeProtocol() { $(sudo_id).closest('.form-group').addClass('hidden'); $(shell_id).closest('.form-group').addClass('hidden'); } - else if (protocol === 'vnc') { + else if (['vnc', 'mysql'].indexOf(protocol) !== -1) { $('.auth-fields').addClass('hidden'); $(auto_generate_key).closest('.form-group').addClass('hidden'); $(password_id).closest('.form-group').addClass('hidden'); diff --git a/apps/assets/templates/assets/system_user_detail.html b/apps/assets/templates/assets/system_user_detail.html index 8e05cf604..b9d6e5c0b 100644 --- a/apps/assets/templates/assets/system_user_detail.html +++ b/apps/assets/templates/assets/system_user_detail.html @@ -12,11 +12,13 @@
  • {% trans 'Detail' %}
  • + {% if system_user.can_perm_to_asset %}
  • {% trans 'Assets' %}
  • + {% endif %}
  • {% trans 'Update' %}
  • @@ -139,6 +141,7 @@ {% endif %} + {% if system_user.is_need_test_asset_connective %} {% trans 'Test assets connective' %}: @@ -147,13 +150,14 @@ + {% endif %} - {% if system_user.protocol != 'rdp' %} + {% if system_user.is_need_cmd_filter %}
    diff --git a/apps/perms/api/__init__.py b/apps/perms/api/__init__.py index cc6ce8229..61cbd7d58 100644 --- a/apps/perms/api/__init__.py +++ b/apps/perms/api/__init__.py @@ -7,3 +7,6 @@ from .asset_permission_relation import * from .user_group_permission import * from .remote_app_permission import * from .user_remote_app_permission import * +from .database_app_permission import * +from .database_app_permission_relation import * +from .user_database_app_permission import * diff --git a/apps/perms/api/database_app_permission.py b/apps/perms/api/database_app_permission.py new file mode 100644 index 000000000..c7ef54fb3 --- /dev/null +++ b/apps/perms/api/database_app_permission.py @@ -0,0 +1,18 @@ +# coding: utf-8 +# + +from orgs.mixins.api import OrgBulkModelViewSet + +from .. import models, serializers +from common.permissions import IsOrgAdmin + + +__all__ = ['DatabaseAppPermissionViewSet'] + + +class DatabaseAppPermissionViewSet(OrgBulkModelViewSet): + model = models.DatabaseAppPermission + serializer_class = serializers.DatabaseAppPermissionSerializer + filter_fields = ('name',) + search_fields = filter_fields + permission_classes = (IsOrgAdmin,) diff --git a/apps/perms/api/database_app_permission_relation.py b/apps/perms/api/database_app_permission_relation.py new file mode 100644 index 000000000..32ab34355 --- /dev/null +++ b/apps/perms/api/database_app_permission_relation.py @@ -0,0 +1,132 @@ +# coding: utf-8 +# + +from rest_framework import generics +from django.db.models import F, Value +from django.db.models.functions import Concat +from django.shortcuts import get_object_or_404 + +from orgs.mixins.api import OrgBulkModelViewSet +from orgs.utils import current_org +from common.permissions import IsOrgAdmin +from .. import models, serializers + +__all__ = [ + 'DatabaseAppPermissionUserRelationViewSet', + 'DatabaseAppPermissionUserGroupRelationViewSet', + 'DatabaseAppPermissionAllUserListApi', + 'DatabaseAppPermissionDatabaseAppRelationViewSet', + 'DatabaseAppPermissionAllDatabaseAppListApi', + 'DatabaseAppPermissionSystemUserRelationViewSet', +] + + +class RelationMixin(OrgBulkModelViewSet): + def get_queryset(self): + queryset = self.model.objects.all() + org_id = current_org.org_id() + if org_id is not None: + queryset = queryset.filter(databaseapppermission__org_id=org_id) + queryset = queryset.annotate(databaseapppermission_display=F('databaseapppermission__name')) + return queryset + + +class DatabaseAppPermissionUserRelationViewSet(RelationMixin): + serializer_class = serializers.DatabaseAppPermissionUserRelationSerializer + model = models.DatabaseAppPermission.users.through + permission_classes = (IsOrgAdmin,) + filterset_fields = [ + 'id', 'user', 'databaseapppermission' + ] + search_fields = ('user__name', 'user__username', 'databaseapppermission__name') + + def get_queryset(self): + queryset = super().get_queryset() + queryset = queryset.annotate(user_display=F('user__name')) + return queryset + + +class DatabaseAppPermissionUserGroupRelationViewSet(RelationMixin): + serializer_class = serializers.DatabaseAppPermissionUserGroupRelationSerializer + model = models.DatabaseAppPermission.user_groups.through + permission_classes = (IsOrgAdmin,) + filterset_fields = [ + 'id', "usergroup", "databaseapppermission" + ] + search_fields = ["usergroup__name", "databaseapppermission__name"] + + def get_queryset(self): + queryset = super().get_queryset() + queryset = queryset \ + .annotate(usergroup_display=F('usergroup__name')) + return queryset + + +class DatabaseAppPermissionAllUserListApi(generics.ListAPIView): + permission_classes = (IsOrgAdmin,) + serializer_class = serializers.DatabaseAppPermissionAllUserSerializer + filter_fields = ("username", "name") + search_fields = filter_fields + + def get_queryset(self): + pk = self.kwargs.get("pk") + perm = get_object_or_404(models.DatabaseAppPermission, pk=pk) + users = perm.get_all_users().only( + *self.serializer_class.Meta.only_fields + ) + return users + + +class DatabaseAppPermissionDatabaseAppRelationViewSet(RelationMixin): + serializer_class = serializers.DatabaseAppPermissionDatabaseAppRelationSerializer + model = models.DatabaseAppPermission.database_apps.through + permission_classes = (IsOrgAdmin,) + filterset_fields = [ + 'id', 'databaseapp', 'databaseapppermission', + ] + search_fields = [ + "id", "databaseapp__name", "databaseapppermission__name" + ] + + def get_queryset(self): + queryset = super().get_queryset() + queryset = queryset \ + .annotate(databaseapp_display=F('databaseapp__name')) + return queryset + + +class DatabaseAppPermissionAllDatabaseAppListApi(generics.ListAPIView): + permission_classes = (IsOrgAdmin,) + serializer_class = serializers.DatabaseAppPermissionAllDatabaseAppSerializer + filter_fields = ("name",) + search_fields = filter_fields + + def get_queryset(self): + pk = self.kwargs.get("pk") + perm = get_object_or_404(models.DatabaseAppPermission, pk=pk) + database_apps = perm.get_all_database_apps().only( + *self.serializer_class.Meta.only_fields + ) + return database_apps + + +class DatabaseAppPermissionSystemUserRelationViewSet(RelationMixin): + serializer_class = serializers.DatabaseAppPermissionSystemUserRelationSerializer + model = models.DatabaseAppPermission.system_users.through + permission_classes = (IsOrgAdmin,) + filterset_fields = [ + 'id', 'systemuser', 'databaseapppermission' + ] + search_fields = [ + 'databaseapppermission__name', 'systemuser__name', 'systemuser__username' + ] + + def get_queryset(self): + queryset = super().get_queryset() + queryset = queryset.annotate( + systemuser_display=Concat( + F('systemuser__name'), Value('('), F('systemuser__username'), + Value(')') + ) + ) + return queryset diff --git a/apps/perms/api/user_database_app_permission.py b/apps/perms/api/user_database_app_permission.py new file mode 100644 index 000000000..3a973b8c1 --- /dev/null +++ b/apps/perms/api/user_database_app_permission.py @@ -0,0 +1,127 @@ +# coding: utf-8 +# + +import uuid +from django.shortcuts import get_object_or_404 +from rest_framework.views import APIView, Response +from common.permissions import IsOrgAdminOrAppUser, IsValidUser +from common.tree import TreeNodeSerializer +from orgs.mixins import generics +from users.models import User, UserGroup +from applications.serializers import DatabaseAppSerializer +from applications.models import DatabaseApp +from assets.models import SystemUser +from .. import utils, serializers +from .mixin import UserPermissionMixin + +__all__ = [ + 'UserGrantedDatabaseAppsApi', + 'UserGrantedDatabaseAppsAsTreeApi', + 'UserGroupGrantedDatabaseAppsApi', + 'ValidateUserDatabaseAppPermissionApi', + 'UserGrantedDatabaseAppSystemUsersApi', +] + + +class UserGrantedDatabaseAppsApi(generics.ListAPIView): + permission_classes = (IsOrgAdminOrAppUser,) + serializer_class = DatabaseAppSerializer + filter_fields = ['id', 'name'] + search_fields = ['name'] + + def get_object(self): + user_id = self.kwargs.get('pk', '') + if user_id: + user = get_object_or_404(User, id=user_id) + else: + user = self.request.user + return user + + def get_queryset(self): + util = utils.DatabaseAppPermissionUtil(self.get_object()) + queryset = util.get_database_apps() + return queryset + + def get_permissions(self): + if self.kwargs.get('pk') is None: + self.permission_classes = (IsValidUser,) + return super().get_permissions() + + +class UserGrantedDatabaseAppsAsTreeApi(UserGrantedDatabaseAppsApi): + serializer_class = TreeNodeSerializer + permission_classes = (IsOrgAdminOrAppUser,) + + def get_serializer(self, database_apps, *args, **kwargs): + if database_apps is None: + database_apps = [] + only_database_app = self.request.query_params.get('only', '0') == '1' + tree_root = None + data = [] + if not only_database_app: + tree_root = utils.construct_database_apps_tree_root() + data.append(tree_root) + for database_app in database_apps: + node = utils.parse_database_app_to_tree_node(tree_root, database_app) + data.append(node) + data.sort() + return super().get_serializer(data, many=True) + + +class UserGrantedDatabaseAppSystemUsersApi(UserPermissionMixin, generics.ListAPIView): + permission_classes = (IsOrgAdminOrAppUser,) + serializer_class = serializers.DatabaseAppSystemUserSerializer + only_fields = serializers.DatabaseAppSystemUserSerializer.Meta.only_fields + + def get_queryset(self): + util = utils.DatabaseAppPermissionUtil(self.obj) + database_app_id = self.kwargs.get('database_app_id') + database_app = get_object_or_404(DatabaseApp, id=database_app_id) + system_users = util.get_database_app_system_users(database_app) + return system_users + + +# Validate + +class ValidateUserDatabaseAppPermissionApi(APIView): + permission_classes = (IsOrgAdminOrAppUser,) + + def get(self, request, *args, **kwargs): + user_id = request.query_params.get('user_id', '') + database_app_id = request.query_params.get('database_app_id', '') + system_user_id = request.query_params.get('system_user_id', '') + + try: + user_id = uuid.UUID(user_id) + database_app_id = uuid.UUID(database_app_id) + system_user_id = uuid.UUID(system_user_id) + except ValueError: + return Response({'msg': False}, status=403) + + user = get_object_or_404(User, id=user_id) + database_app = get_object_or_404(DatabaseApp, id=database_app_id) + system_user = get_object_or_404(SystemUser, id=system_user_id) + + util = utils.DatabaseAppPermissionUtil(user) + system_users = util.get_database_app_system_users(database_app) + if system_user in system_users: + return Response({'msg': True}, status=200) + + return Response({'msg': False}, status=403) + + +# UserGroup + +class UserGroupGrantedDatabaseAppsApi(generics.ListAPIView): + permission_classes = (IsOrgAdminOrAppUser,) + serializer_class = DatabaseAppSerializer + + def get_queryset(self): + queryset = [] + user_group_id = self.kwargs.get('pk') + if not user_group_id: + return queryset + user_group = get_object_or_404(UserGroup, id=user_group_id) + util = utils.DatabaseAppPermissionUtil(user_group) + queryset = util.get_database_apps() + return queryset diff --git a/apps/perms/forms/__init__.py b/apps/perms/forms/__init__.py index 129901afc..c6581b858 100644 --- a/apps/perms/forms/__init__.py +++ b/apps/perms/forms/__init__.py @@ -3,3 +3,4 @@ from .asset_permission import * from .remote_app_permission import * +from .database_app_permission import * diff --git a/apps/perms/forms/asset_permission.py b/apps/perms/forms/asset_permission.py index 8282f0880..92ca8030f 100644 --- a/apps/perms/forms/asset_permission.py +++ b/apps/perms/forms/asset_permission.py @@ -5,8 +5,7 @@ from django import forms from django.utils.translation import ugettext_lazy as _ from orgs.mixins.forms import OrgModelForm -from orgs.utils import current_org -from assets.models import Asset, Node +from assets.models import Asset, Node, SystemUser from ..models import AssetPermission, Action __all__ = [ @@ -58,6 +57,12 @@ class AssetPermissionForm(OrgModelForm): nodes_field.queryset = Node.objects.none() users_field.queryset = [] + # 过滤系统用户 + system_users_field = self.fields.get('system_users') + system_users_field.queryset = SystemUser.objects.exclude( + protocol=SystemUser.PROTOCOL_MYSQL + ) + def set_nodes_initial(self, nodes): field = self.fields['nodes'] field.choices = [(n.id, n.full_value) for n in nodes] diff --git a/apps/perms/forms/database_app_permission.py b/apps/perms/forms/database_app_permission.py new file mode 100644 index 000000000..57ce4d0e3 --- /dev/null +++ b/apps/perms/forms/database_app_permission.py @@ -0,0 +1,49 @@ +# coding: utf-8 +# + +from django.utils.translation import ugettext as _ +from django import forms +from orgs.mixins.forms import OrgModelForm +from assets.models import SystemUser + +from ..models import DatabaseAppPermission + + +__all__ = ['DatabaseAppPermissionCreateUpdateForm'] + + +class DatabaseAppPermissionCreateUpdateForm(OrgModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + users_field = self.fields.get('users') + if self.instance: + users_field.queryset = self.instance.users.all() + else: + users_field.queryset = [] + + # 过滤系统用户 + system_users_field = self.fields.get('system_users') + system_users_field.queryset = SystemUser.objects.filter( + protocol=SystemUser.PROTOCOL_MYSQL + ) + + class Meta: + model = DatabaseAppPermission + exclude = ( + 'id', 'date_created', 'created_by', 'org_id' + ) + widgets = { + 'users': forms.SelectMultiple( + attrs={'class': 'users-select2', 'data-placeholder': _('User')} + ), + 'user_groups': forms.SelectMultiple( + attrs={'class': 'select2', 'data-placeholder': _('User group')} + ), + 'database_apps': forms.SelectMultiple( + attrs={'class': 'select2', 'data-placeholder': _('DatabaseApp')} + ), + 'system_users': forms.SelectMultiple( + attrs={'class': 'select2', 'data-placeholder': _('System users')} + ), + } diff --git a/apps/perms/forms/remote_app_permission.py b/apps/perms/forms/remote_app_permission.py index abac92e05..c4179297f 100644 --- a/apps/perms/forms/remote_app_permission.py +++ b/apps/perms/forms/remote_app_permission.py @@ -4,7 +4,7 @@ from django.utils.translation import ugettext as _ from django import forms from orgs.mixins.forms import OrgModelForm -from orgs.utils import current_org +from assets.models import SystemUser from ..models import RemoteAppPermission @@ -24,6 +24,12 @@ class RemoteAppPermissionCreateUpdateForm(OrgModelForm): else: users_field.queryset = [] + # 过滤系统用户 + system_users_field = self.fields.get('system_users') + system_users_field.queryset = SystemUser.objects.filter( + protocol=SystemUser.PROTOCOL_RDP + ) + class Meta: model = RemoteAppPermission exclude = ( @@ -43,13 +49,3 @@ class RemoteAppPermissionCreateUpdateForm(OrgModelForm): attrs={'class': 'select2', 'data-placeholder': _('System user')} ) } - - def clean_user_groups(self): - users = self.cleaned_data.get('users') - user_groups = self.cleaned_data.get('user_groups') - - if not users and not user_groups: - raise forms.ValidationError( - _("User or group at least one required") - ) - return self.cleaned_data['user_groups'] diff --git a/apps/perms/migrations/0010_auto_20191218_1705.py b/apps/perms/migrations/0010_auto_20191218_1705.py new file mode 100644 index 000000000..c3144bc5d --- /dev/null +++ b/apps/perms/migrations/0010_auto_20191218_1705.py @@ -0,0 +1,47 @@ +# Generated by Django 2.1.11 on 2019-12-18 09:05 + +import common.utils.django +from django.conf import settings +from django.db import migrations, models +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0024_auto_20191118_1612'), + ('assets', '0046_auto_20191218_1705'), + ('applications', '0004_auto_20191218_1705'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('perms', '0009_remoteapppermission_system_users'), + ] + + operations = [ + migrations.CreateModel( + name='DatabaseAppPermission', + fields=[ + ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128, verbose_name='Name')), + ('is_active', models.BooleanField(default=True, verbose_name='Active')), + ('date_start', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='Date start')), + ('date_expired', models.DateTimeField(db_index=True, default=common.utils.django.date_expired_default, verbose_name='Date expired')), + ('created_by', models.CharField(blank=True, max_length=128, verbose_name='Created by')), + ('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')), + ('comment', models.TextField(blank=True, verbose_name='Comment')), + ('database_apps', models.ManyToManyField(blank=True, related_name='granted_by_permissions', to='applications.DatabaseApp', verbose_name='DatabaseApp')), + ('system_users', models.ManyToManyField(related_name='granted_by_database_app_permissions', to='assets.SystemUser', verbose_name='System user')), + ('user_groups', models.ManyToManyField(blank=True, to='users.UserGroup', verbose_name='User group')), + ('users', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'verbose_name': 'DatabaseApp permission', + 'ordering': ('name',), + }, + ), + migrations.AlterUniqueTogether( + name='databaseapppermission', + unique_together={('org_id', 'name')}, + ), + ] diff --git a/apps/perms/models/__init__.py b/apps/perms/models/__init__.py index 129901afc..c6581b858 100644 --- a/apps/perms/models/__init__.py +++ b/apps/perms/models/__init__.py @@ -3,3 +3,4 @@ from .asset_permission import * from .remote_app_permission import * +from .database_app_permission import * diff --git a/apps/perms/models/database_app_permission.py b/apps/perms/models/database_app_permission.py new file mode 100644 index 000000000..de2693274 --- /dev/null +++ b/apps/perms/models/database_app_permission.py @@ -0,0 +1,30 @@ +# coding: utf-8 +# + +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from .base import BasePermission + +__all__ = [ + 'DatabaseAppPermission', +] + + +class DatabaseAppPermission(BasePermission): + database_apps = models.ManyToManyField( + 'applications.DatabaseApp', related_name='granted_by_permissions', + blank=True, verbose_name=_("DatabaseApp") + ) + system_users = models.ManyToManyField( + 'assets.SystemUser', related_name='granted_by_database_app_permissions', + verbose_name=_("System user") + ) + + class Meta: + unique_together = [('org_id', 'name')] + verbose_name = _('DatabaseApp permission') + ordering = ('name',) + + def get_all_database_apps(self): + return self.database_apps.all() diff --git a/apps/perms/serializers/__init__.py b/apps/perms/serializers/__init__.py index e464b4cc6..7f83bae9b 100644 --- a/apps/perms/serializers/__init__.py +++ b/apps/perms/serializers/__init__.py @@ -5,3 +5,5 @@ from .asset_permission import * from .user_permission import * from .remote_app_permission import * from .asset_permission_relation import * +from .database_app_permission import * +from .database_app_permission_relation import * diff --git a/apps/perms/serializers/asset_permission_relation.py b/apps/perms/serializers/asset_permission_relation.py index eaa38afe2..808f40468 100644 --- a/apps/perms/serializers/asset_permission_relation.py +++ b/apps/perms/serializers/asset_permission_relation.py @@ -6,6 +6,7 @@ from common.mixins import BulkSerializerMixin from common.serializers import AdaptedBulkListSerializer from assets.models import Asset, Node from ..models import AssetPermission +from users.models import User __all__ = [ 'AssetPermissionUserRelationSerializer', @@ -50,14 +51,12 @@ class AssetPermissionUserRelationSerializer(RelationMixin, serializers.ModelSeri ] -class AssetPermissionAllUserSerializer(serializers.ModelSerializer): +class AssetPermissionAllUserSerializer(serializers.Serializer): user = serializers.UUIDField(read_only=True, source='id') user_display = serializers.SerializerMethodField() class Meta: - model = Asset only_fields = ['id', 'username', 'name'] - fields = ['user', 'user_display'] @staticmethod def get_user_display(obj): @@ -84,14 +83,12 @@ class AssetPermissionAssetRelationSerializer(RelationMixin, serializers.ModelSer ] -class AssetPermissionAllAssetSerializer(serializers.ModelSerializer): +class AssetPermissionAllAssetSerializer(serializers.Serializer): asset = serializers.UUIDField(read_only=True, source='id') asset_display = serializers.SerializerMethodField() class Meta: - model = Asset only_fields = ['id', 'hostname', 'ip'] - fields = ['asset', 'asset_display'] @staticmethod def get_asset_display(obj): diff --git a/apps/perms/serializers/database_app_permission.py b/apps/perms/serializers/database_app_permission.py new file mode 100644 index 000000000..75cdea0c9 --- /dev/null +++ b/apps/perms/serializers/database_app_permission.py @@ -0,0 +1,21 @@ +# coding: utf-8 +# + +from common.serializers import AdaptedBulkListSerializer +from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from .. import models + +__all__ = ['DatabaseAppPermissionSerializer'] + + +class DatabaseAppPermissionSerializer(BulkOrgResourceModelSerializer): + class Meta: + model = models.DatabaseAppPermission + list_serializer_class = AdaptedBulkListSerializer + fields = [ + 'id', 'name', 'users', 'user_groups', + 'database_apps', 'system_users', 'comment', 'is_active', + 'date_start', 'date_expired', 'is_valid', + 'created_by', 'date_created' + ] + read_only_fields = ['created_by', 'date_created'] diff --git a/apps/perms/serializers/database_app_permission_relation.py b/apps/perms/serializers/database_app_permission_relation.py new file mode 100644 index 000000000..1a8263cda --- /dev/null +++ b/apps/perms/serializers/database_app_permission_relation.py @@ -0,0 +1,94 @@ +# coding: utf-8 +# +from rest_framework import serializers + +from applications.models import DatabaseApp +from common.mixins import BulkSerializerMixin +from common.serializers import AdaptedBulkListSerializer + +from .. import models + +__all__ = [ + 'DatabaseAppPermissionUserRelationSerializer', + 'DatabaseAppPermissionUserGroupRelationSerializer', + 'DatabaseAppPermissionAllUserSerializer', + 'DatabaseAppPermissionDatabaseAppRelationSerializer', + 'DatabaseAppPermissionAllDatabaseAppSerializer', + 'DatabaseAppPermissionSystemUserRelationSerializer', +] + + +class RelationMixin(BulkSerializerMixin, serializers.Serializer): + databaseapppermission_display = serializers.ReadOnlyField() + + def get_field_names(self, declared_fields, info): + fields = super().get_field_names(declared_fields, info) + fields.extend(['databaseapppermission', "databaseapppermission_display"]) + return fields + + class Meta: + list_serializer_class = AdaptedBulkListSerializer + + +class DatabaseAppPermissionUserRelationSerializer(RelationMixin, serializers.ModelSerializer): + user_display = serializers.ReadOnlyField() + + class Meta(RelationMixin.Meta): + model = models.DatabaseAppPermission.users.through + fields = [ + 'id', 'user', 'user_display', + ] + + +class DatabaseAppPermissionUserGroupRelationSerializer(RelationMixin, serializers.ModelSerializer): + usergroup_display = serializers.ReadOnlyField() + + class Meta(RelationMixin.Meta): + model = models.DatabaseAppPermission.user_groups.through + fields = [ + 'id', 'usergroup', "usergroup_display", + ] + + +class DatabaseAppPermissionAllUserSerializer(serializers.Serializer): + user = serializers.UUIDField(read_only=True, source='id') + user_display = serializers.SerializerMethodField() + + class Meta: + only_fields = ['id', 'username', 'name'] + + @staticmethod + def get_user_display(obj): + return str(obj) + + +class DatabaseAppPermissionDatabaseAppRelationSerializer(RelationMixin, serializers.ModelSerializer): + databaseapp_display = serializers.ReadOnlyField() + + class Meta(RelationMixin.Meta): + model = models.DatabaseAppPermission.database_apps.through + fields = [ + 'id', "databaseapp", "databaseapp_display", + ] + + +class DatabaseAppPermissionAllDatabaseAppSerializer(serializers.Serializer): + databaseapp = serializers.UUIDField(read_only=True, source='id') + databaseapp_display = serializers.SerializerMethodField() + + class Meta: + only_fields = ['id', 'name'] + + @staticmethod + def get_databaseapp_display(obj): + return str(obj) + + +class DatabaseAppPermissionSystemUserRelationSerializer(RelationMixin, serializers.ModelSerializer): + systemuser_display = serializers.ReadOnlyField() + + class Meta(RelationMixin.Meta): + model = models.DatabaseAppPermission.system_users.through + fields = [ + 'id', 'systemuser', 'systemuser_display' + ] diff --git a/apps/perms/serializers/user_permission.py b/apps/perms/serializers/user_permission.py index 7ab4cf66b..c194cf64a 100644 --- a/apps/perms/serializers/user_permission.py +++ b/apps/perms/serializers/user_permission.py @@ -13,6 +13,7 @@ __all__ = [ 'AssetGrantedSerializer', 'ActionsSerializer', 'AssetSystemUserSerializer', 'RemoteAppSystemUserSerializer', + 'DatabaseAppSystemUserSerializer', ] @@ -41,6 +42,16 @@ class RemoteAppSystemUserSerializer(serializers.ModelSerializer): read_only_fields = fields +class DatabaseAppSystemUserSerializer(serializers.ModelSerializer): + class Meta: + model = SystemUser + only_fields = ( + 'id', 'name', 'username', 'priority', 'protocol', 'login_mode', + ) + fields = list(only_fields) + read_only_fields = fields + + class AssetGrantedSerializer(serializers.ModelSerializer): """ 被授权资产的数据结构 diff --git a/apps/perms/templates/perms/database_app_permission_create_update.html b/apps/perms/templates/perms/database_app_permission_create_update.html new file mode 100644 index 000000000..a779e5826 --- /dev/null +++ b/apps/perms/templates/perms/database_app_permission_create_update.html @@ -0,0 +1,143 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load static %} +{% load bootstrap3 %} +{% block custom_head_css_js %} + +{% endblock %} + +{% block content %} +
    +
    +
    +
    +
    +
    {{ action }}
    + +
    +
    +
    + {% if form.non_field_errors %} +
    + {{ form.non_field_errors }} +
    + {% endif %} + {% csrf_token %} + +

    {% trans 'Basic' %}

    + {% bootstrap_field form.name layout="horizontal" %} +
    + +

    {% trans 'User' %}

    + {% bootstrap_field form.users layout="horizontal" %} + {% bootstrap_field form.user_groups layout="horizontal" %} +
    + +

    {% trans 'DatabaseApp' %}

    + {% bootstrap_field form.database_apps layout="horizontal" %} + {% bootstrap_field form.system_users layout="horizontal" %} +
    + +

    {% trans 'Other' %}

    +
    + +
    + {{ form.is_active }} +
    +
    +
    + +
    +
    + + {% if form.errors %} + + to + + {% else %} + + to + + {% endif %} +
    + {{ form.date_expired.errors }} + {{ form.date_start.errors }} +
    +
    + + {% bootstrap_field form.comment layout="horizontal" %} + +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block custom_foot_js %} + + + + + + +{% endblock %} diff --git a/apps/perms/templates/perms/database_app_permission_database_app.html b/apps/perms/templates/perms/database_app_permission_database_app.html new file mode 100644 index 000000000..0a23618d0 --- /dev/null +++ b/apps/perms/templates/perms/database_app_permission_database_app.html @@ -0,0 +1,237 @@ +{% extends 'base.html' %} +{% load static %} +{% load i18n %} + +{% block content %} +
    +
    +
    +
    + +
    +
    +
    +
    + {% trans 'DatabaseApp list of ' %} {{ database_app_permission.name }} +
    + + + + + + + + + + +
    +
    +
    + + + + + + + + + + +
    + + {% trans 'DatabaseApp' %}{% trans 'Action' %}
    +
    +
    +
    +
    +
    +
    + {% trans 'Add DatabaseApp to this permission' %} +
    +
    + + + + + + + + + + + +
    + +
    + +
    +
    +
    + +
    +
    + {% trans 'System user' %} +
    +
    + + + + + + + + + + + + {% for system_user in object.system_users.all %} + + + + + {% endfor %} + +
    + +
    + +
    {{ system_user|truncatechars:21}} + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/perms/templates/perms/database_app_permission_detail.html b/apps/perms/templates/perms/database_app_permission_detail.html new file mode 100644 index 000000000..feae4db24 --- /dev/null +++ b/apps/perms/templates/perms/database_app_permission_detail.html @@ -0,0 +1,157 @@ +{% extends 'base.html' %} +{% load static %} +{% load i18n %} + +{% block content %} +
    +
    +
    +
    + +
    +
    +
    +
    + {{ object.name }} +
    + + + + + + + + + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {% trans 'Name' %}:{{ object.name }}
    {% trans 'User count' %}:{{ object.users.count }}
    {% trans 'User group count' %}:{{ object.user_groups.count }}
    {% trans 'DatabaseApp count' %}:{{ object.database_apps.count }}
    {% trans 'System user count' %}:{{ object.system_users.count }}
    {% trans 'Date start' %}:{{ object.date_start }}
    {% trans 'Date expired' %}:{{ object.date_expired }}
    {% trans 'Date created' %}:{{ object.date_created }}
    {% trans 'Created by' %}:{{ object.created_by }}
    {% trans 'Comment' %}:{{ object.comment }}
    +
    +
    +
    + +
    +
    +
    + {% trans 'Quick update' %} +
    +
    + + + + + + + +
    {% trans 'Active' %} : +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/perms/templates/perms/database_app_permission_list.html b/apps/perms/templates/perms/database_app_permission_list.html new file mode 100644 index 000000000..cad068929 --- /dev/null +++ b/apps/perms/templates/perms/database_app_permission_list.html @@ -0,0 +1,99 @@ +{% extends '_base_list.html' %} +{% load i18n static %} +{% block table_search %}{% endblock %} +{% block table_container %} + + + + + + + + + + + + + + + + +
    + + {% trans 'Name' %}{% trans 'User' %}{% trans 'User group' %}{% trans 'DatabaseApp' %}{% trans 'System user' %}{% trans 'Validity' %}{% trans 'Action' %}
    +{% endblock %} +{% block content_bottom_left %}{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/perms/templates/perms/database_app_permission_user.html b/apps/perms/templates/perms/database_app_permission_user.html new file mode 100644 index 000000000..603f60a6e --- /dev/null +++ b/apps/perms/templates/perms/database_app_permission_user.html @@ -0,0 +1,251 @@ +{% extends 'base.html' %} +{% load static %} +{% load i18n %} + +{% block content %} +
    +
    +
    +
    + +
    +
    +
    +
    + {% trans 'User list of ' %} {{ database_app_permission.name }} +
    + + + + + + + + + + +
    +
    +
    + + + + + + + + + + +
    + + {% trans 'Name' %}{% trans 'Action' %}
    +
    +
    +
    +
    +
    +
    + {% trans 'Add user to permission' %} +
    +
    + + + + + + + + + + + +
    + +
    + +
    +
    +
    + +
    +
    + {% trans 'Add user group to permission' %} +
    +
    + + + + + + + + + + + + {% for user_group in database_app_permission.user_groups.all %} + + + + + {% endfor %} + +
    + +
    + +
    {{ user_group }} + +
    +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/perms/templates/perms/remote_app_permission_create_update.html b/apps/perms/templates/perms/remote_app_permission_create_update.html index a551e48ba..f6e0cda7d 100644 --- a/apps/perms/templates/perms/remote_app_permission_create_update.html +++ b/apps/perms/templates/perms/remote_app_permission_create_update.html @@ -123,7 +123,7 @@ $(document).ready(function () { var method = "POST"; var the_url = '{% url "api-perms:remote-app-permission-list" %}'; var redirect_to = '{% url "perms:remote-app-permission-list" %}'; - {% if type == "update" %} + {% if api_action == "update" %} the_url = '{% url "api-perms:remote-app-permission-detail" pk=object.id %}'; method = "PUT"; {% endif %} diff --git a/apps/perms/templates/perms/remote_app_permission_list.html b/apps/perms/templates/perms/remote_app_permission_list.html index 3812b747b..6436c6981 100644 --- a/apps/perms/templates/perms/remote_app_permission_list.html +++ b/apps/perms/templates/perms/remote_app_permission_list.html @@ -75,7 +75,7 @@ function initTable() { {data: "remote_apps", orderable: false}, {data: "system_users", orderable: false}, {data: "is_valid", orderable: false}, - {data: "id", orderable: false} + {data: "id", orderable: false, width: "100px"} ], op_html: $('#actions').html() }; diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py index 696d34882..9ec3b754f 100644 --- a/apps/perms/urls/api_urls.py +++ b/apps/perms/urls/api_urls.py @@ -3,7 +3,8 @@ from django.urls import re_path from common import api as capi from .asset_permission import asset_permission_urlpatterns -from .application_permission import remote_app_permission_urlpatterns +from .remote_app_permission import remote_app_permission_urlpatterns +from .database_app_permission import database_app_permission_urlpatterns app_name = 'perms' @@ -13,6 +14,7 @@ old_version_urlpatterns = [ ] urlpatterns = asset_permission_urlpatterns + \ - remote_app_permission_urlpatterns \ - + old_version_urlpatterns + remote_app_permission_urlpatterns + \ + database_app_permission_urlpatterns + \ + old_version_urlpatterns diff --git a/apps/perms/urls/database_app_permission.py b/apps/perms/urls/database_app_permission.py new file mode 100644 index 000000000..a793f980e --- /dev/null +++ b/apps/perms/urls/database_app_permission.py @@ -0,0 +1,47 @@ +# coding: utf-8 +# + +from django.urls import path, include +from rest_framework_bulk.routes import BulkRouter +from .. import api + + +router = BulkRouter() +router.register('database-app-permissions', api.DatabaseAppPermissionViewSet, 'database-app-permission') +router.register('database-app-permissions-users-relations', api.DatabaseAppPermissionUserRelationViewSet, 'database-app-permissions-users-relation') +router.register('database-app-permissions-user-groups-relations', api.DatabaseAppPermissionUserGroupRelationViewSet, 'database-app-permissions-user-groups-relation') +router.register('database-app-permissions-database-apps-relations', api.DatabaseAppPermissionDatabaseAppRelationViewSet, 'database-app-permissions-database-apps-relation') +router.register('database-app-permissions-system-users-relations', api.DatabaseAppPermissionSystemUserRelationViewSet, 'database-app-permissions-system-users-relation') + +user_permission_urlpatterns = [ + path('/database-apps/', api.UserGrantedDatabaseAppsApi.as_view(), name='user-database-apps'), + path('database-apps/', api.UserGrantedDatabaseAppsApi.as_view(), name='my-database-apps'), + + # DatabaseApps as tree + path('/database-apps/tree/', api.UserGrantedDatabaseAppsAsTreeApi.as_view(), name='user-databases-apps-tree'), + path('database-apps/tree/', api.UserGrantedDatabaseAppsAsTreeApi.as_view(), name='my-databases-apps-tree'), + + path('/database-apps//system-users/', api.UserGrantedDatabaseAppSystemUsersApi.as_view(), name='user-database-app-system-users'), + path('database-apps//system-users/', api.UserGrantedDatabaseAppSystemUsersApi.as_view(), name='user-database-app-system-users'), +] + +user_group_permission_urlpatterns = [ + path('/database-apps/', api.UserGroupGrantedDatabaseAppsApi.as_view(), name='user-group-database-apps'), +] + +permission_urlpatterns = [ + # 授权规则中授权的用户和数据库应用 + path('/users/all/', api.DatabaseAppPermissionAllUserListApi.as_view(), name='database-app-permission-all-users'), + path('/database-apps/all/', api.DatabaseAppPermissionAllDatabaseAppListApi.as_view(), name='database-app-permission-all-database-apps'), + + # 验证用户是否有某个数据库应用的权限 + path('user/validate/', api.ValidateUserDatabaseAppPermissionApi.as_view(), name='validate-user-database-app-permission'), +] + +database_app_permission_urlpatterns = [ + path('users/', include(user_permission_urlpatterns)), + path('user-groups/', include(user_group_permission_urlpatterns)), + path('database-app-permissions/', include(permission_urlpatterns)) +] + +database_app_permission_urlpatterns += router.urls diff --git a/apps/perms/urls/application_permission.py b/apps/perms/urls/remote_app_permission.py similarity index 100% rename from apps/perms/urls/application_permission.py rename to apps/perms/urls/remote_app_permission.py diff --git a/apps/perms/urls/views_urls.py b/apps/perms/urls/views_urls.py index 964025db3..f4deba8c8 100644 --- a/apps/perms/urls/views_urls.py +++ b/apps/perms/urls/views_urls.py @@ -23,4 +23,12 @@ urlpatterns = [ path('remote-app-permission//', views.RemoteAppPermissionDetailView.as_view(), name='remote-app-permission-detail'), path('remote-app-permission//user/', views.RemoteAppPermissionUserView.as_view(), name='remote-app-permission-user-list'), path('remote-app-permission//remote-app/', views.RemoteAppPermissionRemoteAppView.as_view(), name='remote-app-permission-remote-app-list'), + + # database-app-permission + path('database-app-permission/', views.DatabaseAppPermissionListView.as_view(), name='database-app-permission-list'), + path('database-app-permission/create/', views.DatabaseAppPermissionCreateView.as_view(), name='database-app-permission-create'), + path('database-app-permission//update/', views.DatabaseAppPermissionUpdateView.as_view(), name='database-app-permission-update'), + path('database-app-permission//', views.DatabaseAppPermissionDetailView.as_view(), name='database-app-permission-detail'), + path('database-app-permission//user/', views.DatabaseAppPermissionUserView.as_view(), name='database-app-permission-user-list'), + path('database-app-permission//database-app/', views.DatabaseAppPermissionDatabaseAppView.as_view(), name='database-app-permission-database-app-list'), ] diff --git a/apps/perms/utils/__init__.py b/apps/perms/utils/__init__.py index 129901afc..c6581b858 100644 --- a/apps/perms/utils/__init__.py +++ b/apps/perms/utils/__init__.py @@ -3,3 +3,4 @@ from .asset_permission import * from .remote_app_permission import * +from .database_app_permission import * diff --git a/apps/perms/utils/database_app_permission.py b/apps/perms/utils/database_app_permission.py new file mode 100644 index 000000000..526b0fc59 --- /dev/null +++ b/apps/perms/utils/database_app_permission.py @@ -0,0 +1,99 @@ +# coding: utf-8 +# + +from django.db.models import Q +from orgs.utils import set_to_root_org + +from ..models import DatabaseAppPermission +from common.tree import TreeNode +from applications.models import DatabaseApp +from assets.models import SystemUser + + +__all__ = [ + 'DatabaseAppPermissionUtil', + 'construct_database_apps_tree_root', + 'parse_database_app_to_tree_node' +] + + +def get_user_database_app_permissions(user, include_group=True): + if include_group: + groups = user.groups.all() + arg = Q(users=user) | Q(user_groups__in=groups) + else: + arg = Q(users=user) + return DatabaseAppPermission.objects.all().valid().filter(arg) + + +def get_user_group_database_app_permission(user_group): + return DatabaseAppPermission.objects.all().valid().filter( + user_group=user_group + ) + + +class DatabaseAppPermissionUtil: + get_permissions_map = { + 'User': get_user_database_app_permissions, + 'UserGroup': get_user_group_database_app_permission + } + + def __init__(self, obj): + self.object = obj + self.change_org_if_need() + + @staticmethod + def change_org_if_need(): + set_to_root_org() + + @property + def permissions(self): + obj_class = self.object.__class__.__name__ + func = self.get_permissions_map[obj_class] + _permissions = func(self.object) + return _permissions + + def get_database_apps(self): + database_apps = DatabaseApp.objects.filter( + granted_by_permissions__in=self.permissions + ) + return database_apps + + def get_database_app_system_users(self, database_app): + queryset = self.permissions + kwargs = {'database_apps': database_app} + queryset = queryset.filter(**kwargs) + system_users_ids = queryset.values_list('system_users', flat=True) + system_users_ids = system_users_ids.distinct() + system_users = SystemUser.objects.filter(id__in=system_users_ids) + system_users = system_users.order_by('-priority') + return system_users + + +def construct_database_apps_tree_root(): + tree_root = { + 'id': 'ID_DATABASE_APP_ROOT', + 'name': 'DatabaseApp', + 'title': 'DatabaseApp', + 'pId': '', + 'open': False, + 'isParent': True, + 'iconSkin': '', + 'meta': {'type': 'database_app'} + } + return TreeNode(**tree_root) + + +def parse_database_app_to_tree_node(parent, database_app): + pid = parent.id if parent else '' + tree_node = { + 'id': database_app.id, + 'name': database_app.name, + 'title': database_app.name, + 'pId': pid, + 'open': False, + 'isParent': False, + 'iconSkin': 'file', + 'meta': {'type': 'database_app'} + } + return TreeNode(**tree_node) diff --git a/apps/perms/views/__init__.py b/apps/perms/views/__init__.py index 129901afc..c6581b858 100644 --- a/apps/perms/views/__init__.py +++ b/apps/perms/views/__init__.py @@ -3,3 +3,4 @@ from .asset_permission import * from .remote_app_permission import * +from .database_app_permission import * diff --git a/apps/perms/views/asset_permission.py b/apps/perms/views/asset_permission.py index 48a7f2aa3..71b4d5604 100644 --- a/apps/perms/views/asset_permission.py +++ b/apps/perms/views/asset_permission.py @@ -162,13 +162,14 @@ class AssetPermissionAssetView(PermissionsMixin, def get_context_data(self, **kwargs): assets = self.object.assets.all().values_list('id', flat=True) assets = [str(i) for i in assets] + system_users_remain = SystemUser.objects\ + .exclude(granted_by_permissions=self.object)\ + .exclude(protocol=SystemUser.PROTOCOL_MYSQL) context = { 'app': _('Perms'), 'assets': assets, 'action': _('Asset permission asset list'), - 'system_users_remain': SystemUser.objects.exclude( - granted_by_permissions=self.object - ), + 'system_users_remain': system_users_remain, } kwargs.update(context) return super().get_context_data(**kwargs) diff --git a/apps/perms/views/database_app_permission.py b/apps/perms/views/database_app_permission.py new file mode 100644 index 000000000..50627defe --- /dev/null +++ b/apps/perms/views/database_app_permission.py @@ -0,0 +1,152 @@ +# coding: utf-8 +# + +from django.utils.translation import ugettext as _ + +from django.views.generic import ( + TemplateView, CreateView, UpdateView, DetailView, ListView +) +from django.views.generic.edit import SingleObjectMixin +from django.conf import settings + +from common.permissions import PermissionsMixin, IsOrgAdmin +from users.models import UserGroup +from applications.models import DatabaseApp +from assets.models import SystemUser + +from .. import models, forms + + +__all__ = [ + 'DatabaseAppPermissionListView', 'DatabaseAppPermissionCreateView', + 'DatabaseAppPermissionUpdateView', 'DatabaseAppPermissionDetailView', + 'DatabaseAppPermissionUserView', 'DatabaseAppPermissionDatabaseAppView', +] + + +class DatabaseAppPermissionListView(PermissionsMixin, TemplateView): + template_name = 'perms/database_app_permission_list.html' + permission_classes = [IsOrgAdmin] + + def get_context_data(self, **kwargs): + context = { + 'app': _('Perms'), + 'action': _('DatabaseApp permission list') + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class DatabaseAppPermissionCreateView(PermissionsMixin, CreateView): + template_name = 'perms/database_app_permission_create_update.html' + model = models.DatabaseAppPermission + form_class = forms.DatabaseAppPermissionCreateUpdateForm + permission_classes = [IsOrgAdmin] + + def get_context_data(self, **kwargs): + context = { + 'app': _('Perms'), + 'action': _('Create DatabaseApp permission'), + 'api_action': 'create', + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class DatabaseAppPermissionUpdateView(PermissionsMixin, UpdateView): + template_name = 'perms/database_app_permission_create_update.html' + model = models.DatabaseAppPermission + form_class = forms.DatabaseAppPermissionCreateUpdateForm + permission_classes = [IsOrgAdmin] + + def get_context_data(self, **kwargs): + context = { + 'app': _('Perms'), + 'action': _('Update DatabaseApp permission'), + 'api_action': 'update' + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class DatabaseAppPermissionDetailView(PermissionsMixin, DetailView): + template_name = 'perms/database_app_permission_detail.html' + model = models.DatabaseAppPermission + permission_classes = [IsOrgAdmin] + + def get_context_data(self, **kwargs): + context = { + 'app': _('Perms'), + 'action': _('DatabaseApp permission detail') + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class DatabaseAppPermissionUserView(PermissionsMixin, + SingleObjectMixin, + ListView): + template_name = 'perms/database_app_permission_user.html' + context_object_name = 'database_app_permission' + paginate_by = settings.DISPLAY_PER_PAGE + object = None + permission_classes = [IsOrgAdmin] + + def get(self, request, *args, **kwargs): + self.object = self.get_object(queryset=models.DatabaseAppPermission.objects.all()) + return super().get(request, *args, **kwargs) + + def get_queryset(self): + queryset = list(self.object.get_all_users()) + return queryset + + def get_context_data(self, **kwargs): + users = [str(i) for i in self.object.users.all().values_list('id', flat=True)] + user_groups_remain = UserGroup.objects.exclude( + databaseapppermission=self.object) + context = { + 'app': _('Perms'), + 'action': _('DatabaseApp permission user list'), + 'users': users, + 'user_groups_remain': user_groups_remain, + } + kwargs.update(context) + return super().get_context_data(**kwargs) + + +class DatabaseAppPermissionDatabaseAppView(PermissionsMixin, + SingleObjectMixin, + ListView): + template_name = 'perms/database_app_permission_database_app.html' + context_object_name = 'database_app_permission' + paginate_by = settings.DISPLAY_PER_PAGE + object = None + permission_classes = [IsOrgAdmin] + + def get(self, request, *args, **kwargs): + self.object = self.get_object( + queryset=models.DatabaseAppPermission.objects.all() + ) + return super().get(request, *args, **kwargs) + + def get_queryset(self): + queryset = list(self.object.get_all_database_apps()) + return queryset + + def get_context_data(self, **kwargs): + database_apps = self.object.get_all_database_apps().values_list('id', flat=True) + database_apps = [str(i) for i in database_apps] + system_users_remain = SystemUser.objects\ + .exclude(granted_by_database_app_permissions=self.object)\ + .filter(protocol=SystemUser.PROTOCOL_MYSQL) + context = { + 'app': _('Perms'), + 'database_apps': database_apps, + 'database_apps_remain': DatabaseApp.objects.exclude( + granted_by_permissions=self.object + ), + 'system_users_remain': system_users_remain, + 'action': _('DatabaseApp permission DatabaseApp list'), + } + kwargs.update(context) + return super().get_context_data(**kwargs) diff --git a/apps/perms/views/remote_app_permission.py b/apps/perms/views/remote_app_permission.py index 742a5fd85..875600c68 100644 --- a/apps/perms/views/remote_app_permission.py +++ b/apps/perms/views/remote_app_permission.py @@ -48,7 +48,7 @@ class RemoteAppPermissionCreateView(PermissionsMixin, CreateView): context = { 'app': _('Perms'), 'action': _('Create RemoteApp permission'), - 'type': 'create' + 'api_action': 'create' } kwargs.update(context) return super().get_context_data(**kwargs) @@ -65,7 +65,7 @@ class RemoteAppPermissionUpdateView(PermissionsMixin, UpdateView): context = { 'app': _('Perms'), 'action': _('Update RemoteApp permission'), - 'type': 'update' + 'api_action': 'update' } kwargs.update(context) return super().get_context_data(**kwargs) @@ -77,12 +77,13 @@ class RemoteAppPermissionDetailView(PermissionsMixin, DetailView): permission_classes = [IsOrgAdmin] def get_context_data(self, **kwargs): + system_users_remain = SystemUser.objects\ + .exclude(granted_by_remote_app_permissions=self.object)\ + .filter(protocol=SystemUser.PROTOCOL_RDP) context = { 'app': _('Perms'), 'action': _('RemoteApp permission detail'), - 'system_users_remain': SystemUser.objects.exclude( - granted_by_remote_app_permissions=self.object - ), + 'system_users_remain': system_users_remain, } kwargs.update(context) return super().get_context_data(**kwargs) @@ -107,10 +108,10 @@ class RemoteAppPermissionUserView(PermissionsMixin, return queryset def get_context_data(self, **kwargs): - user_remain = current_org.get_org_members(exclude=('Auditor',)).exclude( - remoteapppermission=self.object) - user_groups_remain = UserGroup.objects.exclude( - remoteapppermission=self.object) + user_remain = current_org.get_org_members(exclude=('Auditor',))\ + .exclude(remoteapppermission=self.object) + user_groups_remain = UserGroup.objects\ + .exclude(remoteapppermission=self.object) context = { 'app': _('Perms'), 'action': _('RemoteApp permission user list'), diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index 578cedfa6..5ad4a2618 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -61,6 +61,7 @@ {% endif %} @@ -78,6 +79,9 @@
  • {% trans 'RemoteApp' %}
  • +
  • + {% trans 'DatabaseApp' %} +
  • {% endif %} diff --git a/apps/templates/_nav_user.html b/apps/templates/_nav_user.html index 8196a4f44..bc8ff88a7 100644 --- a/apps/templates/_nav_user.html +++ b/apps/templates/_nav_user.html @@ -16,6 +16,11 @@ {% trans 'RemoteApp' %} +
  • + + {% trans 'DatabaseApp' %} + +
  • {% endif %} diff --git a/apps/terminal/migrations/0020_auto_20191218_1721.py b/apps/terminal/migrations/0020_auto_20191218_1721.py new file mode 100644 index 000000000..d9c6bfa5b --- /dev/null +++ b/apps/terminal/migrations/0020_auto_20191218_1721.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.11 on 2019-12-18 09:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('terminal', '0019_auto_20191206_1000'), + ] + + operations = [ + migrations.AlterField( + model_name='session', + name='protocol', + field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('vnc', 'vnc'), ('telnet', 'telnet'), ('mysql', 'mysql')], db_index=True, default='ssh', max_length=8), + ), + ] diff --git a/apps/terminal/models.py b/apps/terminal/models.py index f6676383a..5cb6583d8 100644 --- a/apps/terminal/models.py +++ b/apps/terminal/models.py @@ -169,6 +169,7 @@ class Session(OrgModelMixin): ('rdp', 'rdp'), ('vnc', 'vnc'), ('telnet', 'telnet'), + ('mysql', 'mysql'), ) id = models.UUIDField(default=uuid.uuid4, primary_key=True) diff --git a/apps/terminal/templates/terminal/command_list.html b/apps/terminal/templates/terminal/command_list.html index 1fc6f1d76..5e2dcddc9 100644 --- a/apps/terminal/templates/terminal/command_list.html +++ b/apps/terminal/templates/terminal/command_list.html @@ -168,7 +168,7 @@ $(document).ready(function () { function format(d) { - var output = $("
    ");
    +    var output = $("
    ");
     
         output.append('$ ', d.input);
         output.append('\r\n\r\n');
    diff --git a/apps/terminal/templates/terminal/session_detail.html b/apps/terminal/templates/terminal/session_detail.html
    index 1c96e3ee7..37f256d70 100644
    --- a/apps/terminal/templates/terminal/session_detail.html
    +++ b/apps/terminal/templates/terminal/session_detail.html
    @@ -55,7 +55,7 @@
                                                 
                                                     {{ forloop.counter }}
                                                     {{ command.input | truncatechars:40 }}
    -                                                
    +                                                
     $ {{ command.input }}
     
     {{ command.output }}