[Feature] 添加功能:数据库应用 (#3551)

* [Update] 添加数据库应用Model

* [Update] 添加数据库应用ViewSet

* [Update] 添加数据库应用HTML

* [Update] 更新数据库应用迁移文件

* [Update] 添加数据库应用授权Model

* [Update] 添加数据库应用授权ViewSet(待续)

* [Update] 添加数据库应用授权ViewSet(完结)

* [Update] 添加数据库应用授权View(待续)

* [Update] 添加数据库应用授权View(待续2)

* [Update] 修改远程应用授权View(小问题)

* [Update] 添加数据库应用授权View(待续3)

* [Update] 添加数据库应用授权View(完结)

* [Update] 添加数据库应用授权相关API

* [Update] 添加数据库应用View(用户页面)

* [Update] 修改数据库应用授权Model/View/API(系统用户)

* [Update] 修改系统用户Model/View(添加mysql协议)

* [Update] 修改用户页面(我的应用)

* [Update] 添加迁移文件

* [Update] 添加迁移文件2

* [Update] 续添加迁移文件2(Model更改)

* [Update] 修改系统用户序列类(mysql协议自动生成密码问题)

* [Update] 修改数据库应用/资产等授权序列类

* [Update] 修改命令列表/会话详情命令溢出

* [Update] 修改授权详情中添加系统用户的过滤

* [Update] 修改列表动作的宽度
pull/3553/head
BaiJiangJie 2019-12-19 15:28:17 +08:00 committed by GitHub
parent ac2ba63856
commit 16f727c60d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 2470 additions and 47 deletions

View File

@ -1 +1,2 @@
from .remote_app import *
from .database_app import *

View File

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

View File

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

View File

@ -1 +1,2 @@
from .remote_app import *
from .database_app import *

View File

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

View File

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

View File

@ -1 +1,2 @@
from .remote_app import *
from .database_app import *

View File

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

View File

@ -1 +1,2 @@
from .remote_app import *
from .database_app import *

View File

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

View File

@ -0,0 +1,55 @@
{% extends '_base_create_update.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% block form %}
<form id="DatabaseAppForm" method="post" class="form-horizontal">
{% bootstrap_form form layout="horizontal" %}
<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>
{% endblock %}
{% block custom_foot_js %}
<script type="text/javascript">
var app_type_id = '#' + '{{ form.type.id_for_label }}';
function getFormDataType(){
return $(app_type_id+ " option:selected").val();
}
function getFormData(form){
var data = form.serializeObject();
data['type'] = getFormDataType();
return data
}
$(document).ready(function () {
})
.on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url "api-applications:database-app-list" %}';
var redirect_to = '{% url "applications:database-app-list" %}';
var method = "POST";
{% if api_action == "update" %}
the_url = '{% url "api-applications:database-app-detail" object.id %}';
method = "PUT";
{% endif %}
var form = $("form");
var data = getFormData(form);
var props = {
url: the_url,
data: data,
method: method,
form: form,
redirect_to: redirect_to
};
formSubmit(props);
});
</script>
{% endblock %}

View File

@ -0,0 +1,103 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% 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 class="active">
<a href="{% url 'applications:database-app-detail' pk=database_app.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'applications:database-app-update' pk=database_app.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-application">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-8" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ database_app.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ database_app.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Type' %}:</td>
<td><b>{{ database_app.get_type_display }}</b></td>
</tr>
<tr>
<td>{% trans 'Host' %}:</td>
<td><b>{{ database_app.host }}</b></td>
</tr>
<tr>
<td>{% trans 'Port' %}:</td>
<td><b>{{ database_app.port }}</b></td>
</tr>
<tr>
<td>{% trans 'Database' %}:</td>
<td><b>{{ database_app.database }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ database_app.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ database_app.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ database_app.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
})
.on('click', '.btn-delete-application', function () {
var $this = $(this);
var name = "{{ database_app.name }}";
var rid = "{{ database_app.id }}";
var the_url = '{% url "api-applications:database-app-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', rid);
var redirect_url = "{% url 'applications:database-app-list' %}";
objectDelete($this, name, the_url, redirect_url);
})
</script>
{% endblock %}

View File

@ -0,0 +1,89 @@
{% extends '_base_list.html' %}
{% load i18n static %}
{% block help_message %}
{% endblock %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="btn-group uc pull-left m-r-5">
<button class="btn btn-sm btn-primary">
{% trans "Create DatabaseApp" %}
</button>
<button data-toggle="dropdown" class="btn btn-primary btn-sm dropdown-toggle"><span class="caret"></span></button>
<ul class="dropdown-menu">
{% for key, value in type_choices %}
<li><a class="" href="{% url 'applications:database-app-create' %}?type={{ key }}">{{ value }}</a></li>
{% endfor %}
</ul>
</div>
<table class="table table-striped table-bordered table-hover " id="database_app_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'Host' %}</th>
<th class="text-center">{% trans 'Port' %}</th>
<th class="text-center">{% trans 'Database' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
function initTable() {
var options = {
ele: $('#database_app_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
cellData = htmlEscape(cellData);
{% url 'applications:database-app-detail' pk=DEFAULT_PK as the_url %}
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData, rowData) {
$(td).html(rowData.get_type_display)
}},
{targets: 7, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "applications:database-app-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-rid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}
],
ajax_url: '{% url "api-applications:database-app-list" %}',
columns: [
{data: "id"},
{data: "name" },
{data: "type"},
{data: "host"},
{data: "port"},
{data: "database"},
{data: "comment"},
{data: "id", orderable: false, width: "100px"}
],
op_html: $('#actions').html()
};
jumpserver.initServerSideDataTable(options);
}
$(document).ready(function(){
initTable();
})
.on('click', '.btn-delete', function () {
var $this = $(this);
var $data_table = $('#database_app_list_table').DataTable();
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var rid = $this.data('rid');
var the_url = '{% url "api-applications:database-app-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', rid);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
});
</script>
{% endblock %}

View File

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

View File

@ -0,0 +1,83 @@
{% extends 'base.html' %}
{% load i18n static %}
{% block custom_head_css_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="mail-box-header">
<table class="table table-striped table-bordered table-hover " id="database_app_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'Host' %}</th>
<th class="text-center">{% trans 'Database' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var inited = false;
var database_app_table, url;
function initTable() {
if (inited){
return
} else {
inited = true;
}
url = '{% url "api-perms:my-database-apps" %}';
var options = {
ele: $('#database_app_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(cellData);
$(td).html(name)
}},
{targets: 2, createdCell: function (td, cellData, rowData) {
var type = htmlEscape(rowData.get_type_display);
$(td).html(type);
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var host = htmlEscape(cellData);
$(td).html(host);
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var database = htmlEscape(cellData);
$(td).html(database);
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var conn_btn = '<a href="{% url "luna-view" %}?type=database_app&login_to=' + cellData +'" class="btn btn-xs btn-primary" target="_blank">{% trans "Connect" %}</a>';
$(td).html(conn_btn)
}}
],
ajax_url: url,
columns: [
{data: "id"},
{data: "name"},
{data: "type"},
{data: "host"},
{data: "database"},
{data: "comment", orderable: false},
{data: "id", orderable: false}
]
};
database_app_table = jumpserver.initServerSideDataTable(options);
return database_app_table
}
$(document).ready(function(){
initTable();
})
</script>
{% endblock %}

View File

@ -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/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
]
old_version_urlpatterns = [
re_path('(?P<resource>remote-app)/.*', capi.redirect_plural_name_api)
]

View File

@ -11,6 +11,13 @@ urlpatterns = [
path('remote-app/<uuid:pk>/update/', views.RemoteAppUpdateView.as_view(), name='remote-app-update'),
path('remote-app/<uuid:pk>/', 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/<uuid:pk>/update/', views.DatabaseAppUpdateView.as_view(), name='database-app-update'),
path('database-app/<uuid:pk>/', views.DatabaseAppDetailView.as_view(), name='database-app-detail'),
# User DatabaseApp view
path('user-database-app/', views.UserDatabaseAppListView.as_view(), name='user-database-app-list'),
]

View File

@ -1 +1,2 @@
from .remote_app import *
from .database_app import *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,11 +12,13 @@
<li class="active">
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
{% if system_user.can_perm_to_asset %}
<li>
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets' %}
</a>
</li>
{% endif %}
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
@ -139,6 +141,7 @@
</td>
</tr>
{% endif %}
{% if system_user.is_need_test_asset_connective %}
<tr>
<td width="50%">{% trans 'Test assets connective' %}:</td>
<td>
@ -147,13 +150,14 @@
</span>
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% if system_user.protocol != 'rdp' %}
{% if system_user.is_need_cmd_filter %}
<div class="col-sm-4" style="padding-left: 0; padding-right: 0">
<div class="panel panel-info">
<div class="panel-heading">

View File

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

View File

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

View File

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

View File

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

View File

@ -3,3 +3,4 @@
from .asset_permission import *
from .remote_app_permission import *
from .database_app_permission import *

View File

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

View File

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

View File

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

View File

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

View File

@ -3,3 +3,4 @@
from .asset_permission import *
from .remote_app_permission import *
from .database_app_permission import *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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):
"""
被授权资产的数据结构

View File

@ -0,0 +1,143 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap3 %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
{% endblock %}
{% 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="ibox-title">
<h5>{{ action }}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" >
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
<h3>{% trans 'Basic' %}</h3>
{% bootstrap_field form.name layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'User' %}</h3>
{% bootstrap_field form.users layout="horizontal" %}
{% bootstrap_field form.user_groups layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'DatabaseApp' %}</h3>
{% bootstrap_field form.database_apps layout="horizontal" %}
{% bootstrap_field form.system_users layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3>
<div class="form-group">
<label for="{{ form.is_active.id_for_label }}" class="col-sm-2 control-label">{% trans 'Active' %}</label>
<div class="col-sm-8">
{{ form.is_active }}
</div>
</div>
<div class="form-group {% if form.date_expired.errors or form.date_start.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{% trans 'Validity period' %}</label>
<div class="col-sm-9">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
{% if form.errors %}
<input type="text" class="input-sm form-control" id="date_start" name="date_start" value="{{ form.date_start.value }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" id="date_expired" name="date_expired" value="{{ form.date_expired.value }}">
{% else %}
<input type="text" class="input-sm form-control" id="date_start" name="date_start" value="{{ form.date_start.value|date:'Y-m-d H:i' }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" id="date_expired" name="date_expired" value="{{ form.date_expired.value|date:'Y-m-d H:i' }}">
{% endif %}
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
<span class="help-block ">{{ form.date_start.errors }}</span>
</div>
</div>
{% bootstrap_field form.comment layout="horizontal" %}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" 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>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script type="text/javascript" src='{% static "js/plugins/daterangepicker/moment.min.js" %}'></script>
<script type="text/javascript" src='{% static "js/plugins/daterangepicker/daterangepicker.min.js" %}'></script>
<link rel="stylesheet" type="text/css" href={% static "css/plugins/daterangepicker/daterangepicker.css" %} />
<script>
var dateOptions = {
singleDatePicker: true,
showDropdowns: true,
timePicker: true,
timePicker24Hour: true,
autoApply: true,
locale: {
format: 'YYYY-MM-DD HH:mm'
}
};
$(document).ready(function () {
$('.select2').select2({
closeOnSelect: false
});
usersSelect2Init('.users-select2');
initDateRangePicker('#date_start');
initDateRangePicker('#date_expired');
})
.on("submit", "form", function (evt) {
evt.preventDefault();
var form = $("form");
var data = form.serializeObject();
var method = "POST";
var the_url = '{% url "api-perms:database-app-permission-list" %}';
var redirect_to = '{% url "perms:database-app-permission-list" %}';
{% if api_action == "update" %}
the_url = '{% url "api-perms:database-app-permission-detail" pk=object.id %}';
method = "PUT";
{% endif %}
objectAttrsIsList(data, ['users', 'user_groups', 'database_apps', 'system_users']);
objectAttrsIsDatetime(data, ['date_expired', 'date_start']);
objectAttrsIsBool(data, ['is_active']);
var props = {
url:the_url,
data:data,
method:method,
form:form,
redirect_to:redirect_to
};
formSubmit(props);
})
</script>
{% endblock %}

View File

@ -0,0 +1,237 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% 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 'perms:database-app-permission-detail' pk=database_app_permission.id %}" class="text-center">
<i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li>
<a href="{% url 'perms:database-app-permission-user-list' pk=database_app_permission.id %}" class="text-center">
<i class="fa fa-group"></i> {% trans 'Users and user groups' %}
</a>
</li>
<li class="active">
<a href="{% url 'perms:database-app-permission-database-app-list' pk=database_app_permission.id %}" class="text-center">
<i class="fa fa-inbox"></i> {% trans 'DatabaseApp' %}</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-8" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'DatabaseApp list of ' %} <b>{{ database_app_permission.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-striped table-bordered table-hover" id="database_app_list_table" style="width: 100%">
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'DatabaseApp' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-4" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add DatabaseApp to this permission' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select DatabaseApp' %}" class="select2" id="database_app_select2" style="width: 100%" multiple="" tabindex="4">
{% for database_app in database_apps_remain %}
<option value="{{ database_app.id }}">{{ database_app }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-primary btn-sm btn-add-database-app">{% trans 'Add' %}</button>
</td>
</tr>
</form>
</tbody>
</table>
</div>
</div>
<div class="panel panel-warning">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'System user' %}
</div>
<div class="panel-body">
<table class="table" id="system-user-table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select system users' %}" class="select2 system-users" id="system_users_select2" style="width: 100%" multiple="" tabindex="4">
{% for system_user in system_users_remain %}
<option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-warning btn-sm" id="btn-add-system-user">{% trans 'Add' %}</button>
</td>
</tr>
</form>
{% for system_user in object.system_users.all %}
<tr {% if forloop.counter == 1 %} class="no-borders-tr" {% endif %} >
<td ><b class="bdg-system-user" data-uid={{ system_user.id }}>{{ system_user|truncatechars:21}}</b></td>
<td>
<button class="btn btn-danger btn-xs btn-remove-system-user" data-uid="{{ system_user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var databaseAppsRelationUrl = "{% url 'api-perms:database-app-permissions-database-apps-relation-list' %}";
var systemUsersRelationUrl = "{% url 'api-perms:database-app-permissions-system-users-relation-list' %}";
function getRelationUrl(type) {
var theUrl = "";
switch (type) {
case "databaseapp":
theUrl = databaseAppsRelationUrl;
break;
case "systemuser":
theUrl = systemUsersRelationUrl;
break;
}
return theUrl;
}
function addObjects(objectsId, type) {
if (!objectsId || objectsId.length === 0) {
return
}
var theUrl = getRelationUrl(type);
var body = [];
objectsId.forEach(function (v) {
var data = {databaseapppermission: "{{ object.id }}"};
data[type] = v;
body.push(data)
});
requestApi({
url: theUrl,
body: JSON.stringify(body),
method: "POST",
success: reloadPage
});
}
function removeObject(objectId, type) {
if (!objectId) {
return
}
var theUrl = getRelationUrl(type);
theUrl = setUrlParam(theUrl, 'databaseapppermission', "{{ object.id }}");
theUrl = setUrlParam(theUrl, type, objectId);
requestApi({
url: theUrl,
method: "DELETE",
success: reloadPage
});
}
var databaseAppsDirectRelated = {{ database_apps | safe }};
var table;
function initDatabaseAppTable() {
var options = {
ele: $('#database_app_list_table'),
toggle: true,
columnDefs: [
{targets: 2, createdCell: function (td, cellData, rowData) {
var removeBtn = '<a class="btn btn-xs btn-danger m-l-xs btn-remove-database-app" disabled data-uid="{{ DEFAULT_PK }}"><i class="fa fa-minus"></i></a>'
.replace('{{ DEFAULT_PK }}', cellData);
if (databaseAppsDirectRelated.indexOf(cellData) !== -1) {
removeBtn = removeBtn.replace('disabled', '')
}
$(td).html(removeBtn);
}}
],
ajax_url: "{% url 'api-perms:database-app-permission-all-database-apps' pk=object.id %}",
columns: [
{data: "databaseapp"}, {data: "databaseapp_display"}, {data: "databaseapp", width: "30px"}
],
op_html: $('#actions').html()
};
table = jumpserver.initServerSideDataTable(options);
return table
}
$(document).ready(function () {
$('.select2').select2();
initDatabaseAppTable();
})
.on('click', '.btn-add-database-app', function () {
var databaseAppsId = $("#database_app_select2").val();
addObjects(databaseAppsId, "databaseapp");
})
.on('click', '.btn-remove-database-app', function () {
var databaseAppsId = $(this).data("uid");
removeObject(databaseAppsId, "databaseapp")
})
.on('click', '#btn-add-system-user', function () {
var systemUsersId = $("#system_users_select2").val();
addObjects(systemUsersId, "systemuser");
})
.on('click', '.btn-remove-system-user', function () {
var systemUserId = $(this).data("uid");
removeObject(systemUserId, "systemuser")
})
</script>
{% endblock %}

View File

@ -0,0 +1,157 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% 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 class="active">
<a href="{% url 'perms:database-app-permission-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li>
<a href="{% url 'perms:database-app-permission-user-list' pk=object.id %}" class="text-center">
<i class="fa fa-group"></i> {% trans 'Users and user groups' %}
</a>
</li>
<li>
<a href="{% url 'perms:database-app-permission-database-app-list' pk=object.id %}" class="text-center">
<i class="fa fa-inbox"></i> {% trans 'DatabaseApp' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'perms:database-app-permission-update' pk=object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-8" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ object.name }}</b></td>
</tr>
<tr>
<td>{% trans 'User count' %}:</td>
<td><b>{{ object.users.count }}</b></td>
</tr>
<tr>
<td>{% trans 'User group count' %}:</td>
<td><b>{{ object.user_groups.count }}</b></td>
</tr>
<tr>
<td>{% trans 'DatabaseApp count' %}:</td>
<td><b>{{ object.database_apps.count }}</b></td>
</tr>
<tr>
<td>{% trans 'System user count' %}:</td>
<td><b>{{ object.system_users.count }}</b></td>
</tr>
<tr>
<td>{% trans 'Date start' %}:</td>
<td><b>{{ object.date_start }}</b></td>
</tr>
<tr>
<td>{% trans 'Date expired' %}:</td>
<td><b>{{ object.date_expired }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ object.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ object.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ object.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-4" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick update' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'Active' %} :</td>
<td><span style="float: right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" {% if object.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
}).on('click', '.btn-delete', function () {
var $this = $(this);
var name = "{{ object.name }}";
var did = "{{ object.id }}";
var the_url = '{% url "api-perms:database-app-permission-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', did);
var redirect_url = "{% url 'perms:database-app-permission-list' %}";
objectDelete($this, name, the_url, redirect_url);
}).on('click', '#is_active', function () {
var the_url = '{% url "api-perms:database-app-permission-detail" pk=object.id %}';
var checked = $(this).prop('checked');
var body = {
'is_active': checked
};
requestApi({
url: the_url,
body: JSON.stringify(body)
});
})
</script>
{% endblock %}

View File

@ -0,0 +1,99 @@
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
<a href="{% url 'perms:database-app-permission-create' %}" class="btn btn-sm btn-primary"> {% trans "Create permission" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="database_app_permission_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'DatabaseApp' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Validity' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
function initTable() {
var options = {
ele: $('#database_app_permission_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
cellData = htmlEscape(cellData);
{% url 'perms:database-app-permission-detail' pk=DEFAULT_PK as the_url %}
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData, rowData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 5, createdCell: function (td, cellData, rowData) {
var num = cellData.length;
$(td).html(num);
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 7, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "perms:database-app-permission-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-did="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}
],
ajax_url: '{% url "api-perms:database-app-permission-list" %}',
columns: [
{data: "id"},
{data: "name" },
{data: "users", orderable: false},
{data: "user_groups", orderable: false},
{data: "database_apps", orderable: false},
{data: "system_users", orderable: false},
{data: "is_valid", orderable: false},
{data: "id", orderable: false, width: "100px"}
],
op_html: $('#actions').html()
};
jumpserver.initServerSideDataTable(options);
}
$(document).ready(function(){
initTable();
})
.on('click', '.btn-delete', function () {
var $this = $(this);
var $data_table = $('#database_app_list_table').DataTable();
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var did = $this.data('did');
var the_url = '{% url "api-perms:database-app-permission-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', did);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
});
</script>
{% endblock %}

View File

@ -0,0 +1,251 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% 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 'perms:database-app-permission-detail' pk=database_app_permission.id %}" class="text-center">
<i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="active">
<a href="{% url 'perms:database-app-permission-user-list' pk=database_app_permission.id %}" class="text-center">
<i class="fa fa-group"></i> {% trans 'Users and user groups' %}
</a>
</li>
<li>
<a href="{% url 'perms:database-app-permission-database-app-list' pk=database_app_permission.id %}" class="text-center">
<i class="fa fa-inbox"></i> {% trans 'DatabaseApp' %}</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-8" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'User list of ' %} <b>{{ database_app_permission.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-hover" id="user_list_table">
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th>{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-4" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add user to permission' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select user' %}" class="select2 users-select2" style="width: 100%" multiple="" tabindex="4">
</select>
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-primary btn-sm btn-add-user">{% trans 'Add' %}</button>
</td>
</tr>
</form>
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Add user group to permission' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Select user groups' %}" class="select2 user-groups-select2" style="width: 100%" multiple="" tabindex="4">
{% for user_group in user_groups_remain %}
<option value="{{ user_group.id }}" id="opt_{{ user_group.id }}">{{ user_group }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-sm" id="btn-add-group">{% trans 'Add' %}</button>
</td>
</tr>
</form>
{% for user_group in database_app_permission.user_groups.all %}
<tr {% if forloop.counter == 1 %} class="no-borders-tr" {% endif %}>
<td ><b class="bdg_group" data-gid={{ user_group.id }}>{{ user_group }}</b></td>
<td>
<button class="btn btn-danger btn-xs btn-remove-group" type="button" data-gid="{{ user_group.id }}" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var usersDirectRelated = {{ users | safe }};
var table;
function initUsersTable() {
var options = {
ele: $('#user_list_table'),
toggle: true,
columnDefs: [
{targets: 2, createdCell: function (td, cellData, rowData) {
var removeBtn = '<a class="btn btn-xs btn-danger m-l-xs btn-remove-user" disabled data-uid="{{ DEFAULT_PK }}"><i class="fa fa-minus"></i></a>'
.replace('{{ DEFAULT_PK }}', cellData);
if (usersDirectRelated.indexOf(cellData) !== -1) {
removeBtn = removeBtn.replace('disabled', '')
}
$(td).html(removeBtn);
}}
],
ajax_url: "{% url 'api-perms:database-app-permission-all-users' pk=object.id %}",
columns: [
{data: "user"}, {data: "user_display"}, {data: "user", width: "30px"}
],
op_html: $('#actions').html()
};
table = jumpserver.initServerSideDataTable(options);
return table
}
function addUsers(usersId) {
if (!usersId || usersId.length === 0) {
return
}
var theUrl = "{% url 'api-perms:database-app-permissions-users-relation-list' %}";
var body = [];
usersId.forEach(function (v, i) {
body.push({
"user": v,
"databaseapppermission": "{{ object.id }}"
})
});
var success = function(data) {
location.reload();
};
requestApi({
url: theUrl,
body: JSON.stringify(body),
method: "POST",
success: success
});
}
function removeUser(userId) {
var theUrl = "{% url 'api-perms:database-app-permissions-users-relation-list' %}?user=userId&databaseapppermission={{ object.id }}";
theUrl = theUrl.replace("userId", userId);
var success = function(data) {
location.reload();
};
requestApi({
url: theUrl,
method: "DELETE",
success: success
});
}
function addGroups(groupsId) {
if (!groupsId || groupsId.length === 0) {
return
}
var theUrl = "{% url 'api-perms:database-app-permissions-user-groups-relation-list' %}";
var body = [];
groupsId.forEach(function (v, i) {
body.push({
"usergroup": v,
"databaseapppermission": "{{ object.id }}"
})
});
var success = function(data) {
location.reload();
};
requestApi({
url: theUrl,
body: JSON.stringify(body),
method: "POST",
success: success
});
}
function removeGroup(groupId) {
var theUrl = "{% url 'api-perms:database-app-permissions-user-groups-relation-list' %}?databaseapppermission={{ object.id }}";
theUrl = theUrl.replace("groupId", groupId);
var success = function(data) {
location.reload();
};
requestApi({
url: theUrl,
method: "DELETE",
success: success
});
}
$(document).ready(function () {
$(".select2").select2();
initUsersTable();
usersSelect2Init(".users-select2", null, usersDirectRelated);
}).on('click', '.btn-add-user', function () {
var usersSelected = $(".users-select2").val();
addUsers(usersSelected);
}).on('click', '.btn-remove-user', function () {
var userId = $(this).data("uid");
removeUser(userId)
}).on('click', '#btn-add-group', function () {
var groupsSelected = $(".user-groups-select2").val();
addGroups(groupsSelected);
}).on('click', '.btn-remove-group', function () {
var groupId = $(this).data("gid");
removeGroup(groupId);
})
</script>
{% endblock %}

View File

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

View File

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

View File

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

View File

@ -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('<uuid:pk>/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('<uuid:pk>/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('<uuid:pk>/database-apps/<uuid:database_app_id>/system-users/', api.UserGrantedDatabaseAppSystemUsersApi.as_view(), name='user-database-app-system-users'),
path('database-apps/<uuid:database_app_id>/system-users/', api.UserGrantedDatabaseAppSystemUsersApi.as_view(), name='user-database-app-system-users'),
]
user_group_permission_urlpatterns = [
path('<uuid:pk>/database-apps/', api.UserGroupGrantedDatabaseAppsApi.as_view(), name='user-group-database-apps'),
]
permission_urlpatterns = [
# 授权规则中授权的用户和数据库应用
path('<uuid:pk>/users/all/', api.DatabaseAppPermissionAllUserListApi.as_view(), name='database-app-permission-all-users'),
path('<uuid:pk>/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

View File

@ -23,4 +23,12 @@ urlpatterns = [
path('remote-app-permission/<uuid:pk>/', views.RemoteAppPermissionDetailView.as_view(), name='remote-app-permission-detail'),
path('remote-app-permission/<uuid:pk>/user/', views.RemoteAppPermissionUserView.as_view(), name='remote-app-permission-user-list'),
path('remote-app-permission/<uuid:pk>/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/<uuid:pk>/update/', views.DatabaseAppPermissionUpdateView.as_view(), name='database-app-permission-update'),
path('database-app-permission/<uuid:pk>/', views.DatabaseAppPermissionDetailView.as_view(), name='database-app-permission-detail'),
path('database-app-permission/<uuid:pk>/user/', views.DatabaseAppPermissionUserView.as_view(), name='database-app-permission-user-list'),
path('database-app-permission/<uuid:pk>/database-app/', views.DatabaseAppPermissionDatabaseAppView.as_view(), name='database-app-permission-database-app-list'),
]

View File

@ -3,3 +3,4 @@
from .asset_permission import *
from .remote_app_permission import *
from .database_app_permission import *

View File

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

View File

@ -3,3 +3,4 @@
from .asset_permission import *
from .remote_app_permission import *
from .database_app_permission import *

View File

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

View File

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

View File

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

View File

@ -61,6 +61,7 @@
</a>
<ul class="nav nav-second-level">
<li id="remote-app"><a href="{% url 'applications:remote-app-list' %}">{% trans 'RemoteApp' %}</a></li>
<li id="database-app"><a href="{% url 'applications:database-app-list' %}">{% trans 'DatabaseApp' %}</a></li>
</ul>
</li>
{% endif %}
@ -78,6 +79,9 @@
<li id="remote-app-permission">
<a href="{% url 'perms:remote-app-permission-list' %}">{% trans 'RemoteApp' %}</a>
</li>
<li id="database-app-permission">
<a href="{% url 'perms:database-app-permission-list' %}">{% trans 'DatabaseApp' %}</a>
</li>
{% endif %}
</ul>
</li>

View File

@ -16,6 +16,11 @@
<i class="" style="width: 14px"></i><span class="nav-label">{% trans 'RemoteApp' %}</span><span class="label label-info pull-right"></span>
</a>
</li>
<li id="user-database-app">
<a href="{% url 'applications:user-database-app-list' %}">
<i class="" style="width: 14px"></i><span class="nav-label">{% trans 'DatabaseApp' %}</span><span class="label label-info pull-right"></span>
</a>
</li>
</ul>
</li>
{% endif %}

View File

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

View File

@ -169,6 +169,7 @@ class Session(OrgModelMixin):
('rdp', 'rdp'),
('vnc', 'vnc'),
('telnet', 'telnet'),
('mysql', 'mysql'),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)

View File

@ -168,7 +168,7 @@ $(document).ready(function () {
function format(d) {
var output = $("<pre style='border: none; background: none'></pre>");
var output = $("<pre style='border: none; background: none; white-space: pre-wrap'></pre>");
output.append('$ ', d.input);
output.append('\r\n\r\n');

View File

@ -55,7 +55,7 @@
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ command.input | truncatechars:40 }}</td>
<td><pre style="border: none;background: none">
<td><pre style="border: none;background: none; white-space: pre-wrap;">
$ {{ command.input }}
{{ command.output }}